dsh-plugin-t-expert 0.3.92 → 0.4.8

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.
@@ -0,0 +1,206 @@
1
+ // @ts-check
2
+ /**
3
+ * 技能开关的**宿主装配**(2026-09-28 从 dsh-plugin-skill-gate 整体搬进 T专家)。
4
+ *
5
+ * 逻辑一行没改,只换了落点与签名 —— 名单文件路径仍是 `~/.dsh/skill-gate.json`,
6
+ * 所以你原有的开关名单直接沿用,不需要迁移。
7
+ *
8
+ * ⚠️ 合并时唯一实质性的改动在**调用方**(lib/index.js):原插件用静态
9
+ * `inject = ["skills"]`(强依赖),那样一旦宿主没有 skills 服务,T专家 会**整个不激活**
10
+ * —— 连专家名册一起没。现在改成 `ctx.inject(["skills"], …)` 动态等待,拿不到就只少了
11
+ * 技能开关这一块,名册与召唤照常。
12
+ *
13
+ * 原理(照搬原 README):包装 `ctx.skills.snapshot / list / get`,把名单里技能的
14
+ * `modelInvocable` 置 false;`dsh-tool-skill` 生成目录时本来就会 filter(isModelInvocable),
15
+ * 清单自然变短。`list()` 内部走 `this.snapshot()`,自有属性会遮蔽原型方法,self-call 同样命中。
16
+ */
17
+ import { basename } from "node:path";
18
+ import { overlaySkill, projectCatalog, sanitizeDisabled } from "./overlay.js";
19
+ import { defaultSkillRoots, mergeSkillLists, scanSkillRoots } from "./discover.js";
20
+ import { defaultStorePath, readStore, writeStore } from "./store.js";
21
+ import { GATE_SERVICE } from "./constants.js";
22
+
23
+ export function installSkillGate(ctx, config = {}) {
24
+ const storePath = defaultStorePath(config.storePath);
25
+ /** @type {Set<string>} */
26
+ let disabled = new Set();
27
+ let revision = 0;
28
+ /** @type {Promise<void>} */
29
+ let writeChain = Promise.resolve();
30
+
31
+ const skills = ctx.skills;
32
+ const hadOwnSnapshot = Object.prototype.hasOwnProperty.call(skills, "snapshot");
33
+ const hadOwnList = Object.prototype.hasOwnProperty.call(skills, "list");
34
+ const hadOwnGet = Object.prototype.hasOwnProperty.call(skills, "get");
35
+ const previousSnapshot = skills.snapshot;
36
+ const previousList = skills.list;
37
+ const previousGet = skills.get;
38
+
39
+ async function load() {
40
+ const stored = await readStore(storePath);
41
+ disabled = new Set(stored.disabled);
42
+ revision += 1;
43
+ }
44
+
45
+ /**
46
+ * @param {string[]} names
47
+ */
48
+ async function persist(names) {
49
+ const next = sanitizeDisabled(names);
50
+ writeChain = writeChain.catch(() => {}).then(async () => {
51
+ await writeStore(storePath, next);
52
+ });
53
+ await writeChain;
54
+ disabled = new Set(next);
55
+ revision += 1;
56
+ try {
57
+ ctx.emit("skills/change");
58
+ } catch (error) {
59
+ ctx.logger?.warn?.("[skill-gate] 无法广播 skills/change:", error);
60
+ }
61
+ return snapshotState();
62
+ }
63
+
64
+ async function snapshotState() {
65
+ const views = await collectViews();
66
+ const merged = mergeSkillLists(views.map((view) => view.skills));
67
+ const projected = projectCatalog(merged, disabled, views);
68
+ return {
69
+ revision,
70
+ storePath,
71
+ skills: projected.skills,
72
+ counts: projected.counts,
73
+ views: projected.views,
74
+ };
75
+ }
76
+
77
+ /**
78
+ * 设置页要看到模型实际能看到的那份目录。
79
+ * 空 options 只读宿主全局层(web 上往往只剩 T专家 注册的 2 条随包 skill);
80
+ * 用户目录里那 300+ 条挂在 preset 的 skill-filesystem 上,必须带 scope。
81
+ * 每个来源记成一个视角,面板才能按「某个会话 / 默认 preset」回看,而不是只看并集。
82
+ */
83
+ async function collectViews() {
84
+ /** @type {{ key: string, kind: string, name?: string, skills: object[] }[]} */
85
+ const views = [];
86
+ await pushView(views, { key: "host", kind: "host" }, {});
87
+
88
+ const agents = ctx.get("agents");
89
+ if (agents !== undefined && typeof agents.list === "function") {
90
+ for (const agent of agents.list()) {
91
+ await pushView(views, { key: `agent:${agent.id}`, kind: "agent", name: agentLabel(agent) }, {
92
+ scope: agent,
93
+ cwd: agent.session?.header?.cwd,
94
+ });
95
+ }
96
+ }
97
+
98
+ const presets = ctx.get("agentPresets");
99
+ if (presets !== undefined && typeof presets.standingKeyFor === "function") {
100
+ const id = typeof presets.defaultId === "string" && presets.defaultId !== "" ? presets.defaultId : "default";
101
+ try {
102
+ const scope = await presets.standingKeyFor();
103
+ await pushView(views, { key: `preset:${id}`, kind: "preset", name: id }, { scope });
104
+ } catch (error) {
105
+ ctx.logger?.warn?.("[skill-gate] 读取默认 preset 技能失败:", error);
106
+ }
107
+ }
108
+
109
+ await pushView(views, { key: "disk", kind: "disk" }, null, () => scanSkillRoots(defaultSkillRoots(undefined)));
110
+
111
+ return views;
112
+ }
113
+
114
+ /**
115
+ * @param {{ key: string, kind: string, name?: string, skills: object[] }[]} views
116
+ * @param {{ key: string, kind: string, name?: string }} meta
117
+ * @param {object | null} options
118
+ * @param {() => Promise<object[]>} [loader]
119
+ */
120
+ async function pushView(views, meta, options, loader) {
121
+ try {
122
+ /** 局部名不能叫 skills:外层 `skills` 是服务实例,遮蔽后会撞 TDZ。 */
123
+ const found = loader !== undefined
124
+ ? await loader()
125
+ : (await previousSnapshot.call(skills, options))?.skills ?? [];
126
+ views.push({ ...meta, skills: found });
127
+ } catch (error) {
128
+ ctx.logger?.warn?.(`[skill-gate] 读取视角「${meta.key}」失败:`, error);
129
+ }
130
+ }
131
+
132
+ /** @param {{ id?: unknown, session?: { header?: { cwd?: unknown } } }} agent */
133
+ function agentLabel(agent) {
134
+ const cwd = agent?.session?.header?.cwd;
135
+ if (typeof cwd === "string" && cwd !== "") {
136
+ const name = basename(cwd);
137
+ if (name !== "" && name !== "/") return name;
138
+ }
139
+ const id = agent?.id;
140
+ return typeof id === "string" && id !== "" ? id.slice(0, 8) : "agent";
141
+ }
142
+
143
+ async function wrappedSnapshot(options) {
144
+ const snap = await previousSnapshot.call(skills, options);
145
+ return {
146
+ ...snap,
147
+ skills: (snap?.skills ?? []).map((skill) => overlaySkill(skill, disabled)),
148
+ };
149
+ }
150
+
151
+ async function wrappedList(options) {
152
+ return (await wrappedSnapshot(options)).skills;
153
+ }
154
+
155
+ async function wrappedGet(skillName, options) {
156
+ const definition = await previousGet.call(skills, skillName, options);
157
+ if (definition === undefined || definition === null) return definition;
158
+ return overlaySkill(definition, disabled);
159
+ }
160
+
161
+ skills.snapshot = wrappedSnapshot;
162
+ skills.list = wrappedList;
163
+ skills.get = wrappedGet;
164
+
165
+ ctx.effect(() => () => {
166
+ if (skills.snapshot === wrappedSnapshot) {
167
+ if (hadOwnSnapshot) skills.snapshot = previousSnapshot;
168
+ else delete skills.snapshot;
169
+ }
170
+ if (skills.list === wrappedList) {
171
+ if (hadOwnList) skills.list = previousList;
172
+ else delete skills.list;
173
+ }
174
+ if (skills.get === wrappedGet) {
175
+ if (hadOwnGet) skills.get = previousGet;
176
+ else delete skills.get;
177
+ }
178
+ }, "skill-gate: unwrap skills");
179
+
180
+ const ready = load().catch((error) => {
181
+ ctx.logger?.error?.("[skill-gate] 读取名单失败,按全部开启继续:", error);
182
+ });
183
+
184
+ ctx.reflect.provide(GATE_SERVICE, {
185
+ storePath,
186
+ getRevision: () => revision,
187
+ getDisabled: () => [...disabled].sort(),
188
+ snapshot: async () => {
189
+ await ready;
190
+ return snapshotState();
191
+ },
192
+ setDisabled: async (names) => {
193
+ await ready;
194
+ return persist(names);
195
+ },
196
+ });
197
+
198
+ void ready.then(() => {
199
+ if (disabled.size === 0) return;
200
+ try {
201
+ ctx.emit("skills/change");
202
+ } catch (error) {
203
+ ctx.logger?.warn?.("[skill-gate] 无法广播 skills/change:", error);
204
+ }
205
+ });
206
+ }
@@ -0,0 +1,150 @@
1
+ // @ts-check
2
+ /**
3
+ * 把「本插件关掉的技能」叠到 DSH 官方 invocation 策略上。
4
+ *
5
+ * 只关 modelInvocable:与 SKILL.md 里 `disable-model-invocation: true` 同一语义。
6
+ * userInvocable 不动,斜杠菜单仍能手动点。
7
+ * 原生已经 modelInvocable=false 的条目不会被本插件打开。
8
+ */
9
+
10
+ const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
11
+
12
+ /** @param {string} name */
13
+ export function isSkillName(name) {
14
+ return typeof name === "string" && SKILL_NAME.test(name);
15
+ }
16
+
17
+ /**
18
+ * @param {{ modelInvocable?: boolean, userInvocable?: boolean } | undefined} invocation
19
+ * @param {boolean} gated
20
+ */
21
+ export function overlayInvocation(invocation, gated) {
22
+ const modelInvocable = invocation?.modelInvocable !== false;
23
+ const userInvocable = invocation?.userInvocable !== false;
24
+ if (!gated) {
25
+ return {
26
+ modelInvocable,
27
+ userInvocable,
28
+ };
29
+ }
30
+ return {
31
+ modelInvocable: false,
32
+ userInvocable,
33
+ };
34
+ }
35
+
36
+ /**
37
+ * @template {{ name: string, invocation?: { modelInvocable?: boolean, userInvocable?: boolean } }} T
38
+ * @param {T} skill
39
+ * @param {ReadonlySet<string>} disabled
40
+ * @returns {T}
41
+ */
42
+ export function overlaySkill(skill, disabled) {
43
+ const gated = disabled.has(skill.name);
44
+ if (!gated) return skill;
45
+ return {
46
+ ...skill,
47
+ invocation: overlayInvocation(skill.invocation, true),
48
+ };
49
+ }
50
+
51
+ /**
52
+ * @typedef {{ key: string, kind: string, name?: string, skills?: Iterable<{ name?: unknown }> }} SkillViewInput
53
+ */
54
+
55
+ /**
56
+ * 视角是「谁在读这份目录」:宿主全局层、默认 preset、某个活跃会话、或磁盘扫描。
57
+ * 每个视角各读一份,并集落成设置页的清单,`views` 让面板能按视角回看。
58
+ * @param {Iterable<{ name?: unknown, invocation?: { modelInvocable?: boolean, userInvocable?: boolean }, description?: unknown, source?: unknown, provider?: unknown }>} skills
59
+ * @param {ReadonlySet<string>} disabled
60
+ * @param {readonly SkillViewInput[]} views
61
+ */
62
+ export function projectCatalog(skills, disabled, views = []) {
63
+ /** @type {Map<string, string[]>} */
64
+ const viewsByName = new Map();
65
+ for (const view of views) {
66
+ for (const skill of view.skills ?? []) {
67
+ if (typeof skill?.name !== "string" || !isSkillName(skill.name)) continue;
68
+ const keys = viewsByName.get(skill.name);
69
+ if (keys === undefined) viewsByName.set(skill.name, [view.key]);
70
+ else if (!keys.includes(view.key)) keys.push(view.key);
71
+ }
72
+ }
73
+ /** @type {ReturnType<typeof projectSkill>[]} */
74
+ const rows = [];
75
+ for (const skill of skills) {
76
+ if (typeof skill?.name !== "string" || !isSkillName(skill.name)) continue;
77
+ rows.push(projectSkill(/** @type {never} */ (skill), disabled, viewsByName.get(skill.name) ?? []));
78
+ }
79
+ rows.sort((a, b) => a.name.localeCompare(b.name));
80
+ const counts = {
81
+ total: rows.length,
82
+ modelVisible: 0,
83
+ gated: 0,
84
+ nativeOff: 0,
85
+ };
86
+ for (const row of rows) {
87
+ if (!row.nativeModel) counts.nativeOff += 1;
88
+ else if (row.gated) counts.gated += 1;
89
+ else counts.modelVisible += 1;
90
+ }
91
+ /**
92
+ * 视角统计与 counts 同构:一次遍历按同一判据分类累加,
93
+ * 所以 modelVisible + gated + nativeOff === total 天然成立,面板直接用这几个数。
94
+ */
95
+ const viewRows = views.map((view) => {
96
+ const names = new Set();
97
+ for (const skill of view.skills ?? []) {
98
+ if (typeof skill?.name === "string" && isSkillName(skill.name)) names.add(skill.name);
99
+ }
100
+ const row = { key: view.key, kind: view.kind, name: view.name ?? "", total: 0, modelVisible: 0, gated: 0, nativeOff: 0 };
101
+ for (const skill of rows) {
102
+ if (!names.has(skill.name)) continue;
103
+ row.total += 1;
104
+ if (!skill.nativeModel) row.nativeOff += 1;
105
+ else if (skill.gated) row.gated += 1;
106
+ else row.modelVisible += 1;
107
+ }
108
+ return row;
109
+ });
110
+ return { skills: rows, counts, views: viewRows };
111
+ }
112
+
113
+ /**
114
+ * @param {{ name: string, invocation?: { modelInvocable?: boolean, userInvocable?: boolean }, description?: unknown, source?: unknown, provider?: unknown }} skill
115
+ * @param {ReadonlySet<string>} disabled
116
+ * @param {readonly string[]} views
117
+ */
118
+ export function projectSkill(skill, disabled, views = []) {
119
+ const nativeModel = skill.invocation?.modelInvocable !== false;
120
+ const nativeUser = skill.invocation?.userInvocable !== false;
121
+ const gated = disabled.has(skill.name);
122
+ const description = typeof skill.description === "string" ? skill.description : "";
123
+ return {
124
+ name: skill.name,
125
+ description: description.length > 240 ? `${description.slice(0, 237)}…` : description,
126
+ source: typeof skill.source === "string" && skill.source !== "" ? skill.source : "unknown",
127
+ provider: typeof skill.provider === "string" && skill.provider !== "" ? skill.provider : "unknown",
128
+ nativeModel,
129
+ nativeUser,
130
+ gated,
131
+ modelVisible: nativeModel && !gated,
132
+ views: [...views],
133
+ };
134
+ }
135
+
136
+ /**
137
+ * @param {unknown} names
138
+ * @returns {string[]}
139
+ */
140
+ export function sanitizeDisabled(names) {
141
+ if (!Array.isArray(names)) return [];
142
+ const unique = new Set();
143
+ for (const item of names) {
144
+ if (typeof item !== "string") continue;
145
+ const name = item.trim();
146
+ if (!isSkillName(name)) continue;
147
+ unique.add(name);
148
+ }
149
+ return [...unique].sort();
150
+ }
@@ -0,0 +1,50 @@
1
+ // @ts-check
2
+ import { z } from "zod";
3
+
4
+ /**
5
+ * 视角数量上限。行级 `views` 与快照 `views` 共用它:
6
+ * 一个技能最多出现在全部视角里,两处上限必须相等,否则一处放宽就漏校验。
7
+ */
8
+ export const MAX_VIEWS = 256;
9
+
10
+ export const skillRowSchema = z.object({
11
+ name: z.string().min(1).max(128),
12
+ description: z.string().max(400),
13
+ source: z.string().min(1).max(64),
14
+ provider: z.string().min(1).max(64),
15
+ nativeModel: z.boolean(),
16
+ nativeUser: z.boolean(),
17
+ gated: z.boolean(),
18
+ modelVisible: z.boolean(),
19
+ /** 这条技能出现在哪些视角里;面板按视角过滤时用它。 */
20
+ views: z.array(z.string().min(1).max(128)).max(MAX_VIEWS),
21
+ });
22
+
23
+ export const countsSchema = z.object({
24
+ total: z.number().int().min(0),
25
+ modelVisible: z.number().int().min(0),
26
+ gated: z.number().int().min(0),
27
+ nativeOff: z.number().int().min(0),
28
+ });
29
+
30
+ /**
31
+ * 一个读取视角:谁在读这份目录,以及它实际看到多少。
32
+ * 四个计数与 {@link countsSchema} 同构,面板在「全部来源」和具体视角之间直接换数据源即可。
33
+ */
34
+ export const viewRowSchema = z.object({
35
+ key: z.string().min(1).max(128),
36
+ kind: z.string().min(1).max(32),
37
+ name: z.string().max(120),
38
+ total: z.number().int().min(0),
39
+ modelVisible: z.number().int().min(0),
40
+ gated: z.number().int().min(0),
41
+ nativeOff: z.number().int().min(0),
42
+ });
43
+
44
+ export const snapshotSchema = z.object({
45
+ revision: z.number().int().min(0),
46
+ storePath: z.string().min(1).max(1024),
47
+ skills: z.array(skillRowSchema).max(4000),
48
+ counts: countsSchema,
49
+ views: z.array(viewRowSchema).max(MAX_VIEWS),
50
+ });
@@ -0,0 +1,82 @@
1
+ // @ts-check
2
+ /**
3
+ * 技能开关名单:~/.dsh/skill-gate.json
4
+ *
5
+ * 故意不走 dsh-settings:那是可选服务,apply 期探测会漏;这份文件也方便手工编辑。
6
+ */
7
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
8
+ import { dirname, join } from "node:path";
9
+ import { homedir } from "node:os";
10
+ import { sanitizeDisabled } from "./overlay.js";
11
+
12
+ export const STORE_VERSION = 1;
13
+ export const DEFAULT_STORE_NAME = "skill-gate.json";
14
+
15
+ /** @param {string | undefined} configured */
16
+ export function defaultStorePath(configured) {
17
+ if (typeof configured === "string" && configured.trim() !== "") return configured.trim();
18
+ return join(homedir(), ".dsh", DEFAULT_STORE_NAME);
19
+ }
20
+
21
+ /**
22
+ * @param {string} raw
23
+ * @param {string} [path] - 仅用于错误日志;不传则日志不带路径(测试场景)。
24
+ * @returns {{ version: number, disabled: string[] }}
25
+ */
26
+ export function parseStore(raw, path) {
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(raw);
30
+ } catch (error) {
31
+ // JSON 损坏:0.4.7 之前是空 catch 静默吃,UI 会表现为"技能开关回到默认状态"且无任何线索;
32
+ // 现在出声(保留原行为 = 回到默认),下次写盘会用原子 rename 把坏文件替换掉。
33
+ console.warn(`[skill-gate] 解析${path !== undefined ? ` ${path}` : ""} 失败,按默认继续:`,
34
+ error?.message ?? error);
35
+ return { version: STORE_VERSION, disabled: [] };
36
+ }
37
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
38
+ return { version: STORE_VERSION, disabled: [] };
39
+ }
40
+ const version = typeof parsed.version === "number" && Number.isInteger(parsed.version)
41
+ ? parsed.version
42
+ : STORE_VERSION;
43
+ return {
44
+ version,
45
+ disabled: sanitizeDisabled(parsed.disabled),
46
+ };
47
+ }
48
+
49
+ /** @param {Iterable<string>} disabled */
50
+ export function serializeStore(disabled) {
51
+ return `${JSON.stringify({
52
+ version: STORE_VERSION,
53
+ disabled: sanitizeDisabled([...disabled]),
54
+ }, null, 2)}\n`;
55
+ }
56
+
57
+ /**
58
+ * @param {string} path
59
+ * @returns {Promise<{ version: number, disabled: string[] }>}
60
+ */
61
+ export async function readStore(path) {
62
+ try {
63
+ return parseStore(await readFile(path, "utf8"), path);
64
+ } catch (error) {
65
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
66
+ return { version: STORE_VERSION, disabled: [] };
67
+ }
68
+ throw error;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param {string} path
74
+ * @param {Iterable<string>} disabled
75
+ */
76
+ export async function writeStore(path, disabled) {
77
+ const body = serializeStore(disabled);
78
+ await mkdir(dirname(path), { recursive: true });
79
+ const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
80
+ await writeFile(tmp, body, "utf8");
81
+ await rename(tmp, path);
82
+ }
package/locale/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "meta": {
3
+ "description": "T Expert — a 323-expert, 22-division roster with full Chinese translations, as a standalone DeepSeek Harness plugin."
4
+ }
5
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "meta": {
3
+ "description": "T专家 —— 323 位专家、22 个分区的名册,配完整中文译文,可独立安装的 DeepSeek Harness 插件。"
4
+ }
5
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-t-expert",
3
- "version": "0.3.92",
3
+ "version": "0.4.8",
4
4
  "type": "module",
5
5
  "description": "T Expert — a 323-expert, 22-division roster with full Chinese translations, as a standalone DeepSeek Harness plugin.",
6
6
  "icon": "icon.svg",
@@ -37,16 +37,19 @@
37
37
  "./client": "./lib/client.js",
38
38
  "./remote": "./lib/remote.js",
39
39
  "./cordis.patch.yml": "./cordis.patch.yml",
40
- "./package.json": "./package.json"
40
+ "./package.json": "./package.json",
41
+ "./locale/*": "./locale/*"
41
42
  },
42
43
  "files": [
43
44
  "lib",
45
+ "locale",
44
46
  "icon.svg",
45
47
  "data/experts",
46
48
  "data/zh",
47
49
  "data/source.json",
48
50
  "cordis.patch.yml",
49
51
  "README.md",
52
+ "CHANGELOG.md",
50
53
  "LICENSE",
51
54
  "THIRD-PARTY-NOTICES",
52
55
  "vendor/third-party-licenses"
@@ -63,7 +66,8 @@
63
66
  "@deepseek-ai/dsh-api-session-controller",
64
67
  "@deepseek-ai/dsh-client-ui-settings",
65
68
  "@deepseek-ai/dsh-client-ui-input-trigger",
66
- "@deepseek-ai/dsh-client-ui-primitives"
69
+ "@deepseek-ai/dsh-client-ui-primitives",
70
+ "@deepseek-ai/dsh-client-ui-slots"
67
71
  ],
68
72
  "platform": "web"
69
73
  }
@@ -79,23 +83,23 @@
79
83
  },
80
84
  "peerDependencies": {
81
85
  "@deepseek-ai/cordis": "^4.0.1",
82
- "@deepseek-ai/dsh": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
83
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
84
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
85
- "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
86
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
87
- "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
88
- "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
89
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
90
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
91
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
92
- "@deepseek-ai/dsh-llm": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
93
- "@deepseek-ai/dsh-session": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
94
- "@deepseek-ai/dsh-settings": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
95
- "@deepseek-ai/dsh-subagent": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
96
- "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
97
- "@deepseek-ai/dsh-tools": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
98
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1",
86
+ "@deepseek-ai/dsh": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
87
+ "@deepseek-ai/dsh-agent": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
88
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
89
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
90
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
91
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
92
+ "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
93
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
94
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
95
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
96
+ "@deepseek-ai/dsh-llm": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
97
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
98
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
99
+ "@deepseek-ai/dsh-subagent": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
100
+ "@deepseek-ai/dsh-system-prompt": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
101
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
102
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.1 || ^0.1.6-alpha.2 || ^0.1.7-rc.1 || ^0.2.0-rc.1",
99
103
  "@deepseek-ai/schemastery": "^3.18.1",
100
104
  "react": "^18.2.0"
101
105
  },