@deepseek-ai/dsh-client-ui-model-selection 0.1.6-alpha.1 → 0.1.6-alpha.2

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-model-selection/README.md
5
- README.md: f3c373619fec0482876d8ac540f06e188b5b34d9
6
- README.zh.md: b8ee9cd73bb3fd5cdf0d0ecd8559b271c5abc933
5
+ README.md: 5e16cdf18d2eccb63097c2a524bfb2d20178d15e
6
+ README.zh.md: a99581e827c5ce66a31f6e4cbad11394e03dde4d
package/README.md CHANGED
@@ -25,7 +25,7 @@ The Web GUI lets users switch the model and reasoning effort for an existing ses
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
27
 
28
- Mount this plugin alongside `ui-conversation` and the commands package; the composer then shows the model seat next to the pending indicator, and `/model` opens the same directory as a popup. Both surfaces show the host-reported current selection when the exact provider/model pair remains in the advertised groups; a missing catalog row leaves the routable selection intact while the trigger prompts `Select model`.
28
+ Mount this plugin alongside `ui-conversation` and the commands package; the composer then shows the model seat next to the pending indicator, and `/model` opens the same directory as a popup. While the seat's menu is open, `↑`/`↓` move focus across the rows of the shown pane, Tab settles the focused row, and Escape and `Shift+Tab` leave a drilled pane first and otherwise close back to the trigger. Drilling lands on the row of the value in use, and going back lands on the cell that opened the pane left. Both surfaces show the host-reported current selection when the exact provider/model pair remains in the advertised groups; a missing catalog row leaves the routable selection intact while the trigger prompts `Select model`.
29
29
 
30
30
  ### Model and effort
31
31
 
@@ -35,6 +35,10 @@ Models stay grouped by provider. The composer menu shows model and effort names
35
35
 
36
36
  When the Host reports that no adapter serves the session's route, this plugin raises a composer block and the input goes inert with its own copy; recovering clears it without a reload. A `null` before the first load or after one failed never blocks, and catalog membership never blocks either — a route serving a model it does not advertise is missing from the groups yet usable.
37
37
 
38
+ ### Selection failures
39
+
40
+ When another writer owns the Session, model-selection failures tell the user to quit other running DSH instances and retry.
41
+
38
42
  -----
39
43
 
40
44
  <a id="understand-the-implementation"></a>
package/README.zh.md CHANGED
@@ -25,7 +25,7 @@ Web GUI 允许用户通过 `/model` 弹窗或 composer 模型控件切换既有
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 与 `ui-conversation` 及命令包一起挂载本插件;composer 随即在待处理指示器旁显示模型位,`/model` 则以弹窗打开同一份目录。当确切提供方/模型对仍在已公布分组中时,两个界面都显示 Host 报告的当前选择;目录行缺席时,可路由的选择保持不变,触发器提示 `Select model`。
28
+ 与 `ui-conversation` 及命令包一起挂载本插件;composer 随即在待处理指示器旁显示模型位,`/model` 则以弹窗打开同一份目录。模型位菜单打开期间,`↑`/`↓` 在所显示面板的行间移动焦点,`Tab` 选定聚焦行,Escape 与 `Shift+Tab` 先退出已下钻的面板,否则关闭并回到触发器。下钻落在正在使用的那一行,返回则落在打开该面板的格子上。当确切提供方/模型对仍在已公布分组中时,两个界面都显示 Host 报告的当前选择;目录行缺席时,可路由的选择保持不变,触发器提示 `Select model`。
29
29
 
30
30
  ### 模型与推理强度
31
31
 
@@ -35,6 +35,10 @@ Web GUI 允许用户通过 `/model` 弹窗或 composer 模型控件切换既有
35
35
 
36
36
  当 Host 报告没有适配器服务该会话的路由时,本插件注册一个 composer 阻塞块,输入框随之停用并显示本插件自己的文案;恢复后无需重新加载即清除。首次加载之前或加载失败之后的 `null` 绝不阻断;目录成员关系同样不阻断——一条仍在服务、只是不公布该模型的路由不在分组里,却可用。
37
37
 
38
+ ### 选择失败
39
+
40
+ 当会话被其他写句柄占用时,模型选择失败提示用户退出其他正在运行的 DSH 后重试。
41
+
38
42
  -----
39
43
 
40
44
  <a id="understand-the-implementation"></a>
package/lib/client.js CHANGED
@@ -10,6 +10,69 @@ window.__ModuleLoader__.load({
10
10
  let react_jsx_runtime = require("react/jsx-runtime");
11
11
  let react = require("react");
12
12
  let react_dom = require("react-dom");
13
+ //#region ../../util/values/src/index.ts
14
+ /**
15
+ * Weak-key lookup with a strongly retained iterable set of associated values.
16
+ *
17
+ * Each value must belong to only one key. The container performs no automatic
18
+ * cleanup; owners delete associations or clear the container at lifecycle end.
19
+ */
20
+ var WeakMapWithValues = class {
21
+ keys = /* @__PURE__ */ new WeakMap();
22
+ valueSet = /* @__PURE__ */ new Set();
23
+ /** Live strongly retained values in insertion order. */
24
+ values = this.valueSet;
25
+ /**
26
+ * Read the value associated with a key.
27
+ * @param key - weakly held lookup key.
28
+ * @returns the associated value, or absence.
29
+ */
30
+ get(key) {
31
+ return this.keys.get(key);
32
+ }
33
+ /**
34
+ * Test whether a key has an association.
35
+ * @param key - weakly held lookup key.
36
+ * @returns whether the key is present.
37
+ */
38
+ has(key) {
39
+ return this.keys.has(key);
40
+ }
41
+ /**
42
+ * Associate one key with one caller-unique value.
43
+ * @param key - weakly held lookup key.
44
+ * @param value - strongly retained value that belongs to no other key.
45
+ * @returns this container.
46
+ */
47
+ set(key, value) {
48
+ if (this.keys.has(key)) {
49
+ const previous = this.keys.get(key);
50
+ if (previous === value) return this;
51
+ this.valueSet.delete(previous);
52
+ }
53
+ this.keys.set(key, value);
54
+ this.valueSet.add(value);
55
+ return this;
56
+ }
57
+ /**
58
+ * Remove one association and its strongly retained value.
59
+ * @param key - weakly held lookup key.
60
+ * @returns whether an association was removed.
61
+ */
62
+ delete(key) {
63
+ if (!this.keys.has(key)) return false;
64
+ const value = this.keys.get(key);
65
+ const deleted = this.keys.delete(key);
66
+ this.valueSet.delete(value);
67
+ return deleted;
68
+ }
69
+ /** Remove every association and strongly retained value. */
70
+ clear() {
71
+ this.keys = /* @__PURE__ */ new WeakMap();
72
+ this.valueSet.clear();
73
+ }
74
+ };
75
+ //#endregion
13
76
  //#region lib/types/client/catalog.js
14
77
  /** One Host-generation model catalog shared by every Session selector. */
15
78
  /** Loads at most one model catalog for the current Host generation. */
@@ -146,8 +209,9 @@ window.__ModuleLoader__.load({
146
209
  /**
147
210
  * Select the complete provider/model/reasoning selection. The durable
148
211
  * projection frame updates the shared current; failures surface on the store
149
- * and throw so each entry's own retry surface engages.
212
+ * and return with the operation so each entry can present its own failure.
150
213
  * @param selection - provider, provider-owned model id, and optional adapter-owned effort.
214
+ * @returns the selection outcome, including the original Remote failure.
151
215
  */
152
216
  async select(selection) {
153
217
  this.assertAvailable();
@@ -162,22 +226,26 @@ window.__ModuleLoader__.load({
162
226
  model: selection.model,
163
227
  ...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
164
228
  });
165
- if (this.disposed || generation !== this.generation) {
166
- if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
167
- return;
168
- }
229
+ if (this.disposed || generation !== this.generation) return result.ok ? {
230
+ ok: true,
231
+ value: void 0
232
+ } : result;
169
233
  if (!result.ok) {
170
234
  this.store.update((s) => {
171
235
  s.status = "error";
172
236
  s.error = `${result.error.code}: ${result.error.message}`;
173
237
  });
174
- throw new Error(`session.selectModel failed: ${result.error.code}: ${result.error.message}`);
238
+ return result;
175
239
  }
176
240
  this.store.update((s) => {
177
241
  s.status = "ready";
178
242
  s.error = null;
179
243
  });
180
244
  this.syncInputs();
245
+ return {
246
+ ok: true,
247
+ value: void 0
248
+ };
181
249
  }
182
250
  /**
183
251
  * Invalidate an in-flight selection response from the previous Host generation.
@@ -260,7 +328,7 @@ window.__ModuleLoader__.load({
260
328
  "remote",
261
329
  "remote.session"
262
330
  ];
263
- live = { directories: /* @__PURE__ */ new Map() };
331
+ live = { directories: new WeakMapWithValues() };
264
332
  catalog;
265
333
  /** Localized composer-block copy; this plugin owns the string it raises. */
266
334
  blockReason;
@@ -275,7 +343,7 @@ window.__ModuleLoader__.load({
275
343
  this.catalog.load().catch(() => {});
276
344
  ctx.on("connection/reset", () => {
277
345
  this.catalog.resetGeneration();
278
- for (const directory of this.live.directories.values()) directory.resetConnected();
346
+ for (const directory of this.live.directories.values) directory.resetConnected();
279
347
  });
280
348
  ctx.remote.$on("llm/adapters-updated", () => {
281
349
  this.catalog.refresh();
@@ -295,15 +363,15 @@ window.__ModuleLoader__.load({
295
363
  */
296
364
  directoryFor(sessionId) {
297
365
  const { live } = this;
298
- const existing = live.directories.get(sessionId);
299
- if (existing !== void 0) return existing;
300
366
  const sessions = this.ctx.sessions;
301
367
  const actx = sessions.scope(sessionId);
302
368
  if (actx === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no scope`);
303
369
  const binding = sessions.binding(sessionId);
304
370
  if (binding === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no binding`);
371
+ const existing = live.directories.get(binding);
372
+ if (existing !== void 0) return existing;
305
373
  const directory = new ModelDirectory(this.ctx.remote.session, sessionId, () => sessions.subagentAddress(sessionId) === void 0, this.catalog, binding.session.projections.faceOf("modelSelection"));
306
- live.directories.set(sessionId, directory);
374
+ live.directories.set(binding, directory);
307
375
  const conversation = this.ctx.get("conversation");
308
376
  if (conversation !== void 0) {
309
377
  const publish = () => {
@@ -320,7 +388,7 @@ window.__ModuleLoader__.load({
320
388
  }
321
389
  actx.effect(() => () => {
322
390
  directory.dispose();
323
- live.directories.delete(sessionId);
391
+ live.directories.delete(binding);
324
392
  }, "ui-model-selection: session directory");
325
393
  return directory;
326
394
  }
@@ -387,11 +455,17 @@ window.__ModuleLoader__.load({
387
455
  * each drilling into its own list — the provider-grouped model list over
388
456
  * the shared directory, and the effort levels. The trigger (313:14108's
389
457
  * ToggleButton) shows both: model name + effort in the caption tone.
390
- * Data and submission ride the SAME per-session ModelDirectory as the
391
- * /model popup; exact-model reasoning metadata and the selected effort come
392
- * from the Host rather than a client-owned vocabulary. A rejected selection
393
- * announces through the shared transient Toast anchored to the composer
394
- * card; the in-menu strip with Retry remains the catalog-load surface.
458
+ * While open, ↑/↓ move focus across the rows of the shown pane (wrapping; a
459
+ * step taken while the trigger still holds focus enters at the near end), Tab
460
+ * settles like Enter, and Escape and Shift+Tab leave a drilled pane first and
461
+ * otherwise close back to the trigger. A drilled pane hands focus to the row
462
+ * of the value in use, and returning to the root pane hands it back to the
463
+ * cell that opened it. Data and submission ride the SAME per-session
464
+ * ModelDirectory as the /model popup; exact-model reasoning metadata and the
465
+ * selected effort come from the Host rather than a client-owned vocabulary. A
466
+ * rejected selection announces through the shared transient Toast anchored to
467
+ * the composer card; the in-menu strip with Retry remains the catalog-load
468
+ * surface.
395
469
  */
396
470
  /** Unplaced portal card: hidden but laid out at a fixed origin so offsetWidth/offsetHeight are real (Menu primitive's measure pass). */
397
471
  const MEASURE_STYLE = {
@@ -457,6 +531,18 @@ window.__ModuleLoader__.load({
457
531
  document.removeEventListener("mousedown", closeOutside);
458
532
  };
459
533
  }, [open]);
534
+ const paneFocus = (0, react.useRef)(null);
535
+ (0, react.useEffect)(() => {
536
+ const intent = paneFocus.current;
537
+ paneFocus.current = null;
538
+ if (!open || intent === null) return;
539
+ if (intent === "drill") {
540
+ (menuRef.current?.querySelector("[role=\"menuitemradio\"][aria-checked=\"true\"]:not([disabled])") ?? itemRefs.current.find((item) => item !== null && !item.disabled) ?? triggerRef.current)?.focus();
541
+ return;
542
+ }
543
+ const cell = itemRefs.current[intent === "effort" ? 1 : 0];
544
+ (cell !== null && cell !== void 0 && !cell.disabled ? cell : triggerRef.current)?.focus();
545
+ }, [open, pane]);
460
546
  (0, react.useLayoutEffect)(() => {
461
547
  if (!open) {
462
548
  setMenuPos(null);
@@ -503,20 +589,48 @@ window.__ModuleLoader__.load({
503
589
  triggerRef.current?.focus();
504
590
  });
505
591
  };
592
+ const drill = (next) => {
593
+ paneFocus.current = "drill";
594
+ setPane(next);
595
+ };
596
+ /** Leave a drilled pane for the root one, handing the keyboard back to its cell. */
597
+ const back = (from) => {
598
+ paneFocus.current = from;
599
+ setPane("root");
600
+ };
506
601
  const moveFocus = (offset) => {
507
602
  const items = itemRefs.current.filter((item) => item !== null);
508
603
  if (items.length === 0) return;
509
604
  const active = items.findIndex((item) => item === document.activeElement);
510
- items[(Math.max(active, 0) + offset + items.length) % items.length]?.focus();
605
+ items[active === -1 ? offset > 0 ? 0 : items.length - 1 : (active + offset + items.length) % items.length]?.focus();
511
606
  };
512
607
  const onRootKeyDown = (event) => {
513
608
  if (event.key === "Escape" && open) {
514
609
  event.preventDefault();
515
- if (pane !== "root") setPane("root");
610
+ if (pane !== "root") back(pane);
516
611
  else close(true);
517
612
  return;
518
613
  }
519
614
  if (!open) return;
615
+ if (event.key === "Tab") {
616
+ if (event.shiftKey) {
617
+ event.preventDefault();
618
+ if (pane !== "root") back(pane);
619
+ else close(true);
620
+ return;
621
+ }
622
+ const focused = document.activeElement;
623
+ const rows = itemRefs.current.filter((item) => item !== null);
624
+ if (focused instanceof HTMLButtonElement && rows.includes(focused)) {
625
+ event.preventDefault();
626
+ focused.click();
627
+ return;
628
+ }
629
+ if (focused !== triggerRef.current) return;
630
+ event.preventDefault();
631
+ (menuRef.current?.querySelector("[role=\"menuitemradio\"][aria-checked=\"true\"]:not([disabled])") ?? rows.find((item) => !item.disabled))?.focus();
632
+ return;
633
+ }
520
634
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
521
635
  event.preventDefault();
522
636
  moveFocus(event.key === "ArrowDown" ? 1 : -1);
@@ -526,19 +640,18 @@ window.__ModuleLoader__.load({
526
640
  if (event.relatedTarget instanceof Node && (rootRef.current?.contains(event.relatedTarget) === true || menuRef.current?.contains(event.relatedTarget) === true)) return;
527
641
  close();
528
642
  };
529
- const settleSelection = (accepted) => {
530
- if (accepted) {
643
+ const settleSelection = (result) => {
644
+ if (result === void 0) return;
645
+ if (result.ok) {
531
646
  if (rootRef.current !== null) close(true);
532
647
  return;
533
648
  }
534
- const message = directory.getSnapshot().error;
535
- if (message !== null) {
536
- toastSeq.current += 1;
537
- setToast({
538
- seq: toastSeq.current,
539
- text: t("error.action", { message })
540
- });
541
- }
649
+ const { error } = result;
650
+ toastSeq.current += 1;
651
+ setToast({
652
+ seq: toastSeq.current,
653
+ text: error.code === "session/writer-held" ? t("error.sessionInUse") : t("error.action", { message: `${error.code}: ${error.message}` })
654
+ });
542
655
  };
543
656
  const choose = (selection) => {
544
657
  if (state.current?.provider === selection.provider && state.current.model === selection.model) {
@@ -628,7 +741,7 @@ window.__ModuleLoader__.load({
628
741
  role: "menuitem",
629
742
  className: ModelSelect_module_css_default.cell,
630
743
  onClick: () => {
631
- setPane("model");
744
+ drill("model");
632
745
  },
633
746
  children: [
634
747
  (0, react_jsx_runtime.jsx)("span", {
@@ -647,7 +760,7 @@ window.__ModuleLoader__.load({
647
760
  role: "menuitem",
648
761
  className: ModelSelect_module_css_default.cell,
649
762
  onClick: () => {
650
- setPane("effort");
763
+ drill("effort");
651
764
  },
652
765
  children: [
653
766
  (0, react_jsx_runtime.jsx)("span", {
@@ -809,6 +922,7 @@ window.__ModuleLoader__.load({
809
922
  "effort.providerDefault": "Default",
810
923
  "status.loading": "正在刷新模型列表…",
811
924
  "error.action": "模型操作失败:{message}",
925
+ "error.sessionInUse": "当前会话已被占用,可能是其他正在运行的 DSH 导致的(如其他 dsh web、桌面端),请退出其他正在运行的 DSH 后重试。",
812
926
  "action.reload": "重新加载",
813
927
  "warning.groupLoad": "{name} 加载失败:{message}",
814
928
  "empty.models": "没有可用的模型。",
@@ -833,6 +947,7 @@ window.__ModuleLoader__.load({
833
947
  "effort.providerDefault": "Default",
834
948
  "status.loading": "Refreshing model list…",
835
949
  "error.action": "Model operation failed: {message}",
950
+ "error.sessionInUse": "This session is already in use, possibly by another running DSH instance (such as dsh web or the desktop app). Quit other running DSH instances and try again.",
836
951
  "action.reload": "Reload",
837
952
  "warning.groupLoad": "{name} failed to load: {message}",
838
953
  "empty.models": "No models available.",
@@ -935,7 +1050,11 @@ window.__ModuleLoader__.load({
935
1050
  const directory = models.directoryFor(session.sessionId);
936
1051
  const selection = selectionOf(directory.store.getSnapshot(), option.id);
937
1052
  if (selection === void 0) throw new Error("this provider's catalog failed to load — pick a model from a loaded group");
938
- await directory.select(selection);
1053
+ const result = await directory.select(selection);
1054
+ if (!result.ok) {
1055
+ if (result.error.code === "session/writer-held") throw new Error(t("error.sessionInUse"));
1056
+ throw result.error;
1057
+ }
939
1058
  }
940
1059
  }
941
1060
  }), "ui-model-selection: /model contribution");
@@ -955,7 +1074,7 @@ window.__ModuleLoader__.load({
955
1074
  load: () => {
956
1075
  if (available) directory.load().catch(() => {});
957
1076
  },
958
- select: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false)
1077
+ select: (selection) => available ? directory.select(selection) : Promise.resolve(void 0)
959
1078
  };
960
1079
  }
961
1080
  }, ModelSelect));
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { ModelCatalogFailure, ModelProviderGroup, ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
8
8
  import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client';
9
- import type { TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol';
9
+ import type { RemoteResult, TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol';
10
10
  import type { ObservableSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-store';
11
11
  import type { ModelCatalogDirectory } from './catalog.ts';
12
12
  /** Directory snapshot both entries render from. */
@@ -61,10 +61,11 @@ export declare class ModelDirectory {
61
61
  /**
62
62
  * Select the complete provider/model/reasoning selection. The durable
63
63
  * projection frame updates the shared current; failures surface on the store
64
- * and throw so each entry's own retry surface engages.
64
+ * and return with the operation so each entry can present its own failure.
65
65
  * @param selection - provider, provider-owned model id, and optional adapter-owned effort.
66
- */
67
- select(selection: ModelSelection): Promise<void>;
66
+ * @returns the selection outcome, including the original Remote failure.
67
+ */
68
+ select(selection: ModelSelection): Promise<RemoteResult<void>>;
68
69
  /**
69
70
  * Invalidate an in-flight selection response from the previous Host generation.
70
71
  */
@@ -25,6 +25,7 @@ export declare const zh: {
25
25
  'effort.providerDefault': string;
26
26
  'status.loading': string;
27
27
  'error.action': string;
28
+ 'error.sessionInUse': string;
28
29
  'action.reload': string;
29
30
  'warning.groupLoad': string;
30
31
  'empty.models': string;
@@ -51,6 +52,7 @@ export declare const en: {
51
52
  'effort.providerDefault': string;
52
53
  'status.loading': string;
53
54
  'error.action': string;
55
+ 'error.sessionInUse': string;
54
56
  'action.reload': string;
55
57
  'warning.groupLoad': string;
56
58
  'empty.models': string;
@@ -4,6 +4,7 @@
4
4
  * entry; this package only contributes the single occupant, so no SlotMap
5
5
  * merge lives here.
6
6
  */
7
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
7
8
  import type { ModelSelection } from '@deepseek-ai/dsh-api-remotes/client';
8
9
  import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
9
10
  import type { ModelDirectoryState } from './directory.ts';
@@ -18,8 +19,8 @@ export interface ModelSelectInjected {
18
19
  /**
19
20
  * Select a complete provider/model/reasoning selection.
20
21
  * @param selection - model selection and optional adapter-owned effort.
21
- * @returns whether the host accepted the selection.
22
+ * @returns the Host outcome, or undefined when this Session cannot select a model.
22
23
  */
23
- select: (selection: ModelSelection) => Promise<boolean>;
24
+ select: (selection: ModelSelection) => Promise<RemoteResult<void> | undefined>;
24
25
  }
25
26
  //# sourceMappingURL=slots.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-model-selection",
3
3
  "description": "Model selection over the shared model catalog, Session projection, and session.selectModel",
4
- "version": "0.1.6-alpha.1",
4
+ "version": "0.1.6-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -46,21 +46,22 @@
46
46
  "react-dom": "^18.2.0",
47
47
  "@types/react-dom": "~18.3.0",
48
48
  "clsx": "^2.1.1",
49
- "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.1",
50
- "@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.1",
51
- "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.1",
52
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.1",
53
- "@deepseek-ai/dsh-client-ui-commands": "^0.1.6-alpha.1",
54
- "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.6-alpha.1",
55
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.1",
56
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.1",
57
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.1",
49
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
50
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
51
+ "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
52
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.2",
53
+ "@deepseek-ai/dsh-client-ui-commands": "^0.1.6-alpha.2",
54
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.2",
55
+ "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.6-alpha.2",
56
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
57
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
58
58
  "@deepseek-ai/cordis": "^4.0.2",
59
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.1",
60
- "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.1",
61
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
62
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.1",
63
- "@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.1"
59
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
60
+ "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
61
+ "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
62
+ "@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2",
63
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2",
64
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2"
64
65
  },
65
66
  "files": [
66
67
  "lib/index.js",