@lvy-jsd/dsh-tool-manager 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -2
  3. package/cordis.patch.yml +29 -0
  4. package/lib/catalog.d.ts +35 -0
  5. package/lib/catalog.d.ts.map +1 -0
  6. package/lib/catalog.js +311 -0
  7. package/lib/catalog.js.map +1 -0
  8. package/lib/exempt.d.ts +45 -0
  9. package/lib/exempt.d.ts.map +1 -0
  10. package/lib/exempt.js +122 -0
  11. package/lib/exempt.js.map +1 -0
  12. package/lib/index.d.ts +24 -0
  13. package/lib/index.d.ts.map +1 -0
  14. package/lib/index.js +797 -0
  15. package/lib/index.js.map +1 -0
  16. package/lib/manifest.d.ts +21 -0
  17. package/lib/manifest.d.ts.map +1 -0
  18. package/lib/manifest.js +112 -0
  19. package/lib/manifest.js.map +1 -0
  20. package/lib/metadata.d.ts +38 -0
  21. package/lib/metadata.d.ts.map +1 -0
  22. package/lib/metadata.js +102 -0
  23. package/lib/metadata.js.map +1 -0
  24. package/lib/router/bm25.d.ts +21 -0
  25. package/lib/router/bm25.d.ts.map +1 -0
  26. package/lib/router/bm25.js +63 -0
  27. package/lib/router/bm25.js.map +1 -0
  28. package/lib/router/index.d.ts +9 -0
  29. package/lib/router/index.d.ts.map +1 -0
  30. package/lib/router/index.js +422 -0
  31. package/lib/router/index.js.map +1 -0
  32. package/lib/router/sidecar.d.ts +34 -0
  33. package/lib/router/sidecar.d.ts.map +1 -0
  34. package/lib/router/sidecar.js +156 -0
  35. package/lib/router/sidecar.js.map +1 -0
  36. package/lib/router/types.d.ts +164 -0
  37. package/lib/router/types.d.ts.map +1 -0
  38. package/lib/router/types.js +7 -0
  39. package/lib/router/types.js.map +1 -0
  40. package/lib/service.d.ts +51 -0
  41. package/lib/service.d.ts.map +1 -0
  42. package/lib/service.js +199 -0
  43. package/lib/service.js.map +1 -0
  44. package/lib/settings.d.ts +32 -0
  45. package/lib/settings.d.ts.map +1 -0
  46. package/lib/settings.js +93 -0
  47. package/lib/settings.js.map +1 -0
  48. package/lib/types.d.ts +453 -0
  49. package/lib/types.d.ts.map +1 -0
  50. package/lib/types.js +7 -0
  51. package/lib/types.js.map +1 -0
  52. package/lib/visibility.d.ts +33 -0
  53. package/lib/visibility.d.ts.map +1 -0
  54. package/lib/visibility.js +305 -0
  55. package/lib/visibility.js.map +1 -0
  56. package/package.json +54 -4
  57. package/sidecar/bin/tool-router-sidecar +0 -0
  58. package/src/catalog.ts +305 -0
  59. package/src/exempt.ts +148 -0
  60. package/src/index.ts +805 -0
  61. package/src/manifest.ts +116 -0
  62. package/src/metadata.ts +123 -0
  63. package/src/router/bm25.ts +68 -0
  64. package/src/router/index.ts +440 -0
  65. package/src/router/sidecar.ts +159 -0
  66. package/src/router/types.ts +149 -0
  67. package/src/service.ts +207 -0
  68. package/src/settings.ts +108 -0
  69. package/src/types.ts +450 -0
  70. package/src/visibility.ts +317 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lvy-jsd and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,303 @@
1
- # Temporary Holding Version
1
+ # @lvy-jsd/dsh-tool-manager
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Browse every DeepSeek Harness tool as a directory and load tools on demand.
4
+
5
+ A DSH host with many plugins and MCP connectors sends every tool schema to the
6
+ model on every request. This plugin hides everything except a small always-on
7
+ set and gives the model three entry tools to find and load the rest:
8
+
9
+ ```
10
+ tools_ls("/") # categories
11
+ tools_ls("/code") # groups (a connector or plugin)
12
+ tools_ls("/code/github") # tools, with a one-line summary and schema size
13
+ tools_ls({ query: "pull request" }) # search the whole tree in one step
14
+ tools_load(["/code/github"]) # callable from the next step, for the rest of the session
15
+ tools_load(["mcp__github__get_me"]) # or one tool by exact name
16
+ tools_unload(["/code/github"])
17
+ tools_find("send an email") # ranked matches by intent (BM25), no loading
18
+ tools_route("send an email") # retrieve, re-rank with the model, then load
19
+ ```
20
+
21
+ Loaded tools are **native**: the model receives their full parameter schema
22
+ and calls them directly. Nothing is proxied.
23
+
24
+ The core ships **no placement rules**. Without an extension every group is
25
+ listed under `uncategorized/` — still fully browsable and loadable. Install
26
+ [`dsh-tool-manager-classifier-rules`](../dsh-tool-manager-classifier-rules) for
27
+ built-in rules and config overrides, or write your own classifier.
28
+
29
+ ## How it works
30
+
31
+ - Visibility is a per-agent allowlist installed with the host's
32
+ `agent.ctx.tools.restrict({ allow })`:
33
+ `allow = (pinned ∪ loaded tools ∪ tools of loaded groups) ∩ registered tools`,
34
+ then narrowed by any registered visibility filters.
35
+ Tools that appear later (an MCP server reconnecting, a new plugin, a renamed
36
+ tool) stay hidden until loaded.
37
+ - Loading a group subscribes to it: tools the group gains later become visible
38
+ too.
39
+ - Restrictions from other plugins intersect with this one, so a hard deny (for
40
+ example the MCP connector's governance policy) always wins. A pinned or
41
+ loaded tool that another layer still hides is marked `[denied]` in
42
+ `tools_ls`, and `tools_load` reports it as not callable instead of listing it
43
+ (with one log warning per tool, pointing at connector governance rules and
44
+ per-session injection).
45
+ - DSH's PTC mode (`DSH_TOOLS_MODE=ptc`) works unchanged: the model gets only
46
+ `run_code`, and the SDK the host declares for it is built from the same
47
+ narrowed view, so loads show up there from the next step. `run_code` itself
48
+ is a host transport and never enters the directory.
49
+ - Every agent is restricted separately. A subagent starts from the pinned set
50
+ plus whatever the parent loaded with `forSubagents: true`.
51
+ - Loaded state is in memory. Install
52
+ [`dsh-tool-manager-persist`](../dsh-tool-manager-persist) to save it per
53
+ session and restore it when the session resumes.
54
+ - If the plugin is unloaded, all its restrictions are lifted (fail open to
55
+ "every tool visible", never to "no tools").
56
+
57
+ ## Finding and routing by intent
58
+
59
+ The directory above is browsable, but the model still has to guess the right
60
+ keywords and spend turns searching. The core now ships a retrieval + routing
61
+ layer that makes an unbounded catalog usable without that guesswork:
62
+
63
+ ```
64
+ tools_find("send an email") # ranked matches (BM25), no loading
65
+ tools_route("send an email") # retrieve, re-rank with the model, then load
66
+ skills_find("write a PR") # ranked skills, no loading
67
+ skills_route("write a PR") # retrieve, re-rank, then load the top skill's body
68
+ ```
69
+
70
+ Retrieval is BM25 by default: no model, no network, works offline, and scales
71
+ to a large catalog. The BM25 index lives in a Rust sidecar process
72
+ (`sidecar/`, tantivy over a Unix socket) for headroom — the hot path is native
73
+ and the TypeScript here is a thin client. A dependency-free JS BM25 index is
74
+ kept as the always-available fallback, so retrieval never breaks if the sidecar
75
+ binary is missing or the process dies.
76
+
77
+ When a model is configured (`config.model { provider, model }`), `tools_route`
78
+ and `skills_route` re-rank the BM25 candidates with the model, so a
79
+ natural-language intent that shares no words with a tool name still finds it —
80
+ the RAG-MCP pattern: retrieve a small candidate set, then rank it. Any model
81
+ failure falls back to the BM25 order, so the router never breaks on a model
82
+ hiccup.
83
+
84
+ The router reuses the core's `toolCatalog` service: it indexes the same
85
+ catalog, loads through the same path, and reports the same token budget. The
86
+ sidecar is spawned by the plugin and exits with it (a parent-process monitor
87
+ kills it on a DSH restart).
88
+
89
+ ## Configuration
90
+
91
+ ```yaml
92
+ - id: tool-manager
93
+ name: '@lvy-jsd/dsh-tool-manager'
94
+ config:
95
+ pinned: [] # extra always-on tools (core DSH tools are built in)
96
+ unpin: [] # built-in always-on tools to move into the directory
97
+ exemptProviders: [claude-code] # routes never narrowed (tool list fixed at session start)
98
+ highRisk: [] # never included in group loads; load individually
99
+ ```
100
+
101
+ ### Override files
102
+
103
+ Three files under `$DSH_HOME/tool-manager/` override the config above. Each is
104
+ read at startup and re-read when the settings panel saves through the API (or
105
+ `reload*` is called); write them from the panel, or by hand and then reload.
106
+
107
+ | File | Config key it replaces | What it holds |
108
+ |---|---|---|
109
+ | `settings.json` | `pinned`, `unpin`, `highRisk` | the lists an operator edits most often |
110
+ | `metadata.json` | `annotate()` for the same tool | `{ "<tool>": { "group", "category", "risk", "summary" } }` |
111
+ | `exempt.json` | `exemptProviders` | `{ "providers": [...], "sessions": [...] }` |
112
+
113
+ ### The tool manifest
114
+
115
+ A client that receives its tool list once per session — Claude Code, driven by
116
+ `dsh-oh-my-claude` — cannot discover the directory by asking, because nothing
117
+ tells it the directory exists. So the catalog is also written out as text, to
118
+ `~/.claude/dsh-tool-catalog.md`, after every rebuild:
119
+
120
+ ```
121
+ ## device (14 tools, Devices)
122
+
123
+ ### adb — load with `tools_load({ targets: ["/device/adb"] })`
124
+ - adb_install — Install an APK on a device.
125
+ - adb_shell ! — Run a shell command on a device.
126
+ ```
127
+
128
+ The file opens by saying the listed tools are **not callable yet**, then gives
129
+ the two calls that change that (`tools_ls`, `tools_load`) and the rule to load
130
+ the smallest thing that covers the task. A `!` marks a high-risk tool. Reading
131
+ it runs nothing.
132
+
133
+ Add one line to `~/.claude/CLAUDE.md` so every Claude session reads it:
134
+
135
+ ```
136
+ @~/.claude/dsh-tool-catalog.md
137
+ ```
138
+
139
+ The write is skipped when the text did not change, so a rebuild that moves
140
+ nothing does not cost a prompt re-read. `manifestFile` points it elsewhere, and
141
+ `''` turns it off.
142
+
143
+ The files win as a whole, not key by key: once `exempt.json` exists, the config
144
+ list is ignored rather than merged, so what the file shows is what is in force.
145
+ A missing file means "no opinion, use the config"; a corrupt file is reported as
146
+ a warning and treated as empty rather than silently restoring the config.
147
+ `sessions` exempts one session by id for its remaining lifetime, for checking
148
+ whether a client handles late loading without de-exempting the whole route.
149
+
150
+ A tool's `metadata.json` entry replaces the previous entry as a whole:
151
+ submitting the form with a field blank drops that override, and the package
152
+ declaration (or a classifier) applies again. `parameters` are deliberately not
153
+ part of it — the host owns the schema sent to the model.
154
+
155
+ ## Extending: the `toolCatalog` service
156
+
157
+ Declare `inject: ['toolCatalog']` (or use `ctx.inject(['toolCatalog'], …)` for
158
+ an optional dependency) and register inside an effect so that unloading your
159
+ plugin undoes the registration.
160
+
161
+ ### Classifiers
162
+
163
+ A classifier may place tools into groups and/or groups into categories.
164
+ Return `undefined` to pass.
165
+
166
+ ```js
167
+ export const name = 'my-classifier'
168
+ export const inject = ['toolCatalog']
169
+ export function apply(ctx) {
170
+ ctx.effect(() => ctx.toolCatalog.registerClassifier({
171
+ name: 'my-classifier',
172
+ priority: 200,
173
+ group: (tool) => (tool.name.startsWith('lingshu_') ? 'lingshu' : undefined),
174
+ categorize: (group) => (group.name === 'lingshu' ? 'system' : undefined),
175
+ }))
176
+ }
177
+ ```
178
+
179
+ Resolution order: classifiers with `priority >= 1000` (user overrides) →
180
+ plugin declarations → other classifiers, highest priority first → structural
181
+ fallback (`mcp__server__…` → `server`, shared `prefix_` → `prefix`, else
182
+ `misc`; category `uncategorized`). A classifier that throws is skipped.
183
+
184
+ ### Declaring your own tools
185
+
186
+ Plugin authors know their tools best. Declare them in your `package.json`:
187
+
188
+ ```json
189
+ {
190
+ "dsh": {
191
+ "toolCatalog": {
192
+ "match": "^adb_",
193
+ "group": "adb",
194
+ "category": "device",
195
+ "tools": { "adb_install": { "risk": "high", "summary": "Install an APK." } }
196
+ }
197
+ }
198
+ }
199
+ ```
200
+
201
+ or at runtime:
202
+
203
+ ```js
204
+ ctx.effect(() => ctx.toolCatalog.annotate('^adb_', { category: 'device' }))
205
+ ctx.effect(() => ctx.toolCatalog.annotate('adb_install', { risk: 'high' }))
206
+ ```
207
+
208
+ Fields: `group`, `category`, `risk` (`"high"`), `summary`. Exact-name entries
209
+ win over `match`; runtime annotations win over `package.json`.
210
+
211
+ ### New categories
212
+
213
+ ```js
214
+ ctx.effect(() => ctx.toolCatalog.defineCategory('games', 'Game servers and bots'))
215
+ ```
216
+
217
+ ### High-risk gates
218
+
219
+ A gate declares that a plugin confirms high-risk loads (normally by returning
220
+ `{ kind: 'ask' }` from `tools/pre-execute` for `tools_load`). **With no gate
221
+ registered, high-risk tools cannot be loaded at all** — they are skipped in
222
+ group loads and refused individually. That is deliberate: a deployment without
223
+ an approval path should not be able to arm them.
224
+
225
+ ```js
226
+ ctx.effect(() => ctx.toolCatalog.registerRiskGate({ name: 'my-policy' }))
227
+ ctx.effect(() => ctx.on('tools/pre-execute', async (exec, next) => {
228
+ if (exec.name !== 'tools_load' || !needsConfirmation(exec.arguments)) return next()
229
+ return { kind: 'ask', reason: 'Loading a high-risk tool' }
230
+ }))
231
+ ```
232
+
233
+ See [`dsh-tool-manager-risk-gate`](../dsh-tool-manager-risk-gate).
234
+
235
+ ### Persistence
236
+
237
+ Loaded state lives in memory; a state provider makes it outlive the process.
238
+
239
+ ```js
240
+ ctx.effect(() => ctx.toolCatalog.registerStateProvider({
241
+ name: 'my-store',
242
+ load: (agent) => readFromMyStore(agent.id), // serialized state or null
243
+ save: (agent, state) => writeToMyStore(agent.id, state),
244
+ }))
245
+ ```
246
+
247
+ Providers are consulted in registration order (first non-null wins) and all
248
+ receive every save. `exportState(agent)` / `importState(agent, state)` are the
249
+ plain-JSON equivalents. See
250
+ [`dsh-tool-manager-persist`](../dsh-tool-manager-persist).
251
+
252
+ ### Hooks
253
+
254
+ Plain Cordis events on the host context:
255
+
256
+ | Event | Kind | Payload |
257
+ |---|---|---|
258
+ | `tool-catalog/before-load` | serial; throw to veto | `{ agent, targets, names }` |
259
+ | `tool-catalog/after-load` | emit | `{ agent, targets, added }` |
260
+ | `tool-catalog/before-unload` | serial; throw to veto | `{ agent, targets }` |
261
+ | `tool-catalog/after-unload` | emit | `{ agent, targets, removed }` |
262
+ | `tool-catalog/before-rebuild` | emit | `{}` |
263
+ | `tool-catalog/after-rebuild` | emit | `{ catalog }` |
264
+ | `tool-catalog/state-changed` | emit | `{ agent, state }` (plain JSON, after every load/unload) |
265
+
266
+ ```js
267
+ // A per-session quota: at most 60 visible tools.
268
+ ctx.on('tool-catalog/before-load', ({ agent, names }) => {
269
+ const now = ctx.toolCatalog.allowFor(agent)?.length ?? 0
270
+ if (now + names.length > 60) throw new Error('tool quota reached; unload something first')
271
+ })
272
+ ```
273
+
274
+ ### Visibility filters
275
+
276
+ A filter receives the agent and the computed allowlist and returns the names
277
+ to keep. It can only narrow; it can never add tools or hide the entry tools.
278
+
279
+ ```js
280
+ // Keep GitHub write tools away from bot sessions.
281
+ ctx.effect(() => ctx.toolCatalog.registerVisibilityFilter({
282
+ name: 'no-github-writes-for-bots',
283
+ filter: (agent, names) => (agent.id.startsWith('bot-')
284
+ ? names.filter((n) => !/^mcp__github__(create|delete|merge|push|update)/.test(n))
285
+ : names),
286
+ }))
287
+ ```
288
+
289
+ ### Reading the catalog
290
+
291
+ `ctx.toolCatalog.get()` returns `{ tools, groups, categories }`;
292
+ `resolve(target)`, `stateOf(agent)`, `allowFor(agent)`, `deniedFor(agent)`
293
+ (allowed here but hidden by another layer), `isExempt(agent)`,
294
+ `packages()` (enabled plugin packages: `{ name, dir, manifest }`) and
295
+ `refresh()` are also available.
296
+
297
+ ## Tests
298
+
299
+ See the repository root.
300
+
301
+ ## License
302
+
303
+ MIT
@@ -0,0 +1,29 @@
1
+ # @lvy-jsd/dsh-tool-manager bundle patch: one loader entry.
2
+ - insert:
3
+ - id: tool-manager
4
+ name: '@lvy-jsd/dsh-tool-manager'
5
+ config:
6
+ # Extra always-visible tools on top of the built-in core list.
7
+ pinned: []
8
+ # Built-in always-visible tools to move into the directory instead.
9
+ unpin: []
10
+ # Model routes never narrowed: their tool list is handed to an external
11
+ # client once per session and not refreshed (Claude Code's MCP bridge).
12
+ exemptProviders: [claude-code]
13
+ # Tools excluded from group loads; must be loaded on their own.
14
+ # Loading high-risk tools also needs a risk gate plugin
15
+ # (@lvy-jsd/dsh-tool-manager-risk-gate) to confirm them.
16
+ highRisk: []
17
+ # The catalog as text, for a client that cannot lazy-load (Claude Code
18
+ # reads it through a ~/.claude/CLAUDE.md `@` line). '' turns it off.
19
+ # manifestFile: ~/.claude/dsh-tool-catalog.md
20
+ # --- retrieval + routing (tools_find / tools_route / skills_find / skills_route) ---
21
+ # Model used to re-rank tools_route / skills_route candidates. Leave
22
+ # unset for pure BM25 (no model, works offline).
23
+ # model: { provider: deepseek, model: deepseek-chat }
24
+ # maxTokens: 24
25
+ # topK: 8
26
+ # maxBudget: 20000
27
+ # k1: 1.5
28
+ # b: 0.75
29
+ # sidecar: { bin: '', socket: '', timeoutMs: 2000 }
@@ -0,0 +1,35 @@
1
+ import type { Annotation, Catalog, CatalogNode, Classifier, RegisteredResolver, ToolEntry, ToolSchema } from './types.js';
2
+ export declare const UNCATEGORIZED = "uncategorized";
3
+ export declare const MISC_GROUP = "misc";
4
+ export declare const OVERRIDE_PRIORITY = 1000;
5
+ export declare const DEFAULT_CATEGORIES: Record<string, string>;
6
+ /**
7
+ * Structural group for a tool name, used when nothing else places it:
8
+ * `mcp__server__tool` -> server, `mcp__server__sub__tool` -> server-sub,
9
+ * `prefix_rest` -> prefix when at least two tools share a non-verb prefix, else misc.
10
+ */
11
+ export declare function structuralGroup(name: string, prefixCounts: Map<string, number>): string;
12
+ /** Short form of a tool name inside its group (drops the MCP routing prefix). */
13
+ export declare function shortName(name: string): string;
14
+ /** First sentence of a description, capped for one-line listings. */
15
+ export declare function summary(text: string | undefined, max?: number): string;
16
+ /** Rough token cost of one schema as the model receives it. */
17
+ export declare function estimateTokens(schema: unknown): number;
18
+ export interface BuildOptions {
19
+ /** Placement rules; priority >= OVERRIDE_PRIORITY beats plugin declarations. */
20
+ classifiers?: readonly Classifier[];
21
+ annotations?: readonly Annotation[];
22
+ /** Category vocabulary (name -> description). */
23
+ categories?: Record<string, string>;
24
+ /** Tool names configured as high risk. */
25
+ highRisk?: readonly string[];
26
+ }
27
+ /**
28
+ * Build the directory index.
29
+ * @param schemas - catalog tools (pinned already removed)
30
+ */
31
+ export declare function buildCatalog(schemas: readonly ToolSchema[], { classifiers, annotations, categories, highRisk }?: BuildOptions): Catalog;
32
+ export declare function resolveTarget(catalog: Catalog, raw: unknown, resolvers?: readonly Pick<RegisteredResolver, 'prefix' | 'resolve'>[]): CatalogNode;
33
+ /** Tools under a node (whole tree for root). */
34
+ export declare function toolsUnder(catalog: Catalog, node: CatalogNode): ToolEntry[];
35
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAY,UAAU,EAAqB,kBAAkB,EAAE,SAAS,EAAuB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE3K,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAC5C,eAAO,MAAM,UAAU,SAAS,CAAA;AAChC,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAGrC,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAarD,CAAA;AAUD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CASvF;AAED,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,qEAAqE;AACrE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,SAAK,GAAG,MAAM,CAIlE;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAMtD;AAqDD,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IACnC,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IACnC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,EAAE,WAAgB,EAAE,WAAgB,EAAE,UAA+B,EAAE,QAAa,EAAE,GAAE,YAAiB,GAAG,OAAO,CA+D/K;AAqBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,GAAE,SAAS,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,SAAS,CAAC,EAAO,GAAG,WAAW,CAiDpJ;AAmBD,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,CAM3E"}