@lmzhen/dsh-evolution-settings-ui 0.6.1

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/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @lmzhen/dsh-evolution-settings-ui
2
+
3
+ Web settings cards for the evolution family's parameter namespaces. The Host half registers nothing: the namespaces belong to the plugins that own them (evolution-review, memory-files, tool-memory, evolution-curator, tool-skill-manage), and this package's browser half claims one card per namespace in the keyed `settings.plugin.item` slot.
4
+
5
+ ## What a card does
6
+
7
+ Each card renders the namespace's user-writable (E3) fields with their current value, a source badge, a write control and a revert control. Writes go through the client settings scope (`set`/`unset`), which carries the revision it read as the write's fence; a field whose key is present in the raw user section reads as a user override even when its value equals the deployment value.
8
+
9
+ The field list is GENERATED from the parameter registry (`packages/scripts/gen-param-client-view.mjs` writes `src/client/generated-params.ts`), so the browser half cannot name a parameter the Host does not register. Regenerate after every registry edit; the family gate runs the generator with `--check`.
10
+
11
+ ## Build
12
+
13
+ The browser half ships as the client module system's lazy CJS factory artifact (`lib/client.js`): `packages/scripts/build-client.mjs` runs tsdown for a CommonJS body and wraps it in `window.__ModuleLoader__.load({ id, factory })`, because the platform's own client bundle preset is not published for packages outside its repository. The Host half builds with the family's normal `packages/scripts/build-lib.mjs`.
14
+
15
+ ## Known limitations and deferred work
16
+
17
+ - Card copy lives in this package's own dictionary (`src/client/messages.ts`) and reaches components through the inject face; the platform locale seat is not bound yet (an outside package cannot declare an in-repo locale namespace type).
18
+ - The cards render plain controls with class names and no stylesheet: the family ships no CSS build step for client packages yet.
19
+ - Only E3 (user-writable) rows are rendered; deployment tiers are reported by `/evolution params` and the doctor divergence section instead.
package/lib/client.js ADDED
@@ -0,0 +1,335 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@lmzhen/dsh-evolution-settings-ui",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region src/client/generated-params.ts
9
+ const CLIENT_PARAM_SECTIONS = [
10
+ {
11
+ namespace: "evolution-curator",
12
+ fields: [
13
+ {
14
+ id: "curatorIntervalHours",
15
+ group: "curator",
16
+ doc: "Minimum hours between deterministic curation passes."
17
+ },
18
+ {
19
+ id: "staleAfterDays",
20
+ group: "curator",
21
+ doc: "Inactive days before a skill counts as stale."
22
+ },
23
+ {
24
+ id: "archiveAfterDays",
25
+ group: "curator",
26
+ doc: "Inactive days before a stale skill is archived (must be >= staleAfterDays)."
27
+ },
28
+ {
29
+ id: "qualityWarnStaleAfterDays",
30
+ group: "curator",
31
+ doc: "Age at which a low quality score starts warning."
32
+ },
33
+ {
34
+ id: "minIdleHours",
35
+ group: "curator",
36
+ doc: "Idle hours required before an automatic curation pass runs."
37
+ },
38
+ {
39
+ id: "minIdleFailOpen",
40
+ group: "curator",
41
+ doc: "Let the idle gate open when the activity probe is unavailable."
42
+ },
43
+ {
44
+ id: "llmReview",
45
+ group: "curator",
46
+ doc: "Enable the LLM nomination pass on top of the deterministic lifecycle."
47
+ },
48
+ {
49
+ id: "curatorReviewMaxTokens",
50
+ group: "curator",
51
+ doc: "Token budget of the curator LLM review."
52
+ },
53
+ {
54
+ id: "curatorReviewTimeoutMs",
55
+ group: "curator",
56
+ doc: "Wall-clock bound of the curator LLM review."
57
+ },
58
+ {
59
+ id: "healthSoftBodyChars",
60
+ group: "curator",
61
+ doc: "Body character line the health view judges against."
62
+ },
63
+ {
64
+ id: "healthStampDensityPerKb",
65
+ group: "curator",
66
+ doc: "Stamp density per KB that flags log-like content in a body."
67
+ },
68
+ {
69
+ id: "healthChurnMinPatches",
70
+ group: "curator",
71
+ doc: "Patches without a read that flag a write-ghost skill."
72
+ }
73
+ ]
74
+ },
75
+ {
76
+ namespace: "evolution-memory",
77
+ fields: [
78
+ {
79
+ id: "memoryChars",
80
+ group: "memory",
81
+ doc: "Character budget the memory store enforces for MEMORY.md."
82
+ },
83
+ {
84
+ id: "userChars",
85
+ group: "memory",
86
+ doc: "Character budget the memory store enforces for USER.md."
87
+ },
88
+ {
89
+ id: "addDatePrefix",
90
+ group: "memory",
91
+ doc: "Prefix stored memory entries with their date heading."
92
+ },
93
+ {
94
+ id: "maxConsolidationFailures",
95
+ group: "memory",
96
+ doc: "Consolidation failures one turn tolerates before the tool gives up."
97
+ }
98
+ ]
99
+ },
100
+ {
101
+ namespace: "evolution-review",
102
+ fields: [
103
+ {
104
+ id: "reviewSkillInterval",
105
+ group: "review",
106
+ doc: "Activity units between skill-review injections."
107
+ },
108
+ {
109
+ id: "reviewMemoryInterval",
110
+ group: "review",
111
+ doc: "Activity units between memory-review injections."
112
+ },
113
+ {
114
+ id: "skillReviewTrigger",
115
+ group: "review",
116
+ doc: "Which channel may inject a skill review (cadence, completion, both)."
117
+ },
118
+ {
119
+ id: "skillReviewCompletionMinToolCalls",
120
+ group: "review",
121
+ doc: "Tool calls a task needs before the completion channel injects."
122
+ },
123
+ {
124
+ id: "reviewEnabled",
125
+ group: "review",
126
+ doc: "Master switch for the review plugin."
127
+ },
128
+ {
129
+ id: "reviewMode",
130
+ group: "review",
131
+ doc: "Run the review in the parent session (inject) or on a subagent."
132
+ },
133
+ {
134
+ id: "reviewWakeInject",
135
+ group: "review",
136
+ doc: "Deliver the deferred review as a waking follow-up message."
137
+ }
138
+ ]
139
+ },
140
+ {
141
+ namespace: "evolution-skills",
142
+ fields: [
143
+ {
144
+ id: "skillContentChars",
145
+ group: "write-caps",
146
+ doc: "Character cap on a SKILL.md body (tighten-only)."
147
+ },
148
+ {
149
+ id: "maxSkillFileBytes",
150
+ group: "write-caps",
151
+ doc: "Byte cap on one support file (tighten-only)."
152
+ },
153
+ {
154
+ id: "maxSkillNameLength",
155
+ group: "write-caps",
156
+ doc: "Character cap on a skill name (tighten-only)."
157
+ },
158
+ {
159
+ id: "maxDescriptionLength",
160
+ group: "write-caps",
161
+ doc: "Character cap on a skill description (tighten-only)."
162
+ },
163
+ {
164
+ id: "descriptionStrict",
165
+ group: "write-caps",
166
+ doc: "Refuse a description over the authoring bar instead of advising."
167
+ },
168
+ {
169
+ id: "strictCrossSource",
170
+ group: "write-caps",
171
+ doc: "Refuse writes whose catalog entry resolves outside the family."
172
+ },
173
+ {
174
+ id: "citationPolicy",
175
+ group: "write-caps",
176
+ doc: "Refuse a move that would leave a dangling reference, or verify it."
177
+ },
178
+ {
179
+ id: "supportFileCharPolicy",
180
+ group: "write-caps",
181
+ doc: "Warn about an oversize support file, or refuse the write."
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ namespace: "evolution-tool-memory",
187
+ fields: [{
188
+ id: "entryPreviewChars",
189
+ group: "memory",
190
+ doc: "Characters of one memory entry shown in a tool result preview."
191
+ }]
192
+ }
193
+ ];
194
+ //#endregion
195
+ //#region src/client/ParamCard.ts
196
+ /**
197
+ * One namespace's parameter card.
198
+ *
199
+ * Structural local types on purpose: this package is distributed outside the
200
+ * platform repository and a value import of another plugin is forbidden, so the
201
+ * settings scope and its snapshot are declared by the shape the renderer hands
202
+ * over (`getSnapshot`/`subscribe`, status/value/user/writable).
203
+ */
204
+ /** One cell's text: absent reads as a dash, containers as JSON. */
205
+ function format(value) {
206
+ if (value === void 0) return "—";
207
+ if (typeof value === "string") return value;
208
+ return JSON.stringify(value);
209
+ }
210
+ /** Parse one typed value the way the command surface does: JSON, else raw text. */
211
+ function parse(draft) {
212
+ const text = draft.trim();
213
+ try {
214
+ return JSON.parse(text);
215
+ } catch {
216
+ return text;
217
+ }
218
+ }
219
+ /** Whether a value is a plain section object. */
220
+ function isSection(value) {
221
+ return typeof value === "object" && value !== null && !Array.isArray(value);
222
+ }
223
+ /** One parameter row: current value, source badge, write and reset controls. */
224
+ function FieldRow(props) {
225
+ const [draft, setDraft] = (0, react.useState)("");
226
+ return (0, react.createElement)("div", { className: "evolution-param-row" }, (0, react.createElement)("code", null, props.field.id), (0, react.createElement)("span", { className: "evolution-param-value" }, format(props.value)), (0, react.createElement)("span", { className: "evolution-param-source" }, props.overridden ? props.t("overridden") : props.t("deployment")), (0, react.createElement)("input", {
227
+ value: draft,
228
+ disabled: props.disabled,
229
+ placeholder: props.field.doc,
230
+ onChange: (event) => {
231
+ setDraft(event.target.value);
232
+ }
233
+ }), (0, react.createElement)("button", {
234
+ disabled: props.disabled || draft.trim() === "",
235
+ onClick: () => {
236
+ props.write(props.field.id, parse(draft));
237
+ }
238
+ }, props.t("apply")), props.overridden ? (0, react.createElement)("button", {
239
+ disabled: props.disabled,
240
+ onClick: () => {
241
+ props.clear(props.field.id);
242
+ }
243
+ }, props.t("reset")) : null);
244
+ }
245
+ /**
246
+ * Render one namespace's card.
247
+ * @param props - the card data, its callbacks, and the bound section hook.
248
+ * @returns the card.
249
+ */
250
+ function ParamCard(props) {
251
+ const { t, fields, write, clear } = props;
252
+ const snapshot = props.useParamSection((state) => state);
253
+ if (snapshot.status === "loading") return (0, react.createElement)("p", null, t("loading"));
254
+ if (snapshot.status === "unavailable") return (0, react.createElement)("p", null, t("unavailable"));
255
+ const user = isSection(snapshot.user) ? snapshot.user : {};
256
+ const value = isSection(snapshot.value) ? snapshot.value : {};
257
+ const disabled = !snapshot.writable;
258
+ return (0, react.createElement)("section", { className: "evolution-params" }, (0, react.createElement)("h4", null, t("title") + " — " + props.namespace), disabled ? (0, react.createElement)("p", null, t("readonly")) : null, fields.length === 0 ? (0, react.createElement)("p", null, t("empty")) : fields.map((field) => (0, react.createElement)(FieldRow, {
259
+ key: field.id,
260
+ field,
261
+ value: value[field.id],
262
+ overridden: Object.hasOwn(user, field.id),
263
+ disabled,
264
+ t,
265
+ write,
266
+ clear
267
+ })));
268
+ }
269
+ //#endregion
270
+ //#region src/client/messages.ts
271
+ /**
272
+ * Copy for the evolution parameter cards.
273
+ *
274
+ * The bundle carries its own dictionary: this package is distributed outside the
275
+ * platform repository, whose typed locale seats are declared per in-repo
276
+ * namespace. Components never inline a string — the card receives `t` through its
277
+ * inject face, which is where a locale-service binding would plug in later.
278
+ */
279
+ /** Message keys the cards render. */
280
+ const MESSAGES = {
281
+ title: "Evolution parameters",
282
+ overridden: "user override",
283
+ deployment: "deployment value",
284
+ loading: "reading the settings document…",
285
+ unavailable: "this namespace is not served by the Host (its owning plugin row is not mounted)",
286
+ readonly: "the settings document is read-only in this session (memory mode)",
287
+ apply: "Apply",
288
+ reset: "Reset to deployment",
289
+ empty: "no writable field in this namespace"
290
+ };
291
+ /**
292
+ * Resolve one message.
293
+ * @param key - the message key.
294
+ * @returns the copy this bundle carries.
295
+ */
296
+ function message(key) {
297
+ return MESSAGES[key];
298
+ }
299
+ //#endregion
300
+ //#region src/client/index.ts
301
+ /** Required client services: the slot registry and the settings transport. */
302
+ const inject = ["slots", "settingsScope"];
303
+ /**
304
+ * Register one card per generated namespace section.
305
+ * @param ctx - client cordis context.
306
+ */
307
+ function apply(ctx) {
308
+ const seam = ctx;
309
+ seam.slots.inject("settings.plugin.item", function* () {
310
+ for (const section of CLIENT_PARAM_SECTIONS) {
311
+ const scope = seam.settingsScope.bind({ namespace: section.namespace });
312
+ const source = scope;
313
+ const face = () => ({
314
+ namespace: section.namespace,
315
+ fields: section.fields,
316
+ t: (key) => message(key),
317
+ write: (field, value) => scope.set(field, value),
318
+ clear: (field) => scope.unset(field),
319
+ hooks: { paramSection: source }
320
+ });
321
+ yield seam.slots.register({
322
+ name: "settings.plugin.item",
323
+ key: section.namespace,
324
+ inject: face
325
+ }, ParamCard);
326
+ }
327
+ });
328
+ }
329
+ //#endregion
330
+ exports.apply = apply;
331
+ exports.inject = inject;
332
+
333
+ return module.exports;
334
+ }
335
+ });
package/lib/index.js ADDED
@@ -0,0 +1,18 @@
1
+ //#region lib/types/index.js
2
+ /**
3
+ * Host half of the evolution settings-UI package: it registers NOTHING.
4
+ *
5
+ * The cards are the browser half (`./client`). The Host halves that own the
6
+ * settings namespaces are the plugins themselves (evolution-review, memory-files,
7
+ * tool-memory, evolution-curator, tool-skill-manage), so this row carries no
8
+ * service and no behaviour: disabling it removes the cards and nothing else.
9
+ * @module @lmzhen/dsh-evolution-settings-ui
10
+ */
11
+ const name = "evolution-settings-ui";
12
+ /** No Host dependency: the row exists so the Loader discovers the package's
13
+ * `dsh.client` manifest and serves `./client` to the page. */
14
+ const inject = [];
15
+ /** Intentionally empty: the Host half owns no state and no registration. */
16
+ function apply() {}
17
+ //#endregion
18
+ export { apply, inject, name };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * One namespace's parameter card.
3
+ *
4
+ * Structural local types on purpose: this package is distributed outside the
5
+ * platform repository and a value import of another plugin is forbidden, so the
6
+ * settings scope and its snapshot are declared by the shape the renderer hands
7
+ * over (`getSnapshot`/`subscribe`, status/value/user/writable).
8
+ */
9
+ import { type ReactNode } from 'react';
10
+ import type { ClientParamField } from './generated-params.ts';
11
+ import type { MessageKey } from './messages.ts';
12
+ import type { ParamSectionSnapshot, ParamSectionSource } from './seam.ts';
13
+ /** Injected face: plain data and callbacks, plus the hook seat. */
14
+ export interface ParamCardFace {
15
+ namespace: string;
16
+ fields: readonly ClientParamField[];
17
+ t: (key: MessageKey) => string;
18
+ write: (field: string, value: unknown) => Promise<void>;
19
+ clear: (field: string) => Promise<void>;
20
+ hooks: {
21
+ paramSection: ParamSectionSource;
22
+ };
23
+ }
24
+ /** Props the renderer binds for one card: the inject face plus its hook seat. */
25
+ export type ParamCardProps = ParamCardFace & {
26
+ /** Bound from `hooks.paramSection` by the renderer. */
27
+ readonly useParamSection: <T>(selector: (state: ParamSectionSnapshot) => T) => T;
28
+ };
29
+ /**
30
+ * Render one namespace's card.
31
+ * @param props - the card data, its callbacks, and the bound section hook.
32
+ * @returns the card.
33
+ */
34
+ export declare function ParamCard(props: ParamCardProps): ReactNode;
35
+ //# sourceMappingURL=ParamCard.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * GENERATED by packages/scripts/gen-param-client-view.mjs — do not edit.
3
+ *
4
+ * One card per settings namespace; fields are the E3 rows of the registry, so
5
+ * the browser half names exactly what the Host half registers.
6
+ */
7
+ /** One field a card renders a control for. */
8
+ export interface ClientParamField {
9
+ id: string;
10
+ group: string;
11
+ /** The registry summary, used as the field help text. */
12
+ doc: string;
13
+ }
14
+ /** One card: the settings namespace plus the fields it exposes. */
15
+ export interface ClientParamSection {
16
+ namespace: string;
17
+ fields: readonly ClientParamField[];
18
+ }
19
+ export declare const CLIENT_PARAM_SECTIONS: readonly ClientParamSection[];
20
+ //# sourceMappingURL=generated-params.d.ts.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Browser half: one card per settings namespace the family registers.
3
+ *
4
+ * The join key is the namespace itself — the Host half of each owning plugin
5
+ * registers it, this half claims it in the keyed `settings.plugin.item` slot, and
6
+ * the configuration tab pairs them. A namespace the Host does not serve renders
7
+ * nothing, and a deployment that drops this row shows no cards at all: the pair
8
+ * carries no Host-side behaviour.
9
+ * @module @lmzhen/dsh-evolution-settings-ui/client
10
+ */
11
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
12
+ /** Required client services: the slot registry and the settings transport. */
13
+ export declare const inject: string[];
14
+ /**
15
+ * Register one card per generated namespace section.
16
+ * @param ctx - client cordis context.
17
+ */
18
+ export declare function apply(ctx: ClientContext): void;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copy for the evolution parameter cards.
3
+ *
4
+ * The bundle carries its own dictionary: this package is distributed outside the
5
+ * platform repository, whose typed locale seats are declared per in-repo
6
+ * namespace. Components never inline a string — the card receives `t` through its
7
+ * inject face, which is where a locale-service binding would plug in later.
8
+ */
9
+ /** Message keys the cards render. */
10
+ export declare const MESSAGES: {
11
+ readonly title: "Evolution parameters";
12
+ readonly overridden: "user override";
13
+ readonly deployment: "deployment value";
14
+ readonly loading: "reading the settings document…";
15
+ readonly unavailable: "this namespace is not served by the Host (its owning plugin row is not mounted)";
16
+ readonly readonly: "the settings document is read-only in this session (memory mode)";
17
+ readonly apply: "Apply";
18
+ readonly reset: "Reset to deployment";
19
+ readonly empty: "no writable field in this namespace";
20
+ };
21
+ /** One message key. */
22
+ export type MessageKey = keyof typeof MESSAGES;
23
+ /**
24
+ * Resolve one message.
25
+ * @param key - the message key.
26
+ * @returns the copy this bundle carries.
27
+ */
28
+ export declare function message(key: MessageKey): string;
29
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Structural view of the client seams this bundle uses.
3
+ *
4
+ * The package is distributed OUTSIDE the platform repository, so it declares the
5
+ * two seams it consumes instead of importing another plugin's values (forbidden
6
+ * by the client bundle-purity rule) or its types (which would drag the platform
7
+ * sources into this project). The shapes are the renderer's contract: a keyed
8
+ * `settings.plugin.item` registration, the inject face it hands back, and the
9
+ * per-namespace settings scope the write path fences with the revision it read.
10
+ * @module @lmzhen/dsh-evolution-settings-ui/client
11
+ */
12
+ /** Snapshot of one settings namespace, as the client scope reports it. */
13
+ export interface ParamSectionSnapshot {
14
+ status: 'loading' | 'ready' | 'unavailable';
15
+ value: Record<string, unknown> | undefined;
16
+ /** Raw user section: a key's PRESENCE marks the override, not its value. */
17
+ user: unknown;
18
+ writable: boolean;
19
+ }
20
+ /** The reactive source the renderer binds to a `use<Name>` seat. */
21
+ export interface ParamSectionSource {
22
+ getSnapshot(): ParamSectionSnapshot;
23
+ subscribe(listener: () => void): () => void;
24
+ }
25
+ /** One namespace's write face. */
26
+ export interface SettingsScopeLike {
27
+ set(field: string, value: unknown): Promise<void>;
28
+ unset(field: string): Promise<void>;
29
+ }
30
+ /** Registration options the keyed slot takes. */
31
+ export interface SlotRegisterOptions {
32
+ name: 'settings.plugin.item';
33
+ /** The settings namespace this card edits — the slot's key. */
34
+ key: string;
35
+ inject: () => unknown;
36
+ }
37
+ /** The slice of the client context this plugin consumes. */
38
+ export interface ClientSeam {
39
+ slots: {
40
+ inject(name: string, callback: () => Generator<unknown, void, unknown>): unknown;
41
+ register(options: SlotRegisterOptions, component: (props: never) => unknown): () => void;
42
+ };
43
+ settingsScope: {
44
+ bind(spec: {
45
+ namespace: string;
46
+ }): SettingsScopeLike;
47
+ };
48
+ }
49
+ //# sourceMappingURL=seam.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Host half of the evolution settings-UI package: it registers NOTHING.
3
+ *
4
+ * The cards are the browser half (`./client`). The Host halves that own the
5
+ * settings namespaces are the plugins themselves (evolution-review, memory-files,
6
+ * tool-memory, evolution-curator, tool-skill-manage), so this row carries no
7
+ * service and no behaviour: disabling it removes the cards and nothing else.
8
+ * @module @lmzhen/dsh-evolution-settings-ui
9
+ */
10
+ export declare const name = "evolution-settings-ui";
11
+ /** No Host dependency: the row exists so the Loader discovers the package's
12
+ * `dsh.client` manifest and serves `./client` to the page. */
13
+ export declare const inject: string[];
14
+ /** Intentionally empty: the Host half owns no state and no registration. */
15
+ export declare function apply(): void;
16
+ //# sourceMappingURL=index.d.ts.map
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@lmzhen/dsh-evolution-settings-ui",
3
+ "description": "Web settings cards for the evolution family's parameter namespaces (community build)",
4
+ "version": "0.6.1",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/lmzhen/dsh-evolution.git",
11
+ "directory": "packages/evolution-settings-ui"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./client": {
22
+ "types": "./lib/types/client/index.d.ts",
23
+ "default": "./lib/client.js"
24
+ },
25
+ "./package.json": "./package.json"
26
+ },
27
+ "files": [
28
+ "lib/*.js",
29
+ "lib/types/**/*.d.ts"
30
+ ],
31
+ "license": "MIT",
32
+ "dsh": {
33
+ "client": {
34
+ "platform": "web",
35
+ "inject": [
36
+ "@deepseek-ai/dsh-client-ui-settings-plugins"
37
+ ]
38
+ }
39
+ },
40
+ "peerDependencies": {
41
+ "@deepseek-ai/cordis": "^4.0.1",
42
+ "react": "^18.2.0"
43
+ }
44
+ }