dsh-edge 0.1.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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +183 -0
  4. package/README.zh.md +183 -0
  5. package/THIRD_PARTY_NOTICES.md +199 -0
  6. package/dist/_headers +3 -0
  7. package/dist/assets/fonts/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. package/dist/assets/fonts/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. package/dist/assets/fonts/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. package/dist/assets/fonts/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. package/dist/assets/fonts/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. package/dist/assets/fonts/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. package/dist/assets/fonts/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. package/dist/assets/fonts/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. package/dist/assets/fonts/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. package/dist/assets/fonts/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. package/dist/assets/fonts/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. package/dist/assets/fonts/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. package/dist/assets/fonts/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. package/dist/assets/fonts/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. package/dist/assets/fonts/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. package/dist/assets/fonts/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. package/dist/assets/fonts/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. package/dist/assets/fonts/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. package/dist/assets/fonts/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. package/dist/assets/fonts/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. package/dist/assets/fonts/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. package/dist/assets/fonts/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. package/dist/assets/fonts/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. package/dist/assets/fonts/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. package/dist/assets/fonts/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. package/dist/assets/fonts/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. package/dist/assets/fonts/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. package/dist/assets/fonts/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. package/dist/assets/fonts/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. package/dist/assets/fonts/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. package/dist/assets/fonts/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. package/dist/assets/fonts/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. package/dist/assets/fonts/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. package/dist/assets/fonts/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. package/dist/assets/fonts/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. package/dist/assets/fonts/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. package/dist/assets/fonts/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. package/dist/assets/fonts/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. package/dist/assets/fonts/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. package/dist/assets/fonts/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. package/dist/assets/fonts/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. package/dist/assets/fonts/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. package/dist/assets/fonts/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. package/dist/assets/fonts/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. package/dist/assets/fonts/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. package/dist/assets/fonts/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. package/dist/assets/fonts/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. package/dist/assets/fonts/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. package/dist/assets/fonts/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. package/dist/assets/fonts/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. package/dist/assets/fonts/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. package/dist/assets/fonts/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. package/dist/assets/index-C-1AiF3k.js +112 -0
  67. package/dist/assets/index-CSGf6Qzd.css +1 -0
  68. package/dist/assets/langs/c-BIGW1oBm.js +2 -0
  69. package/dist/assets/langs/cpp-DIPi6g--.js +2 -0
  70. package/dist/assets/langs/csharp-DSvCPggb.js +2 -0
  71. package/dist/assets/langs/css-CLj8gQPS.js +2 -0
  72. package/dist/assets/langs/go-C27-OAKa.js +2 -0
  73. package/dist/assets/langs/html-CfGypltT.js +2 -0
  74. package/dist/assets/langs/ini-BEwlwnbL.js +2 -0
  75. package/dist/assets/langs/java-CylS5w8V.js +2 -0
  76. package/dist/assets/langs/kotlin-BdnUsdx6.js +2 -0
  77. package/dist/assets/langs/less-B1dDrJ26.js +2 -0
  78. package/dist/assets/langs/lua-BaeVxFsk.js +2 -0
  79. package/dist/assets/langs/markdown-Cvjx9yec.js +2 -0
  80. package/dist/assets/langs/mdx-Cmh6b_Ma.js +2 -0
  81. package/dist/assets/langs/php-D-uCvoST.js +2 -0
  82. package/dist/assets/langs/python-B6aJPvgy.js +2 -0
  83. package/dist/assets/langs/ruby-DEItuUFs.js +2 -0
  84. package/dist/assets/langs/rust-B1yitclQ.js +2 -0
  85. package/dist/assets/langs/scss-D5BDwBP9.js +2 -0
  86. package/dist/assets/langs/sql-CRqJ_cUM.js +2 -0
  87. package/dist/assets/langs/swift-D82vCrfD.js +2 -0
  88. package/dist/assets/langs/toml-vGWfd6FD.js +2 -0
  89. package/dist/assets/langs/xml-sdJ4AIDG.js +2 -0
  90. package/dist/assets/langs/yaml-Buea-lGh.js +2 -0
  91. package/dist/assets/vendor-Cjbwl5VI.js +411 -0
  92. package/dist/assets/vendor-CjyC-hUb.css +1 -0
  93. package/dist/favicon.svg +8 -0
  94. package/dist/index.html +35 -0
  95. package/dist/manifest.webmanifest +16 -0
  96. package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +420 -0
  97. package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +5937 -0
  98. package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +10207 -0
  99. package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +1231 -0
  100. package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +259 -0
  101. package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +10539 -0
  102. package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +1725 -0
  103. package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +1116 -0
  104. package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +9832 -0
  105. package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +373 -0
  106. package/dist/plugins/@deepseek-ai/dsh-client-ui-edge/client.js +549 -0
  107. package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +859 -0
  108. package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +280 -0
  109. package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +456 -0
  110. package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +804 -0
  111. package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +461 -0
  112. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +254 -0
  113. package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +609 -0
  114. package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +292 -0
  115. package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +327 -0
  116. package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +710 -0
  117. package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +1312 -0
  118. package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +1630 -0
  119. package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +7370 -0
  120. package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +700 -0
  121. package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +474 -0
  122. package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +2426 -0
  123. package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +1372 -0
  124. package/package.json +97 -0
  125. package/scripts/assemble-web.mjs +230 -0
  126. package/scripts/bundle-size.d.mts +2 -0
  127. package/scripts/bundle-size.mjs +26 -0
  128. package/scripts/bundle.mjs +53 -0
  129. package/scripts/cli.d.mts +39 -0
  130. package/scripts/cli.mjs +496 -0
  131. package/scripts/install.d.mts +149 -0
  132. package/scripts/install.mjs +989 -0
  133. package/scripts/legal-files.mjs +24 -0
  134. package/scripts/verify-packed.mjs +32 -0
  135. package/scripts/wrangler-config.d.mts +18 -0
  136. package/scripts/wrangler-config.mjs +84 -0
  137. package/src/agent.ts +119 -0
  138. package/src/auth.ts +352 -0
  139. package/src/deepseek.ts +231 -0
  140. package/src/deployment.ts +147 -0
  141. package/src/direct-shell-core-empty.ts +10 -0
  142. package/src/direct-shell-protocol.ts +5 -0
  143. package/src/direct-shell.ts +341 -0
  144. package/src/do-session-persistence.ts +1064 -0
  145. package/src/edge-api.ts +796 -0
  146. package/src/edge-credentials.ts +56 -0
  147. package/src/edge-remotes.ts +41 -0
  148. package/src/edge-workspace-store.ts +339 -0
  149. package/src/http.ts +212 -0
  150. package/src/index.ts +196 -0
  151. package/src/instance.ts +1007 -0
  152. package/src/isolated-direct-shell-unavailable.ts +11 -0
  153. package/src/protocol.ts +39 -0
  154. package/src/release.ts +9 -0
  155. package/src/session-store.ts +1110 -0
  156. package/src/sse.ts +86 -0
  157. package/src/web-search.ts +38 -0
  158. package/src/workspace.ts +270 -0
  159. package/wrangler.jsonc +56 -0
@@ -0,0 +1,804 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-model-selection",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _deepseek_ai_cordis = require("@deepseek-ai/cordis");
8
+ let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ let react = require("react");
11
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
12
+ //#region lib/types/client/directory.js
13
+ /** One session's shared directory controller; disposed with the session scope. */
14
+ var ModelDirectory = class {
15
+ sessions;
16
+ sessionId;
17
+ available;
18
+ /** The shared snapshot both entries render from (uSES-safe store). */
19
+ store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)({
20
+ current: null,
21
+ routable: null,
22
+ groups: [],
23
+ failures: [],
24
+ status: "idle",
25
+ error: null
26
+ });
27
+ /** Latest operation wins; an older response never overwrites a newer one. */
28
+ generation = 0;
29
+ disposed = false;
30
+ /**
31
+ * @param sessions - the session wire face (captured from the plugin's root connection).
32
+ * @param sessionId - the owning session.
33
+ * @param available - whether this session may use Agent-bound model RPCs.
34
+ */
35
+ constructor(sessions, sessionId, available) {
36
+ this.sessions = sessions;
37
+ this.sessionId = sessionId;
38
+ this.available = available;
39
+ }
40
+ /**
41
+ * Refresh the advisory directory (both entries call this on open).
42
+ * Failure preserves the last good groups and current selection.
43
+ * @returns the fresh directory value.
44
+ */
45
+ async load() {
46
+ this.assertAvailable();
47
+ const generation = ++this.generation;
48
+ this.store.update((s) => {
49
+ s.status = "loading";
50
+ s.error = null;
51
+ });
52
+ const { result } = await this.sessions.models({ sessionId: this.sessionId });
53
+ if (this.disposed || generation !== this.generation) {
54
+ if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
55
+ return result.value;
56
+ }
57
+ if (!result.ok) {
58
+ this.store.update((s) => {
59
+ s.status = "error";
60
+ s.error = `${result.error.code}: ${result.error.message}`;
61
+ });
62
+ throw new Error(`session.models failed: ${result.error.code}: ${result.error.message}`);
63
+ }
64
+ const { current, routable, groups, failures } = result.value;
65
+ this.store.update((s) => {
66
+ s.current = current;
67
+ s.routable = routable;
68
+ s.groups = groups;
69
+ s.failures = failures;
70
+ s.status = "ready";
71
+ s.error = null;
72
+ });
73
+ return result.value;
74
+ }
75
+ /**
76
+ * Select the complete provider/model/reasoning selection (both entries submit through here). Success
77
+ * updates the shared current; failure surfaces on the store and throws so
78
+ * each entry's own retry surface engages.
79
+ * @param selection - provider, provider-owned model id, and optional adapter-owned effort.
80
+ */
81
+ async select(selection) {
82
+ this.assertAvailable();
83
+ const generation = ++this.generation;
84
+ this.store.update((s) => {
85
+ s.status = "selecting";
86
+ s.error = null;
87
+ });
88
+ const { result } = await this.sessions.selectModel({
89
+ sessionId: this.sessionId,
90
+ provider: selection.provider,
91
+ model: selection.model,
92
+ ...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
93
+ });
94
+ if (this.disposed || generation !== this.generation) {
95
+ if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
96
+ return;
97
+ }
98
+ if (!result.ok) {
99
+ this.store.update((s) => {
100
+ s.status = "error";
101
+ s.error = `${result.error.code}: ${result.error.message}`;
102
+ });
103
+ throw new Error(`session.selectModel failed: ${result.error.code}: ${result.error.message}`);
104
+ }
105
+ this.store.update((s) => {
106
+ s.current = result.value.selected;
107
+ s.routable = true;
108
+ s.status = "ready";
109
+ s.error = null;
110
+ });
111
+ }
112
+ /**
113
+ * Drop the previous Host generation's projection and repull it. Clearing
114
+ * first prevents an unconsumed process-local selection from being displayed
115
+ * while the restarted Host has restored the last logged model selection.
116
+ */
117
+ resetConnected() {
118
+ if (this.disposed) return;
119
+ ++this.generation;
120
+ this.store.update((s) => {
121
+ s.current = null;
122
+ s.routable = null;
123
+ s.groups = [];
124
+ s.failures = [];
125
+ s.status = "idle";
126
+ s.error = null;
127
+ });
128
+ if (!this.available()) return;
129
+ this.load().catch(() => {});
130
+ }
131
+ /** Scope teardown: late settlements lose write access to the store. */
132
+ dispose() {
133
+ this.disposed = true;
134
+ }
135
+ assertAvailable() {
136
+ if (!this.available()) throw new Error("model selection is unavailable for addressed subagent sessions");
137
+ }
138
+ };
139
+ //#endregion
140
+ //#region lib/types/client/service.js
141
+ /**
142
+ * ModelDirectoryResolver (`ctx.modelDirectories`): the root owner of per-session
143
+ * {@link ModelDirectory} instances. Both selection entries (the /model popup
144
+ * and the composer model seat) resolve their session's directory through
145
+ * this service, which is what makes the dual entry one shared state.
146
+ *
147
+ * Per-session storage follows the client service pattern (InputTriggerService /
148
+ * CommandUiRuntime): a lazy service-internal map whose entry is deleted by the
149
+ * owning scope's disposer. The host `dsh-scope` ScopedLayers registry does
150
+ * does not belong here: it derives scope from the host carrier mechanism
151
+ * (object-keyed), while client scopes tag contexts with branded SessionId
152
+ * strings, and it models global+shadow named registries — this is a
153
+ * per-session singleton with no global layer to merge.
154
+ */
155
+ /** The `ctx.modelDirectories` session model-selection service. */
156
+ var ModelDirectoryResolver = class extends _deepseek_ai_cordis.Service {
157
+ static inject = [
158
+ "connection",
159
+ "sessions",
160
+ "remote"
161
+ ];
162
+ live = { directories: /* @__PURE__ */ new Map() };
163
+ /** Localized composer-block copy; this plugin owns the string it raises. */
164
+ blockReason;
165
+ /**
166
+ * @param ctx - owning root context (the service registers itself as `models`).
167
+ * @param config - the bound translator for this plugin's own dictionary.
168
+ */
169
+ constructor(ctx, config) {
170
+ super(ctx, "modelDirectories");
171
+ this.blockReason = config.blockReason;
172
+ ctx.on("connection/reset", () => {
173
+ for (const directory of this.live.directories.values()) directory.resetConnected();
174
+ });
175
+ const refresh = () => {
176
+ for (const directory of this.live.directories.values()) directory.load().catch(() => void 0);
177
+ };
178
+ ctx.remote.$on("llm/adapters-updated", refresh);
179
+ ctx.remote.$on("settings/document-updated", refresh);
180
+ }
181
+ /**
182
+ * Resolve the per-session shared directory (lazy; the scope disposer
183
+ * removes and disposes it). Unknown sessions fail loud.
184
+ * @param sessionId - the owning session.
185
+ * @returns the resident directory both entries share.
186
+ */
187
+ directoryFor(sessionId) {
188
+ const { live } = this;
189
+ const existing = live.directories.get(sessionId);
190
+ if (existing !== void 0) return existing;
191
+ const sessions = this.ctx.get("sessions");
192
+ const actx = sessions.scope(sessionId);
193
+ if (actx === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no scope`);
194
+ const directory = new ModelDirectory(this.ctx.get("connection").api.sessions, sessionId, () => sessions.subagentAddress(sessionId) === void 0);
195
+ live.directories.set(sessionId, directory);
196
+ const conversation = this.ctx.get("conversation");
197
+ if (conversation !== void 0) {
198
+ const publish = () => {
199
+ conversation.blocks.set(sessionId, directory.store.getSnapshot().routable === false ? { reason: this.blockReason() } : void 0);
200
+ };
201
+ publish();
202
+ actx.effect(() => {
203
+ const stop = directory.store.subscribe(publish);
204
+ return () => {
205
+ stop();
206
+ conversation.blocks.set(sessionId, void 0);
207
+ };
208
+ }, "ui-model-selection: composer block");
209
+ }
210
+ actx.effect(() => () => {
211
+ directory.dispose();
212
+ live.directories.delete(sessionId);
213
+ }, "ui-model-selection: session directory");
214
+ return directory;
215
+ }
216
+ };
217
+ //#endregion
218
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
219
+ function r(e) {
220
+ var t, f, n = "";
221
+ if ("string" == typeof e || "number" == typeof e) n += e;
222
+ else if ("object" == typeof e) if (Array.isArray(e)) {
223
+ var o = e.length;
224
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
225
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
226
+ return n;
227
+ }
228
+ function clsx() {
229
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
230
+ return n;
231
+ }
232
+ //#endregion
233
+ //#region \0dsh-css:/private/var/folders/nm/n5smty9962gf5rhjc5855kt80000gn/T/dsh-edge-release-worktree.XXXXXX.RcJjogaGt4/repo/packages/client/ui-model-selection/src/client/ModelSelect.module.css.mjs
234
+ const css = "._6ruuW_root{min-width:0;position:relative}._6ruuW_trigger{min-width:0;max-width:220px;height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;border-radius:24px;outline:none;align-items:center;gap:4px;padding:0 4px 0 8px;font-size:13px;font-weight:500;line-height:20px;display:flex}._6ruuW_trigger:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_trigger:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3)}._6ruuW_trigger:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}._6ruuW_triggerLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}._6ruuW_triggerEffort{color:var(--dsw-alias-label-caption);flex:none}._6ruuW_chevron{color:var(--dsw-alias-label-caption);flex:none;transition:transform .12s}._6ruuW_chevronOpen{transform:rotate(180deg)}._6ruuW_menu{z-index:20;border:1px solid var(--dsw-alias-border-inverted);background:var(--dsw-specific-menu);width:min(240px,100vw - 32px);max-height:min(360px,100vh - 96px);box-shadow:var(--dsw-shadow-lv3);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;bottom:calc(100% + 8px);right:0;overflow:hidden}._6ruuW_status,._6ruuW_empty{color:var(--dsw-alias-label-tertiary);padding:10px;font-size:13px;line-height:20px}._6ruuW_error,._6ruuW_warning{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary);border-radius:8px;justify-content:space-between;align-items:flex-start;gap:8px;margin-bottom:4px;padding:7px 8px;font-size:12px;line-height:18px;display:flex}._6ruuW_warning{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-state-warn-label)}._6ruuW_retry{color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;flex:none;padding:0;font-weight:600}._6ruuW_groups{min-height:0;overflow-y:auto}._6ruuW_group+._6ruuW_group{margin-top:4px}._6ruuW_groupTitle{z-index:1;background:var(--dsw-specific-menu);color:var(--dsw-alias-label-tertiary);padding:5px 8px 3px;font-size:12px;font-weight:500;line-height:18px;position:sticky;top:0}._6ruuW_option{width:100%;min-height:38px;color:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:10px;outline:none;align-items:center;gap:8px;padding:6px 8px;display:flex}._6ruuW_option:hover:not(:disabled),._6ruuW_option:focus-visible{background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_selected{background:0 0}._6ruuW_option:disabled{color:var(--dsw-alias-label-dimmed);cursor:default}._6ruuW_optionCopy{flex-direction:column;flex:1;min-width:0;display:flex}._6ruuW_modelName{color:inherit;text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:20px;overflow:hidden}._6ruuW_description{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}._6ruuW_check{color:var(--dsw-alias-label-primary);flex:0 0 18px;place-items:center;display:grid}._6ruuW_cell{width:100%;height:40px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;background:0 0;border:none;border-radius:10px;align-items:center;gap:8px;padding:0 10px;font-size:14px;line-height:22px;display:flex}._6ruuW_cell:hover{background:var(--dsw-alias-interactive-bg-hover)}._6ruuW_cellLabel{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}._6ruuW_cellValue{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:0 auto;overflow:hidden}._6ruuW_cellChevron{color:var(--dsw-alias-label-tertiary);flex:none}";
235
+ const tagId = "@deepseek-ai/dsh-client-ui-model-selection/ModelSelect.module.css";
236
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
237
+ const tag = document.createElement("style");
238
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-model-selection";
239
+ tag.dataset.pluginCss = tagId;
240
+ tag.textContent = css;
241
+ document.head.appendChild(tag);
242
+ }
243
+ var ModelSelect_module_css_default = {
244
+ "trigger": "_6ruuW_trigger",
245
+ "groupTitle": "_6ruuW_groupTitle",
246
+ "chevron": "_6ruuW_chevron",
247
+ "optionCopy": "_6ruuW_optionCopy",
248
+ "groups": "_6ruuW_groups",
249
+ "check": "_6ruuW_check",
250
+ "error": "_6ruuW_error",
251
+ "cell": "_6ruuW_cell",
252
+ "root": "_6ruuW_root",
253
+ "group": "_6ruuW_group",
254
+ "option": "_6ruuW_option",
255
+ "selected": "_6ruuW_selected",
256
+ "modelName": "_6ruuW_modelName",
257
+ "empty": "_6ruuW_empty",
258
+ "triggerLabel": "_6ruuW_triggerLabel",
259
+ "chevronOpen": "_6ruuW_chevronOpen",
260
+ "retry": "_6ruuW_retry",
261
+ "description": "_6ruuW_description",
262
+ "cellLabel": "_6ruuW_cellLabel",
263
+ "cellValue": "_6ruuW_cellValue",
264
+ "cellChevron": "_6ruuW_cellChevron",
265
+ "status": "_6ruuW_status",
266
+ "warning": "_6ruuW_warning",
267
+ "menu": "_6ruuW_menu",
268
+ "triggerEffort": "_6ruuW_triggerEffort"
269
+ };
270
+ //#endregion
271
+ //#region lib/types/client/ModelSelect.js
272
+ /**
273
+ * ModelSelect: the composer's named model seat (`conversation.input.model`).
274
+ * Two-level selection per figma 496:26454's MenuDropdown: the root menu is
275
+ * the Model / Effort row pair (label + current value + a right chevron),
276
+ * each drilling into its own list — the provider-grouped model list over
277
+ * the shared directory, and the effort levels. The trigger (313:14108's
278
+ * ToggleButton) shows both: model name + effort in the caption tone.
279
+ * Data and submission ride the SAME per-session ModelDirectory as the
280
+ * /model popup; exact-model reasoning metadata and the selected effort come
281
+ * from the Host rather than a client-owned vocabulary. A rejected selection
282
+ * announces through the shared transient Toast anchored to the composer
283
+ * card; the in-menu strip with Retry remains the catalog-load surface.
284
+ */
285
+ /**
286
+ * Render the composer model seat.
287
+ * @param props - owner share (locked) + injected face (shared directory
288
+ * store/verbs) + the standard locale seat.
289
+ * @returns the trigger and, while open, the two-level menu.
290
+ */
291
+ function ModelSelect({ locked, available, directory, load, select, t }) {
292
+ const state = (0, react.useSyncExternalStore)((fn) => directory.subscribe(fn), () => directory.getSnapshot());
293
+ const [open, setOpen] = (0, react.useState)(false);
294
+ const [pane, setPane] = (0, react.useState)("root");
295
+ const lastActionRef = (0, react.useRef)("load");
296
+ const [toast, setToast] = (0, react.useState)(null);
297
+ const toastSeq = (0, react.useRef)(0);
298
+ const rootRef = (0, react.useRef)(null);
299
+ const triggerRef = (0, react.useRef)(null);
300
+ const itemRefs = (0, react.useRef)([]);
301
+ const id = (0, react.useId)();
302
+ const choices = (0, react.useMemo)(() => state.groups.flatMap((group) => group.models.map((model) => ({
303
+ group,
304
+ model,
305
+ selection: {
306
+ provider: group.id,
307
+ model: model.id,
308
+ ...model.reasoning?.defaultEffort === void 0 ? {} : { reasoningEffort: model.reasoning.defaultEffort }
309
+ }
310
+ }))), [state.groups]);
311
+ const currentChoice = choices[state.current === null ? -1 : choices.findIndex((c) => c.selection.provider === state.current?.provider && c.selection.model === state.current.model)];
312
+ const reasoning = currentChoice?.model.reasoning;
313
+ const effectiveEffort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
314
+ const effortLabel = reasoning === void 0 ? void 0 : effectiveEffort === void 0 ? t("effort.providerDefault") : reasoning.efforts.find((level) => level.id === effectiveEffort)?.name ?? effectiveEffort;
315
+ const effortChoices = (0, react.useMemo)(() => reasoning === void 0 ? [] : [...reasoning.defaultEffort === void 0 ? [{
316
+ key: "provider-default",
317
+ effort: void 0,
318
+ label: t("effort.providerDefault")
319
+ }] : [], ...reasoning.efforts.map((effort) => ({
320
+ key: `effort:${effort.id}`,
321
+ effort: effort.id,
322
+ label: effort.name,
323
+ ...effort.description === void 0 ? {} : { description: effort.description }
324
+ }))], [reasoning, t]);
325
+ const busy = state.status === "selecting";
326
+ const reload = () => {
327
+ lastActionRef.current = "load";
328
+ load();
329
+ };
330
+ (0, react.useEffect)(() => {
331
+ if (available) {
332
+ lastActionRef.current = "load";
333
+ load();
334
+ }
335
+ }, [available, load]);
336
+ (0, react.useEffect)(() => {
337
+ if (!open) return;
338
+ const closeOutside = (event) => {
339
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
340
+ };
341
+ document.addEventListener("mousedown", closeOutside);
342
+ return () => {
343
+ document.removeEventListener("mousedown", closeOutside);
344
+ };
345
+ }, [open]);
346
+ if (!available) return null;
347
+ const show = () => {
348
+ setPane("root");
349
+ setOpen(true);
350
+ reload();
351
+ };
352
+ const close = (restoreFocus = false) => {
353
+ setOpen(false);
354
+ setPane("root");
355
+ if (restoreFocus) queueMicrotask(() => {
356
+ triggerRef.current?.focus();
357
+ });
358
+ };
359
+ const moveFocus = (offset) => {
360
+ const items = itemRefs.current.filter((item) => item !== null);
361
+ if (items.length === 0) return;
362
+ const active = items.findIndex((item) => item === document.activeElement);
363
+ items[(Math.max(active, 0) + offset + items.length) % items.length]?.focus();
364
+ };
365
+ const onRootKeyDown = (event) => {
366
+ if (event.key === "Escape" && open) {
367
+ event.preventDefault();
368
+ if (pane !== "root") setPane("root");
369
+ else close(true);
370
+ return;
371
+ }
372
+ if (!open) return;
373
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
374
+ event.preventDefault();
375
+ moveFocus(event.key === "ArrowDown" ? 1 : -1);
376
+ }
377
+ };
378
+ const onBlur = (event) => {
379
+ if (event.relatedTarget instanceof Node && rootRef.current?.contains(event.relatedTarget)) return;
380
+ close();
381
+ };
382
+ const settleSelection = (accepted) => {
383
+ if (accepted) {
384
+ if (rootRef.current !== null) close(true);
385
+ return;
386
+ }
387
+ const message = directory.getSnapshot().error;
388
+ if (message !== null) {
389
+ toastSeq.current += 1;
390
+ setToast({
391
+ seq: toastSeq.current,
392
+ text: t("error.action", { message })
393
+ });
394
+ }
395
+ };
396
+ const choose = (selection) => {
397
+ if (state.current?.provider === selection.provider && state.current.model === selection.model) {
398
+ close(true);
399
+ return;
400
+ }
401
+ lastActionRef.current = "select";
402
+ select(selection).then(settleSelection);
403
+ };
404
+ const chooseEffort = (effort) => {
405
+ if (state.current === null) return;
406
+ if (effectiveEffort === effort) {
407
+ close(true);
408
+ return;
409
+ }
410
+ const selection = {
411
+ provider: state.current.provider,
412
+ model: state.current.model,
413
+ ...effort === void 0 ? {} : { reasoningEffort: effort }
414
+ };
415
+ lastActionRef.current = "select";
416
+ select(selection).then(settleSelection);
417
+ };
418
+ const modelLabel = currentChoice?.model.name ?? t("trigger.fallback");
419
+ const triggerLabel = effortLabel === void 0 ? modelLabel : `${modelLabel} · ${effortLabel}`;
420
+ const triggerAria = currentChoice === void 0 ? t("trigger.selectAria") : effortLabel === void 0 ? t("trigger.aria", { model: modelLabel }) : t("trigger.ariaEffort", {
421
+ model: modelLabel,
422
+ effort: effortLabel
423
+ });
424
+ itemRefs.current = [];
425
+ let itemIndex = 0;
426
+ const itemRef = () => {
427
+ const at = itemIndex++;
428
+ return (node) => {
429
+ itemRefs.current[at] = node;
430
+ };
431
+ };
432
+ return (0, react_jsx_runtime.jsxs)("div", {
433
+ ref: rootRef,
434
+ className: ModelSelect_module_css_default.root,
435
+ onKeyDown: onRootKeyDown,
436
+ onBlur,
437
+ children: [
438
+ (0, react_jsx_runtime.jsxs)("button", {
439
+ ref: triggerRef,
440
+ type: "button",
441
+ className: ModelSelect_module_css_default.trigger,
442
+ "aria-label": triggerAria,
443
+ "aria-haspopup": "menu",
444
+ "aria-expanded": open,
445
+ "aria-controls": open ? `${id}-menu` : void 0,
446
+ title: triggerLabel,
447
+ disabled: locked,
448
+ onClick: () => {
449
+ if (open) close();
450
+ else show();
451
+ },
452
+ children: [
453
+ (0, react_jsx_runtime.jsx)("span", {
454
+ className: ModelSelect_module_css_default.triggerLabel,
455
+ children: modelLabel
456
+ }),
457
+ effortLabel !== void 0 && (0, react_jsx_runtime.jsx)("span", {
458
+ className: ModelSelect_module_css_default.triggerEffort,
459
+ children: effortLabel
460
+ }),
461
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(ModelSelect_module_css_default.chevron, open && ModelSelect_module_css_default.chevronOpen) })
462
+ ]
463
+ }),
464
+ open && (0, react_jsx_runtime.jsxs)("div", {
465
+ id: `${id}-menu`,
466
+ className: ModelSelect_module_css_default.menu,
467
+ role: "menu",
468
+ "aria-label": t("menu.aria"),
469
+ "aria-busy": state.status === "loading" || busy,
470
+ children: [
471
+ pane === "root" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("button", {
472
+ ref: itemRef(),
473
+ type: "button",
474
+ role: "menuitem",
475
+ className: ModelSelect_module_css_default.cell,
476
+ onClick: () => {
477
+ setPane("model");
478
+ },
479
+ children: [
480
+ (0, react_jsx_runtime.jsx)("span", {
481
+ className: ModelSelect_module_css_default.cellLabel,
482
+ children: t("menu.model")
483
+ }),
484
+ (0, react_jsx_runtime.jsx)("span", {
485
+ className: ModelSelect_module_css_default.cellValue,
486
+ children: modelLabel
487
+ }),
488
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { className: ModelSelect_module_css_default.cellChevron })
489
+ ]
490
+ }), reasoning !== void 0 && (0, react_jsx_runtime.jsxs)("button", {
491
+ ref: itemRef(),
492
+ type: "button",
493
+ role: "menuitem",
494
+ className: ModelSelect_module_css_default.cell,
495
+ onClick: () => {
496
+ setPane("effort");
497
+ },
498
+ children: [
499
+ (0, react_jsx_runtime.jsx)("span", {
500
+ className: ModelSelect_module_css_default.cellLabel,
501
+ children: t("menu.effort")
502
+ }),
503
+ (0, react_jsx_runtime.jsx)("span", {
504
+ className: ModelSelect_module_css_default.cellValue,
505
+ children: effortLabel
506
+ }),
507
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronRightOutline14, { className: ModelSelect_module_css_default.cellChevron })
508
+ ]
509
+ })] }),
510
+ pane === "model" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
511
+ state.status === "loading" && (0, react_jsx_runtime.jsx)("div", {
512
+ className: ModelSelect_module_css_default.status,
513
+ children: t("status.loading")
514
+ }),
515
+ state.error !== null && lastActionRef.current === "load" && (0, react_jsx_runtime.jsxs)("div", {
516
+ className: ModelSelect_module_css_default.error,
517
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("error.action", { message: state.error }) }), (0, react_jsx_runtime.jsx)("button", {
518
+ type: "button",
519
+ className: ModelSelect_module_css_default.retry,
520
+ onClick: reload,
521
+ children: t("retry")
522
+ })]
523
+ }),
524
+ state.failures.map((failure) => (0, react_jsx_runtime.jsxs)("div", {
525
+ className: ModelSelect_module_css_default.warning,
526
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("warning.groupLoad", {
527
+ name: failure.name,
528
+ message: failure.message
529
+ }) }), (0, react_jsx_runtime.jsx)("button", {
530
+ type: "button",
531
+ className: ModelSelect_module_css_default.retry,
532
+ onClick: reload,
533
+ children: t("retry")
534
+ })]
535
+ }, failure.id)),
536
+ (0, react_jsx_runtime.jsx)("div", {
537
+ className: clsx(ModelSelect_module_css_default.groups, "scrollable"),
538
+ children: state.groups.map((group) => {
539
+ const headingId = `${id}-${group.id}`;
540
+ return (0, react_jsx_runtime.jsxs)("section", {
541
+ role: "group",
542
+ "aria-labelledby": headingId,
543
+ className: ModelSelect_module_css_default.group,
544
+ children: [(0, react_jsx_runtime.jsx)("div", {
545
+ className: ModelSelect_module_css_default.groupTitle,
546
+ id: headingId,
547
+ children: group.name
548
+ }), group.models.map((model) => {
549
+ const selected = state.current?.provider === group.id && state.current.model === model.id;
550
+ return (0, react_jsx_runtime.jsxs)("button", {
551
+ ref: itemRef(),
552
+ type: "button",
553
+ role: "menuitemradio",
554
+ "aria-checked": selected,
555
+ className: clsx(ModelSelect_module_css_default.option, selected && ModelSelect_module_css_default.selected),
556
+ title: model.name,
557
+ disabled: busy,
558
+ onClick: () => {
559
+ choose({
560
+ provider: group.id,
561
+ model: model.id
562
+ });
563
+ },
564
+ children: [(0, react_jsx_runtime.jsxs)("span", {
565
+ className: ModelSelect_module_css_default.optionCopy,
566
+ children: [(0, react_jsx_runtime.jsx)("span", {
567
+ className: ModelSelect_module_css_default.modelName,
568
+ children: model.name
569
+ }), model.description !== void 0 && (0, react_jsx_runtime.jsx)("span", {
570
+ className: ModelSelect_module_css_default.description,
571
+ children: model.description
572
+ })]
573
+ }), (0, react_jsx_runtime.jsx)("span", {
574
+ className: ModelSelect_module_css_default.check,
575
+ children: selected ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
576
+ })]
577
+ }, model.id);
578
+ })]
579
+ }, group.id);
580
+ })
581
+ }),
582
+ state.status === "ready" && choices.length === 0 && (0, react_jsx_runtime.jsx)("div", {
583
+ className: ModelSelect_module_css_default.empty,
584
+ children: t("empty.models")
585
+ })
586
+ ] }),
587
+ pane === "effort" && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [state.error !== null && lastActionRef.current === "load" && (0, react_jsx_runtime.jsxs)("div", {
588
+ className: ModelSelect_module_css_default.error,
589
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("error.action", { message: state.error }) }), (0, react_jsx_runtime.jsx)("button", {
590
+ type: "button",
591
+ className: ModelSelect_module_css_default.retry,
592
+ onClick: reload,
593
+ children: t("action.reload")
594
+ })]
595
+ }), effortChoices.length === 0 ? (0, react_jsx_runtime.jsx)("div", {
596
+ className: ModelSelect_module_css_default.empty,
597
+ children: t("empty.efforts")
598
+ }) : effortChoices.map((level) => (0, react_jsx_runtime.jsxs)("button", {
599
+ ref: itemRef(),
600
+ type: "button",
601
+ role: "menuitemradio",
602
+ "aria-checked": effectiveEffort === level.effort,
603
+ className: clsx(ModelSelect_module_css_default.option, effectiveEffort === level.effort && ModelSelect_module_css_default.selected),
604
+ disabled: busy,
605
+ onClick: () => {
606
+ chooseEffort(level.effort);
607
+ },
608
+ children: [(0, react_jsx_runtime.jsxs)("span", {
609
+ className: ModelSelect_module_css_default.optionCopy,
610
+ children: [(0, react_jsx_runtime.jsx)("span", {
611
+ className: ModelSelect_module_css_default.modelName,
612
+ children: level.label
613
+ }), level.description !== void 0 && (0, react_jsx_runtime.jsx)("span", {
614
+ className: ModelSelect_module_css_default.description,
615
+ children: level.description
616
+ })]
617
+ }), (0, react_jsx_runtime.jsx)("span", {
618
+ className: ModelSelect_module_css_default.check,
619
+ children: effectiveEffort === level.effort ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCheckOutline16, {}) : null
620
+ })]
621
+ }, level.key))] })
622
+ ]
623
+ }),
624
+ toast !== null && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
625
+ text: toast.text,
626
+ icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16, {}),
627
+ anchor: rootRef.current?.closest("[data-composer-card]") ?? null,
628
+ onDone: () => {
629
+ setToast(null);
630
+ }
631
+ }, toast.seq)
632
+ ]
633
+ });
634
+ }
635
+ //#endregion
636
+ //#region lib/types/client/locales.js
637
+ /**
638
+ * `model` namespace dictionaries.
639
+ *
640
+ * `trigger.selectAria` reads identically to `trigger.fallback` today and is
641
+ * still a separate key: the visible fallback label and the accessible name of
642
+ * an unset trigger are free to diverge per locale, and folding it into
643
+ * `trigger.aria` would announce the degenerate "Select model, current Select
644
+ * model".
645
+ */
646
+ /** Simplified Chinese dictionary (the key-set source of truth). */
647
+ const zh = {
648
+ "command.description": "选择本会话使用的模型",
649
+ "option.loadError": "目录加载失败:{message}",
650
+ "trigger.fallback": "选择模型",
651
+ "trigger.selectAria": "选择模型",
652
+ "trigger.aria": "选择模型,当前 {model}",
653
+ "trigger.ariaEffort": "选择模型,当前 {model},推理等级 {effort}",
654
+ "menu.aria": "模型与推理等级",
655
+ "menu.model": "模型",
656
+ "menu.effort": "推理等级",
657
+ "effort.providerDefault": "Default",
658
+ "status.loading": "正在刷新模型列表…",
659
+ "error.action": "模型操作失败:{message}",
660
+ "action.reload": "重新加载",
661
+ "warning.groupLoad": "{name} 加载失败:{message}",
662
+ "empty.models": "没有可用的模型。",
663
+ "blocked.composer": "当前模型不可用,请先选择模型",
664
+ "empty.efforts": "当前模型未提供推理等级。"
665
+ };
666
+ /** English dictionary, checked complete against the zh key set. */
667
+ const en = {
668
+ "command.description": "Select the model for this conversation",
669
+ "option.loadError": "Catalog failed to load: {message}",
670
+ "trigger.fallback": "Select model",
671
+ "trigger.selectAria": "Select model",
672
+ "trigger.aria": "Select model, current {model}",
673
+ "trigger.ariaEffort": "Select model, current {model}, reasoning effort {effort}",
674
+ "menu.aria": "Model and reasoning effort",
675
+ "menu.model": "Model",
676
+ "menu.effort": "Effort",
677
+ "effort.providerDefault": "Default",
678
+ "status.loading": "Refreshing model list…",
679
+ "error.action": "Model operation failed: {message}",
680
+ "action.reload": "Reload",
681
+ "warning.groupLoad": "{name} failed to load: {message}",
682
+ "empty.models": "No models available.",
683
+ "blocked.composer": "This model is unavailable — select one to continue",
684
+ "empty.efforts": "This model provides no reasoning effort levels."
685
+ };
686
+ //#endregion
687
+ //#region lib/types/client/index.js
688
+ /** One selectable row's id: an opaque row key (resolved by lookup, never parsed). */
689
+ function rowId(providerId, modelId) {
690
+ return `${providerId}/${modelId}`;
691
+ }
692
+ /** Flatten the directory into popup rows; failure rows are listed for visibility but never selectable. */
693
+ function optionsOf(directory, t) {
694
+ const rows = [];
695
+ for (const group of directory.groups) for (const model of group.models) rows.push({
696
+ id: rowId(group.id, model.id),
697
+ label: model.name,
698
+ detail: model.description !== void 0 ? `${group.name} · ${model.description}` : group.name,
699
+ ...directory.current.provider === group.id && directory.current.model === model.id ? { active: true } : {}
700
+ });
701
+ for (const failure of directory.failures) rows.push({
702
+ id: `failure/${failure.id}`,
703
+ label: failure.name,
704
+ detail: t("option.loadError", { message: failure.message })
705
+ });
706
+ return rows;
707
+ }
708
+ /**
709
+ * Resolve a picked row back to its model selection by matching against the loaded
710
+ * groups (the same data the rows were built from — ids stay opaque).
711
+ * @param state - the session's directory snapshot.
712
+ * @param id - the picked row id.
713
+ * @returns the row's model selection, or undefined for failure rows / stale ids.
714
+ */
715
+ function selectionOf(state, id) {
716
+ for (const group of state.groups) for (const model of group.models) {
717
+ if (rowId(group.id, model.id) !== id) continue;
718
+ const reasoningEffort = state.current?.provider === group.id && state.current.model === model.id ? state.current?.reasoningEffort ?? model.reasoning?.defaultEffort : model.reasoning?.defaultEffort;
719
+ return {
720
+ provider: group.id,
721
+ model: model.id,
722
+ ...reasoningEffort === void 0 ? {} : { reasoningEffort }
723
+ };
724
+ }
725
+ }
726
+ /** Dictionary namespace owned by this plugin. */
727
+ const NS = "model";
728
+ /** Required services: the contribution registry, the seat's slot registry, locale, and the service's own faces. */
729
+ const inject = [
730
+ "commandUi",
731
+ "connection",
732
+ "locale",
733
+ "sessions",
734
+ "slots",
735
+ "remote"
736
+ ];
737
+ /**
738
+ * Client plugin body: mount ModelDirectoryResolver, register the `model` dictionaries,
739
+ * then register the /model popup contribution and the composer model seat
740
+ * over the service.
741
+ * @param ctx - client root context.
742
+ */
743
+ function apply(ctx) {
744
+ ctx.effect(() => ctx.locale.register(NS, {
745
+ zh,
746
+ en
747
+ }), "ui-model-selection: dictionaries");
748
+ const t = ctx.locale.bind(NS);
749
+ ctx.plugin(ModelDirectoryResolver, { blockReason: () => t("blocked.composer") });
750
+ ctx.inject(["commandUi", "modelDirectories"], (scope) => {
751
+ const command = scope.get("commandUi");
752
+ const models = scope.modelDirectories;
753
+ const sessions = scope.sessions;
754
+ scope.effect(() => command.register({
755
+ name: "model",
756
+ description: t("command.description"),
757
+ available: (session) => sessions.subagentAddress(session.sessionId) === void 0,
758
+ ui: {
759
+ kind: "popupSelect",
760
+ options: async (session) => {
761
+ if (sessions.subagentAddress(session.sessionId) !== void 0) throw new Error("model selection is unavailable for addressed subagent sessions");
762
+ return optionsOf(await models.directoryFor(session.sessionId).load(), t);
763
+ },
764
+ onSelect: async (option, session) => {
765
+ if (sessions.subagentAddress(session.sessionId) !== void 0) throw new Error("model selection is unavailable for addressed subagent sessions");
766
+ const directory = models.directoryFor(session.sessionId);
767
+ const selection = selectionOf(directory.store.getSnapshot(), option.id);
768
+ if (selection === void 0) throw new Error("this provider's catalog failed to load — pick a model from a loaded group");
769
+ await directory.select(selection);
770
+ }
771
+ }
772
+ }), "ui-model-selection: /model contribution");
773
+ });
774
+ ctx.inject(["slots", "modelDirectories"], (scope) => {
775
+ const models = scope.modelDirectories;
776
+ const sessions = scope.sessions;
777
+ scope.slots.inject("conversation.input.model", () => scope.slots.register({
778
+ name: "conversation.input.model",
779
+ locale: NS,
780
+ inject: (sessionId) => {
781
+ const directory = models.directoryFor(sessionId);
782
+ const available = sessions.subagentAddress(sessionId) === void 0;
783
+ return {
784
+ available,
785
+ directory: directory.store,
786
+ load: () => {
787
+ if (available) directory.load().catch(() => {});
788
+ },
789
+ select: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false)
790
+ };
791
+ }
792
+ }, ModelSelect));
793
+ });
794
+ }
795
+ //#endregion
796
+ exports.ModelDirectory = ModelDirectory;
797
+ exports.ModelDirectoryResolver = ModelDirectoryResolver;
798
+ exports.apply = apply;
799
+ exports.inject = inject;
800
+ return module.exports;
801
+ }
802
+ });
803
+
804
+ //# sourceMappingURL=client.js.map