@deepseek-ai/dsh-client-ui-settings-plugins 0.1.5-rc.2 → 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/lib/client.js CHANGED
@@ -6,28 +6,30 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
- let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
9
  let react = require("react");
10
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
11
11
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
12
12
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/fields.module.css.mjs
13
- const css$3 = ".At1oFq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.At1oFq_field+.At1oFq_field{border-top:.5px solid var(--dsw-alias-border-l2)}.At1oFq_head{align-items:center;gap:8px;display:flex}.At1oFq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.At1oFq_badges{align-items:center;gap:8px;display:inline-flex}.At1oFq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.At1oFq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.At1oFq_reset:disabled{cursor:default}.At1oFq_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.At1oFq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.At1oFq_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.At1oFq_inputInvalid{border-color:var(--dsw-alias-label-error);}.At1oFq_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.At1oFq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
14
- const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-plugins/fields.module.css";
15
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
13
+ const css$5 = ".At1oFq_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.At1oFq_field+.At1oFq_field{border-top:.5px solid var(--dsw-alias-border-l2)}.At1oFq_head{align-items:center;gap:8px;display:flex}.At1oFq_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.At1oFq_labelGroup{flex:1;align-items:center;gap:4px;min-width:0;display:flex}.At1oFq_labelGroup>.At1oFq_label{flex:0 auto}.At1oFq_helpButton{width:24px;height:24px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.At1oFq_helpButton:hover,.At1oFq_helpButton[aria-expanded=true]{background:var(--dsw-alias-bg-layer-4);color:var(--dsw-alias-label-secondary)}.At1oFq_helpButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.At1oFq_help{color:var(--dsw-alias-label-secondary);padding:10px 0 0;font-size:12px;line-height:1.6}.At1oFq_help>p{margin:0}.At1oFq_help>p+p{margin-top:8px}.At1oFq_badges{align-items:center;gap:8px;display:inline-flex}.At1oFq_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.At1oFq_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.At1oFq_reset:disabled{cursor:default}.At1oFq_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.At1oFq_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.At1oFq_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.At1oFq_input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}.At1oFq_invalid{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:1.5}.At1oFq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}";
14
+ const tagId$5 = "@deepseek-ai/dsh-client-ui-settings-plugins/fields.module.css";
15
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
16
16
  const tag = document.createElement("style");
17
17
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
18
- tag.dataset.pluginCss = tagId$3;
19
- tag.textContent = css$3;
18
+ tag.dataset.pluginCss = tagId$5;
19
+ tag.textContent = css$5;
20
20
  document.head.appendChild(tag);
21
21
  }
22
22
  var fields_module_css_default = {
23
23
  "badges": "At1oFq_badges",
24
24
  "field": "At1oFq_field",
25
25
  "head": "At1oFq_head",
26
+ "help": "At1oFq_help",
27
+ "helpButton": "At1oFq_helpButton",
26
28
  "hint": "At1oFq_hint",
27
29
  "input": "At1oFq_input",
28
- "inputInvalid": "At1oFq_inputInvalid",
29
30
  "invalid": "At1oFq_invalid",
30
31
  "label": "At1oFq_label",
32
+ "labelGroup": "At1oFq_labelGroup",
31
33
  "reset": "At1oFq_reset"
32
34
  };
33
35
  //#endregion
@@ -47,15 +49,33 @@ window.__ModuleLoader__.load({
47
49
  * @returns the labelled control.
48
50
  */
49
51
  function ValueField(props) {
52
+ const [helpOpen, setHelpOpen] = (0, react.useState)(false);
53
+ const helpId = `${props.id}-help`;
54
+ const messageId = `${props.id}-message`;
55
+ const hasMessage = props.invalid || Boolean(props.hint);
56
+ const description = [hasMessage ? messageId : "", helpOpen ? helpId : ""].filter(Boolean).join(" ");
50
57
  return (0, react_jsx_runtime.jsxs)("div", {
51
58
  className: fields_module_css_default.field,
52
59
  children: [
53
60
  (0, react_jsx_runtime.jsxs)("div", {
54
61
  className: fields_module_css_default.head,
55
- children: [(0, react_jsx_runtime.jsx)("label", {
56
- className: fields_module_css_default.label,
57
- htmlFor: props.id,
58
- children: props.label
62
+ children: [(0, react_jsx_runtime.jsxs)("div", {
63
+ className: fields_module_css_default.labelGroup,
64
+ children: [(0, react_jsx_runtime.jsx)("label", {
65
+ className: fields_module_css_default.label,
66
+ htmlFor: props.id,
67
+ children: props.label
68
+ }), props.help !== void 0 ? (0, react_jsx_runtime.jsx)("button", {
69
+ type: "button",
70
+ className: fields_module_css_default.helpButton,
71
+ "aria-label": props.help.label,
72
+ "aria-expanded": helpOpen,
73
+ "aria-controls": helpId,
74
+ onClick: () => {
75
+ setHelpOpen(!helpOpen);
76
+ },
77
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconInfoOutline14, { size: 12 })
78
+ }) : null]
59
79
  }), props.overridden ? (0, react_jsx_runtime.jsxs)("span", {
60
80
  className: fields_module_css_default.badges,
61
81
  children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
@@ -72,10 +92,11 @@ window.__ModuleLoader__.load({
72
92
  }),
73
93
  (0, react_jsx_runtime.jsx)("input", {
74
94
  id: props.id,
75
- className: props.invalid ? fields_module_css_default.inputInvalid : fields_module_css_default.input,
95
+ className: fields_module_css_default.input,
76
96
  type: "text",
77
97
  ...props.numeric === true ? { inputMode: "numeric" } : {},
78
98
  ...props.invalid ? { "aria-invalid": true } : {},
99
+ "aria-describedby": description || void 0,
79
100
  value: props.text,
80
101
  placeholder: props.placeholder ?? "",
81
102
  disabled: props.disabled,
@@ -83,10 +104,18 @@ window.__ModuleLoader__.load({
83
104
  props.onEdit(event.target.value);
84
105
  }
85
106
  }),
86
- (0, react_jsx_runtime.jsx)("p", {
107
+ hasMessage ? (0, react_jsx_runtime.jsx)("p", {
108
+ id: messageId,
87
109
  className: props.invalid ? fields_module_css_default.invalid : fields_module_css_default.hint,
88
110
  children: props.invalid ? props.invalidLabel : props.hint
89
- })
111
+ }) : null,
112
+ props.help !== void 0 && helpOpen ? (0, react_jsx_runtime.jsx)("div", {
113
+ id: helpId,
114
+ className: fields_module_css_default.help,
115
+ role: "region",
116
+ "aria-label": props.help.label,
117
+ children: props.help.content
118
+ }) : null
90
119
  ]
91
120
  });
92
121
  }
@@ -134,169 +163,94 @@ window.__ModuleLoader__.load({
134
163
  });
135
164
  }
136
165
  //#endregion
137
- //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
138
- function r(e) {
139
- var t, f, n = "";
140
- if ("string" == typeof e || "number" == typeof e) n += e;
141
- else if ("object" == typeof e) if (Array.isArray(e)) {
142
- var o = e.length;
143
- for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
144
- } else for (f in e) e[f] && (n && (n += " "), n += f);
145
- return n;
146
- }
147
- function clsx() {
148
- for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
149
- return n;
150
- }
151
- //#endregion
152
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/PluginCard.module.css.mjs
153
- const css$2 = ".YyYd_a_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);border-radius:16px;list-style:none;transition:border-color .16s,background .16s}.YyYd_a_card:hover{border-color:var(--dsw-alias-label-dimmed)}.YyYd_a_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.YyYd_a_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.YyYd_a_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.YyYd_a_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.YyYd_a_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.YyYd_a_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.YyYd_a_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.YyYd_a_chevronOpen{transform:rotate(180deg)}.YyYd_a_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.YyYd_a_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.YyYd_a_pending{flex:none}.YyYd_a_footer{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.YyYd_a_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.YyYd_a_discard,.YyYd_a_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.YyYd_a_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.YyYd_a_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.YyYd_a_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.YyYd_a_discard:disabled,.YyYd_a_save:disabled{opacity:.4;cursor:default}.YyYd_a_discard:focus-visible,.YyYd_a_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
154
- const tagId$2 = "@deepseek-ai/dsh-client-ui-settings-plugins/PluginCard.module.css";
155
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
166
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/PluginConfigForm.module.css.mjs
167
+ const css$4 = ".KQZgIq_form{flex-direction:column;display:flex}.KQZgIq_readOnly,.KQZgIq_unavailable{color:var(--dsw-alias-label-tertiary);margin:0 0 12px;font-size:12px;line-height:1.5}.KQZgIq_footer{align-items:center;gap:8px;padding-top:16px;display:flex}.KQZgIq_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.KQZgIq_save{appearance:none;font:inherit;cursor:pointer;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.KQZgIq_save:disabled{opacity:.4;cursor:default}.KQZgIq_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}";
168
+ const tagId$4 = "@deepseek-ai/dsh-client-ui-settings-plugins/PluginConfigForm.module.css";
169
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
156
170
  const tag = document.createElement("style");
157
171
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
158
- tag.dataset.pluginCss = tagId$2;
159
- tag.textContent = css$2;
172
+ tag.dataset.pluginCss = tagId$4;
173
+ tag.textContent = css$4;
160
174
  document.head.appendChild(tag);
161
175
  }
162
- var PluginCard_module_css_default = {
163
- "body": "YyYd_a_body",
164
- "card": "YyYd_a_card",
165
- "cardOpen": "YyYd_a_cardOpen",
166
- "chevron": "YyYd_a_chevron",
167
- "chevronOpen": "YyYd_a_chevronOpen",
168
- "description": "YyYd_a_description",
169
- "discard": "YyYd_a_discard",
170
- "failed": "YyYd_a_failed",
171
- "footer": "YyYd_a_footer",
172
- "headText": "YyYd_a_headText",
173
- "header": "YyYd_a_header",
174
- "name": "YyYd_a_name",
175
- "pending": "YyYd_a_pending",
176
- "readOnly": "YyYd_a_readOnly",
177
- "save": "YyYd_a_save"
176
+ var PluginConfigForm_module_css_default = {
177
+ "failed": "KQZgIq_failed",
178
+ "footer": "KQZgIq_footer",
179
+ "form": "KQZgIq_form",
180
+ "readOnly": "KQZgIq_readOnly",
181
+ "save": "KQZgIq_save",
182
+ "unavailable": "KQZgIq_unavailable"
178
183
  };
179
184
  //#endregion
180
- //#region lib/types/client/PluginCard.js
185
+ //#region lib/types/client/PluginConfigForm.js
181
186
  /**
182
- * One plugin's card: a header naming the plugin and what its settings govern,
183
- * disclosing that plugin's controls in place, with the save that writes them.
184
- *
185
- * The header is its own button rather than a shared disclosure row because a
186
- * card stacks its name over its description, while that row lays the two side
187
- * by side — the layout, not the behavior, is what differs. Disclosure is
188
- * card-local state: which card a user has open is a reading gesture, not
189
- * something the Host or the section has any stake in. Staged edits outlive
190
- * collapsing, so the header marks a card holding unsaved edits.
187
+ * One plugin's configuration form as its page on the Plugins page shows it:
188
+ * the read-only notice when the deployment stores settings read-only, the
189
+ * plugin's controls, and the save that writes every staged edit. The page
190
+ * draws the plugin's title and one-liner itself.
191
191
  *
192
- * A card renders nothing while its namespace is unavailable: a deployment that
193
- * does not compose the owning plugin should show no trace of it, rather than a
194
- * disabled card the user cannot act on.
192
+ * Only a save writes. Leaving the page drops every staged edit, so the form
193
+ * discards on unmount and offers no discard control. A form whose namespace
194
+ * the Host stopped serving says so in place of its controls rather than
195
+ * showing fields nothing would accept.
195
196
  */
196
197
  /**
197
- * Render one plugin card.
198
- * @param props - the plugin's copy keys, its form state, and its controls.
199
- * @returns the card, or nothing when the namespace is unavailable.
198
+ * Render one plugin's configuration form.
199
+ * @param props - the form state, its controls, and the save and discard actions.
200
+ * @returns the form, or the unavailable line while the namespace is not served.
200
201
  */
201
- function PluginCard(props) {
202
- const [open, setOpen] = (0, react.useState)(false);
203
- const saveStarted = (0, react.useRef)(false);
202
+ function PluginConfigForm(props) {
204
203
  const { state } = props;
205
- (0, react.useEffect)(() => {
206
- if (state.saving) {
207
- saveStarted.current = true;
208
- return;
209
- }
210
- if (!saveStarted.current) return;
211
- saveStarted.current = false;
212
- if (!state.dirty && !state.failed) setOpen(false);
213
- }, [
214
- state.dirty,
215
- state.failed,
216
- state.saving
217
- ]);
218
- if (!state.available) return null;
219
- const title = props.t(props.titleKey);
204
+ const discard = (0, react.useRef)(props.onDiscard);
205
+ discard.current = props.onDiscard;
206
+ (0, react.useEffect)(() => () => {
207
+ discard.current();
208
+ }, []);
209
+ if (!state.available) return (0, react_jsx_runtime.jsx)("p", {
210
+ className: PluginConfigForm_module_css_default.unavailable,
211
+ role: "status",
212
+ children: props.t("unavailable")
213
+ });
220
214
  const blocked = !state.dirty || state.invalid || state.saving;
221
- return (0, react_jsx_runtime.jsxs)("li", {
222
- className: clsx(PluginCard_module_css_default.card, open && PluginCard_module_css_default.cardOpen),
223
- children: [(0, react_jsx_runtime.jsxs)("button", {
224
- type: "button",
225
- className: PluginCard_module_css_default.header,
226
- "aria-expanded": open,
227
- "aria-label": `${props.t(open ? "collapse" : "expand")}: ${title}`,
228
- onClick: () => {
229
- setOpen(!open);
230
- },
231
- children: [
232
- (0, react_jsx_runtime.jsxs)("span", {
233
- className: PluginCard_module_css_default.headText,
234
- children: [(0, react_jsx_runtime.jsx)("span", {
235
- className: PluginCard_module_css_default.name,
236
- children: title
237
- }), (0, react_jsx_runtime.jsx)("span", {
238
- className: PluginCard_module_css_default.description,
239
- children: props.t(props.descriptionKey)
240
- })]
241
- }),
242
- state.dirty ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
243
- tone: "neutral",
244
- className: PluginCard_module_css_default.pending,
245
- children: props.t("unsaved")
246
- }) : null,
247
- (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: clsx(PluginCard_module_css_default.chevron, open && PluginCard_module_css_default.chevronOpen) })
248
- ]
249
- }), open ? (0, react_jsx_runtime.jsxs)("div", {
250
- className: PluginCard_module_css_default.body,
251
- children: [
252
- !state.writable ? (0, react_jsx_runtime.jsx)("p", {
253
- className: PluginCard_module_css_default.readOnly,
215
+ return (0, react_jsx_runtime.jsxs)("div", {
216
+ className: PluginConfigForm_module_css_default.form,
217
+ children: [
218
+ !state.writable ? (0, react_jsx_runtime.jsx)("p", {
219
+ className: PluginConfigForm_module_css_default.readOnly,
220
+ role: "status",
221
+ children: props.t("readOnly")
222
+ }) : null,
223
+ props.children,
224
+ (0, react_jsx_runtime.jsxs)("div", {
225
+ className: PluginConfigForm_module_css_default.footer,
226
+ children: [state.failed ? (0, react_jsx_runtime.jsx)("p", {
227
+ className: PluginConfigForm_module_css_default.failed,
254
228
  role: "status",
255
- children: props.t("readOnly")
256
- }) : null,
257
- props.children,
258
- (0, react_jsx_runtime.jsxs)("div", {
259
- className: PluginCard_module_css_default.footer,
260
- children: [
261
- state.failed ? (0, react_jsx_runtime.jsx)("p", {
262
- className: PluginCard_module_css_default.failed,
263
- role: "status",
264
- children: props.t("saveFailed")
265
- }) : null,
266
- (0, react_jsx_runtime.jsx)("button", {
267
- type: "button",
268
- className: PluginCard_module_css_default.discard,
269
- disabled: !state.dirty || state.saving,
270
- onClick: props.onDiscard,
271
- children: props.t("discard")
272
- }),
273
- (0, react_jsx_runtime.jsx)("button", {
274
- type: "button",
275
- className: PluginCard_module_css_default.save,
276
- disabled: blocked,
277
- onClick: props.onSave,
278
- children: props.t(state.saving ? "saving" : "save")
279
- })
280
- ]
281
- })
282
- ]
283
- }) : null]
229
+ children: props.t("saveFailed")
230
+ }) : null, (0, react_jsx_runtime.jsx)("button", {
231
+ type: "button",
232
+ className: PluginConfigForm_module_css_default.save,
233
+ disabled: blocked,
234
+ onClick: props.onSave,
235
+ children: props.t(state.saving ? "saving" : "save")
236
+ })]
237
+ })
238
+ ]
284
239
  });
285
240
  }
286
241
  //#endregion
287
242
  //#region lib/types/client/AgentLoopCard.js
288
243
  /**
289
- * Render the agent-loop card.
290
- * @param props - locale copy, the card snapshot, and its form actions.
291
- * @returns the card.
244
+ * Render the agent loop's one-liner or its configuration form, as the Plugins page asks.
245
+ * @param props - the view asked for, locale copy, the form snapshot, and its actions.
246
+ * @returns the one-liner, or the form.
292
247
  */
293
248
  function AgentLoopCard(props) {
294
249
  const { t } = props;
295
250
  const state = props.useAgentLoopCard((snapshot) => snapshot);
296
- return (0, react_jsx_runtime.jsx)(PluginCard, {
251
+ if (props.view === "summary") return t("agentLoopDescription");
252
+ return (0, react_jsx_runtime.jsx)(PluginConfigForm, {
297
253
  t,
298
- titleKey: "agentLoopTitle",
299
- descriptionKey: "agentLoopDescription",
300
254
  state,
301
255
  onSave: props.save,
302
256
  onDiscard: props.discard,
@@ -322,18 +276,17 @@ window.__ModuleLoader__.load({
322
276
  //#endregion
323
277
  //#region lib/types/client/BashCard.js
324
278
  /**
325
- * Render the shell card.
326
- * @param props - locale copy, the card snapshot, and its form actions.
327
- * @returns the card.
279
+ * Render the shell plugin's one-liner or its configuration form, as the Plugins page asks.
280
+ * @param props - the view asked for, locale copy, the form snapshot, and its actions.
281
+ * @returns the one-liner, or the form.
328
282
  */
329
283
  function BashCard(props) {
330
284
  const { t } = props;
331
285
  const state = props.useBashCard((snapshot) => snapshot);
286
+ if (props.view === "summary") return t("bashDescription");
332
287
  const disabled = !state.writable;
333
- return (0, react_jsx_runtime.jsxs)(PluginCard, {
288
+ return (0, react_jsx_runtime.jsxs)(PluginConfigForm, {
334
289
  t,
335
- titleKey: "bashTitle",
336
- descriptionKey: "bashDescription",
337
290
  state,
338
291
  onSave: props.save,
339
292
  onDiscard: props.discard,
@@ -374,56 +327,32 @@ window.__ModuleLoader__.load({
374
327
  }
375
328
  //#endregion
376
329
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/PluginsSettingsSection.module.css.mjs
377
- const css$1 = ".pbvGtq_section{max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.pbvGtq_heading{margin:0;font-size:18px;font-weight:600}.pbvGtq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.pbvGtq_tabs{border-bottom:.5px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:22px;margin-top:2px;display:flex}.pbvGtq_tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}.pbvGtq_tab:hover,.pbvGtq_tab[data-active=true]{color:var(--dsw-alias-label-primary)}.pbvGtq_tab[data-active=true]:after,.pbvGtq_tab:focus-visible:after{background:var(--dsw-alias-label-primary);content:\"\";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.pbvGtq_tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}.pbvGtq_panel{min-width:0;padding-top:2px}.pbvGtq_cards{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pbvGtq_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}";
378
- const tagId$1 = "@deepseek-ai/dsh-client-ui-settings-plugins/PluginsSettingsSection.module.css";
379
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
330
+ const css$3 = ".pbvGtq_section{max-width:760px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.pbvGtq_heading{margin:0;font-size:18px;font-weight:600}.pbvGtq_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.pbvGtq_tabs{border-bottom:.5px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:22px;margin-top:2px;display:flex}.pbvGtq_tab{color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}.pbvGtq_tab:hover,.pbvGtq_tab[data-active=true]{color:var(--dsw-alias-label-primary)}.pbvGtq_tab[data-active=true]:after,.pbvGtq_tab:focus-visible:after{background:var(--dsw-alias-label-primary);content:\"\";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.pbvGtq_tab:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px;color:var(--dsw-alias-label-primary);border-radius:2px}.pbvGtq_panel{min-width:0;padding-top:2px}.pbvGtq_cards{flex-direction:column;gap:10px;margin:0;padding:0;list-style:none;display:flex}.pbvGtq_empty{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px}.pbvGtq_configurable{flex-direction:column;gap:10px;display:flex}.pbvGtq_presetSettings{flex-direction:column;gap:8px;display:flex}.pbvGtq_presetSettingsTitle{margin:0;font-size:15px;font-weight:600;line-height:22px}";
331
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-plugins/PluginsSettingsSection.module.css";
332
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
380
333
  const tag = document.createElement("style");
381
334
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
382
- tag.dataset.pluginCss = tagId$1;
383
- tag.textContent = css$1;
335
+ tag.dataset.pluginCss = tagId$3;
336
+ tag.textContent = css$3;
384
337
  document.head.appendChild(tag);
385
338
  }
386
339
  var PluginsSettingsSection_module_css_default = {
387
340
  "cards": "pbvGtq_cards",
341
+ "configurable": "pbvGtq_configurable",
388
342
  "empty": "pbvGtq_empty",
389
343
  "heading": "pbvGtq_heading",
390
344
  "intro": "pbvGtq_intro",
391
345
  "panel": "pbvGtq_panel",
346
+ "presetSettings": "pbvGtq_presetSettings",
347
+ "presetSettingsTitle": "pbvGtq_presetSettingsTitle",
392
348
  "section": "pbvGtq_section",
393
349
  "tab": "pbvGtq_tab",
394
350
  "tabs": "pbvGtq_tabs"
395
351
  };
396
352
  //#endregion
397
- //#region lib/types/client/ConfigurablePluginsTab.js
398
- /**
399
- * Configurable Host plugins contributed to the shared Plugins section.
400
- *
401
- * The tab enumerates settings namespaces but never interprets one — a card
402
- * arrives through `settings.plugin.item` keyed by the namespace it edits, so a
403
- * plugin that ships a browser half owns its own card and this tab only decides
404
- * which keys to dispatch.
405
- */
406
- /**
407
- * Render cards registered by plugins that expose editable settings.
408
- * @param props - locale copy, slot rendering, and the namespaces to dispatch.
409
- * @returns the card list, or the empty line once the Host has answered.
410
- */
411
- function ConfigurablePluginsTab(props) {
412
- const { t, renderSlot } = props;
413
- const { loaded, namespaces } = props.useConfigurablePlugins((snapshot) => snapshot);
414
- if (namespaces.length > 0) return (0, react_jsx_runtime.jsx)("ul", {
415
- className: PluginsSettingsSection_module_css_default.cards,
416
- children: namespaces.map((ns) => (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderSlot("settings.plugin.item", {}, { entryKey: ns }) }, ns))
417
- });
418
- return loaded ? (0, react_jsx_runtime.jsx)("p", {
419
- className: PluginsSettingsSection_module_css_default.empty,
420
- children: t("empty")
421
- }) : null;
422
- }
423
- //#endregion
424
353
  //#region lib/types/client/PluginsSettingsSection.js
425
354
  /** Plugins settings section: localized tabs around feature-owned pages. */
426
- /** Render one Plugins page whose contents arrive from feature-owned tabs. */
355
+ /** Render one Plugins page whose contents arrive from feature-owned tabs; one contribution shows as the page itself. */
427
356
  function PluginsSettingsSection({ t, renderSlot, useTabs }) {
428
357
  const tabsId = (0, react.useId)();
429
358
  const tabRefs = (0, react.useRef)([]);
@@ -431,6 +360,7 @@ window.__ModuleLoader__.load({
431
360
  const [activeId, setActiveId] = (0, react.useState)();
432
361
  const [visitedIds, setVisitedIds] = (0, react.useState)(() => /* @__PURE__ */ new Set());
433
362
  const active = rows.find((row) => row.id === activeId)?.id ?? rows[0]?.id;
363
+ const single = rows.length === 1 ? rows[0] : void 0;
434
364
  (0, react.useEffect)(() => {
435
365
  if (active === void 0) return;
436
366
  setVisitedIds((previous) => {
@@ -452,6 +382,9 @@ window.__ModuleLoader__.load({
452
382
  rows.length === 0 ? (0, react_jsx_runtime.jsx)("p", {
453
383
  className: PluginsSettingsSection_module_css_default.empty,
454
384
  children: t("empty")
385
+ }) : single !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
386
+ className: PluginsSettingsSection_module_css_default.panel,
387
+ children: renderSlot("settings.plugins.tab", {}, { only: single.id })
455
388
  }) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("div", {
456
389
  className: PluginsSettingsSection_module_css_default.tabs,
457
390
  role: "tablist",
@@ -514,45 +447,132 @@ window.__ModuleLoader__.load({
514
447
  });
515
448
  }
516
449
  //#endregion
517
- //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/SubagentModelSelectionCard.module.css.mjs
518
- const css = ".vCGm7G_permission{gap:6px;padding:12px 0;display:grid}.vCGm7G_toggleRow{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}.vCGm7G_toggleLabel{flex:1;min-width:0}.vCGm7G_selection{gap:10px;display:grid}.vCGm7G_hint,.vCGm7G_notice,.vCGm7G_invalid,.vCGm7G_conflict{margin:0;font-size:12px;line-height:1.5}.vCGm7G_hint,.vCGm7G_notice{color:var(--dsw-alias-label-tertiary)}.vCGm7G_invalid,.vCGm7G_conflict{color:var(--dsw-alias-label-error)}.vCGm7G_catalogError{color:var(--dsw-alias-label-error);justify-content:space-between;align-items:center;gap:12px;font-size:12px;display:flex}.vCGm7G_catalogError button{color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border:0;padding:0}.vCGm7G_models{border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;gap:6px;min-width:0;max-height:280px;margin:0;padding:10px;display:grid;overflow:auto}.vCGm7G_models legend{color:var(--dsw-alias-label-secondary);padding:0 4px;font-size:12px}.vCGm7G_modelGroup{gap:6px;display:grid}.vCGm7G_modelGroup+.vCGm7G_modelGroup{border-top:.5px solid var(--dsw-alias-border-l3);margin-top:4px;padding-top:10px}.vCGm7G_providerName{color:var(--dsw-alias-label-tertiary);padding:0 6px;font-size:11px;font-weight:500}.vCGm7G_model{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;padding:6px;display:grid}.vCGm7G_model:hover{background:var(--dsw-alias-bg-layer-4)}.vCGm7G_modelName,.vCGm7G_route{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.vCGm7G_modelName{color:var(--dsw-alias-label-primary);font-size:13px}.vCGm7G_route{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-size:11px}.vCGm7G_unavailable{color:var(--dsw-alias-label-tertiary);font-size:11px}";
519
- const tagId = "@deepseek-ai/dsh-client-ui-settings-plugins/SubagentModelSelectionCard.module.css";
520
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
450
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/SubagentLimitsFields.module.css.mjs
451
+ const css$2 = ".uedTFq_limits{grid-template-columns:repeat(auto-fit,minmax(min(220px,100%),1fr));gap:16px;display:grid}.uedTFq_limit{min-width:0}.uedTFq_limit input{font-variant-numeric:tabular-nums;min-width:0}.uedTFq_depthTable{border-collapse:collapse;border-block:.5px solid var(--dsw-alias-border-l2);width:100%;font:inherit;text-align:left;margin:8px 0}.uedTFq_depthTable th,.uedTFq_depthTable td{vertical-align:top;padding:6px 0}.uedTFq_depthTable th{font-variant-numeric:tabular-nums;width:24px;color:var(--dsw-alias-label-primary);padding-right:8px;font-weight:500}.uedTFq_depthTable tr+tr{border-top:.5px solid var(--dsw-alias-border-l2)}";
452
+ const tagId$2 = "@deepseek-ai/dsh-client-ui-settings-plugins/SubagentLimitsFields.module.css";
453
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
521
454
  const tag = document.createElement("style");
522
455
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
523
- tag.dataset.pluginCss = tagId;
524
- tag.textContent = css;
456
+ tag.dataset.pluginCss = tagId$2;
457
+ tag.textContent = css$2;
458
+ document.head.appendChild(tag);
459
+ }
460
+ var SubagentLimitsFields_module_css_default = {
461
+ "depthTable": "uedTFq_depthTable",
462
+ "limit": "uedTFq_limit",
463
+ "limits": "uedTFq_limits"
464
+ };
465
+ //#endregion
466
+ //#region lib/types/client/SubagentLimitsFields.js
467
+ /**
468
+ * Render the depth and capacity fields with their original validation and reset behavior.
469
+ * @param props - Locale, staged fields, and edit callbacks.
470
+ * @returns Two responsive fields and their application rules.
471
+ */
472
+ function SubagentLimitsFields(props) {
473
+ const { t, state } = props;
474
+ return (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: (0, react_jsx_runtime.jsxs)("div", {
475
+ className: SubagentLimitsFields_module_css_default.limits,
476
+ children: [(0, react_jsx_runtime.jsx)("div", {
477
+ className: SubagentLimitsFields_module_css_default.limit,
478
+ children: (0, react_jsx_runtime.jsx)(ValueField, {
479
+ id: "plugin-config-subagent-depth",
480
+ label: t("subagentMaxDepth"),
481
+ help: {
482
+ label: t("subagentDepthHelpLabel"),
483
+ content: (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
484
+ (0, react_jsx_runtime.jsx)("p", { children: t("subagentDepthHelp") }),
485
+ (0, react_jsx_runtime.jsx)("table", {
486
+ className: SubagentLimitsFields_module_css_default.depthTable,
487
+ "aria-label": t("subagentDepthHelpLabel"),
488
+ children: (0, react_jsx_runtime.jsxs)("tbody", { children: [(0, react_jsx_runtime.jsxs)("tr", { children: [(0, react_jsx_runtime.jsx)("th", {
489
+ scope: "row",
490
+ children: 0
491
+ }), (0, react_jsx_runtime.jsx)("td", { children: t("subagentDepthZero") })] }), (0, react_jsx_runtime.jsxs)("tr", { children: [(0, react_jsx_runtime.jsx)("th", {
492
+ scope: "row",
493
+ children: 1
494
+ }), (0, react_jsx_runtime.jsx)("td", { children: t("subagentDepthOne") })] })] })
495
+ }),
496
+ (0, react_jsx_runtime.jsx)("p", { children: t("subagentDepthOverride") })
497
+ ] })
498
+ },
499
+ overriddenLabel: t("overridden"),
500
+ resetLabel: t("reset"),
501
+ invalidLabel: t("subagentDepthInvalid"),
502
+ numeric: true,
503
+ disabled: !state.writable || state.saving,
504
+ ...state.maxDepth,
505
+ onEdit: (text) => {
506
+ props.edit("maxDepth", text);
507
+ },
508
+ onReset: () => {
509
+ props.resetField("maxDepth");
510
+ }
511
+ })
512
+ }), (0, react_jsx_runtime.jsx)("div", {
513
+ className: SubagentLimitsFields_module_css_default.limit,
514
+ children: (0, react_jsx_runtime.jsx)(ValueField, {
515
+ id: "plugin-config-subagent-capacity",
516
+ label: t("subagentMaxActive"),
517
+ help: {
518
+ label: t("subagentCapacityHelpLabel"),
519
+ content: (0, react_jsx_runtime.jsx)("p", { children: t("subagentCapacityHelp") })
520
+ },
521
+ overriddenLabel: t("overridden"),
522
+ resetLabel: t("reset"),
523
+ invalidLabel: t("subagentCapacityInvalid"),
524
+ numeric: true,
525
+ disabled: !state.writable || state.saving,
526
+ ...state.maxActiveSubagents,
527
+ onEdit: (text) => {
528
+ props.edit("maxActiveSubagents", text);
529
+ },
530
+ onReset: () => {
531
+ props.resetField("maxActiveSubagents");
532
+ }
533
+ })
534
+ })]
535
+ }) });
536
+ }
537
+ //#endregion
538
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/SubagentModelSelectionFields.module.css.mjs
539
+ const css$1 = "._2QGtya_permission{gap:6px;padding:12px 0;display:grid}._2QGtya_toggleRow{color:var(--dsw-alias-label-primary);justify-content:space-between;align-items:flex-start;gap:16px;font-size:13px;line-height:1.5;display:flex}._2QGtya_toggleLabel{flex:1;min-width:0}._2QGtya_selection{gap:10px;display:grid}._2QGtya_hint,._2QGtya_notice,._2QGtya_invalid,._2QGtya_conflict{margin:0;font-size:12px;line-height:1.5}._2QGtya_hint,._2QGtya_notice{color:var(--dsw-alias-label-tertiary)}._2QGtya_invalid,._2QGtya_conflict{color:var(--dsw-alias-state-error-primary)}._2QGtya_catalogError{color:var(--dsw-alias-state-error-primary);justify-content:space-between;align-items:center;gap:12px;font-size:12px;display:flex}._2QGtya_catalogError button{color:var(--dsw-alias-brand-primary);cursor:pointer;background:0 0;border:0;padding:0}._2QGtya_models{border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;gap:6px;min-width:0;max-height:280px;margin:0;padding:10px;display:grid;overflow:auto}._2QGtya_models legend{color:var(--dsw-alias-label-secondary);padding:0 4px;font-size:12px}._2QGtya_modelGroup{gap:6px;display:grid}._2QGtya_modelGroup+._2QGtya_modelGroup{border-top:.5px solid var(--dsw-alias-border-l3);margin-top:4px;padding-top:10px}._2QGtya_providerName{color:var(--dsw-alias-label-tertiary);padding:0 6px;font-size:11px;font-weight:500}._2QGtya_model{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;padding:6px;display:grid}._2QGtya_model:hover{background:var(--dsw-alias-bg-layer-4)}._2QGtya_modelName,._2QGtya_route{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}._2QGtya_modelName{color:var(--dsw-alias-label-primary);font-size:13px}._2QGtya_route{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-size:11px}._2QGtya_unavailable{color:var(--dsw-alias-label-tertiary);font-size:11px}";
540
+ const tagId$1 = "@deepseek-ai/dsh-client-ui-settings-plugins/SubagentModelSelectionFields.module.css";
541
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
542
+ const tag = document.createElement("style");
543
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
544
+ tag.dataset.pluginCss = tagId$1;
545
+ tag.textContent = css$1;
525
546
  document.head.appendChild(tag);
526
547
  }
527
- var SubagentModelSelectionCard_module_css_default = {
528
- "catalogError": "vCGm7G_catalogError",
529
- "conflict": "vCGm7G_conflict",
530
- "hint": "vCGm7G_hint",
531
- "invalid": "vCGm7G_invalid",
532
- "model": "vCGm7G_model",
533
- "modelGroup": "vCGm7G_modelGroup",
534
- "modelName": "vCGm7G_modelName",
535
- "models": "vCGm7G_models",
536
- "notice": "vCGm7G_notice",
537
- "permission": "vCGm7G_permission",
538
- "providerName": "vCGm7G_providerName",
539
- "route": "vCGm7G_route",
540
- "selection": "vCGm7G_selection",
541
- "toggleLabel": "vCGm7G_toggleLabel",
542
- "toggleRow": "vCGm7G_toggleRow",
543
- "unavailable": "vCGm7G_unavailable"
548
+ var SubagentModelSelectionFields_module_css_default = {
549
+ "catalogError": "_2QGtya_catalogError",
550
+ "conflict": "_2QGtya_conflict",
551
+ "hint": "_2QGtya_hint",
552
+ "invalid": "_2QGtya_invalid",
553
+ "model": "_2QGtya_model",
554
+ "modelGroup": "_2QGtya_modelGroup",
555
+ "modelName": "_2QGtya_modelName",
556
+ "models": "_2QGtya_models",
557
+ "notice": "_2QGtya_notice",
558
+ "permission": "_2QGtya_permission",
559
+ "providerName": "_2QGtya_providerName",
560
+ "route": "_2QGtya_route",
561
+ "selection": "_2QGtya_selection",
562
+ "toggleLabel": "_2QGtya_toggleLabel",
563
+ "toggleRow": "_2QGtya_toggleRow",
564
+ "unavailable": "_2QGtya_unavailable"
544
565
  };
545
566
  //#endregion
546
- //#region lib/types/client/SubagentModelSelectionCard.js
567
+ //#region lib/types/client/SubagentModelSelectionFields.js
547
568
  /** User control for model-selectable subagent delegation in new sessions. */
548
569
  /**
549
570
  * Render the default-off preference and its exact adapter-route choices.
550
571
  * @param props - locale copy, the card snapshot, and its toggle action.
551
- * @returns the preference card, or nothing when the namespace is unavailable.
572
+ * @returns the model permission and route choices inside the shared card.
552
573
  */
553
- function SubagentModelSelectionCard(props) {
554
- const { t } = props;
555
- const state = props.useSubagentModelSelectionCard((snapshot) => snapshot);
574
+ function SubagentModelSelectionFields(props) {
575
+ const { t, state } = props;
556
576
  const availableGroups = /* @__PURE__ */ new Map();
557
577
  const unavailable = [];
558
578
  for (const candidate of state.candidates) {
@@ -568,7 +588,7 @@ window.__ModuleLoader__.load({
568
588
  else group.candidates.push(candidate);
569
589
  }
570
590
  const renderCandidate = (candidate) => (0, react_jsx_runtime.jsxs)("label", {
571
- className: SubagentModelSelectionCard_module_css_default.model,
591
+ className: SubagentModelSelectionFields_module_css_default.model,
572
592
  children: [
573
593
  (0, react_jsx_runtime.jsx)("input", {
574
594
  type: "checkbox",
@@ -579,168 +599,216 @@ window.__ModuleLoader__.load({
579
599
  }
580
600
  }),
581
601
  (0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
582
- className: SubagentModelSelectionCard_module_css_default.modelName,
602
+ className: SubagentModelSelectionFields_module_css_default.modelName,
583
603
  children: candidate.modelName
584
604
  }), (0, react_jsx_runtime.jsx)("span", {
585
- className: SubagentModelSelectionCard_module_css_default.route,
605
+ className: SubagentModelSelectionFields_module_css_default.route,
586
606
  children: `${candidate.providerName} · ${candidate.provider}/${candidate.model}`
587
607
  })] }),
588
608
  !candidate.available ? (0, react_jsx_runtime.jsx)("span", {
589
- className: SubagentModelSelectionCard_module_css_default.unavailable,
609
+ className: SubagentModelSelectionFields_module_css_default.unavailable,
590
610
  children: t("subagentModelSelectionUnavailable")
591
611
  }) : null
592
612
  ]
593
613
  }, candidate.key);
594
- return (0, react_jsx_runtime.jsxs)(PluginCard, {
595
- t,
596
- titleKey: "subagentModelSelectionTitle",
597
- descriptionKey: "subagentModelSelectionDescription",
598
- state,
599
- onSave: props.save,
600
- onDiscard: props.discard,
601
- children: [
602
- (0, react_jsx_runtime.jsxs)("div", {
603
- className: SubagentModelSelectionCard_module_css_default.permission,
604
- children: [(0, react_jsx_runtime.jsxs)("div", {
605
- className: SubagentModelSelectionCard_module_css_default.toggleRow,
606
- children: [(0, react_jsx_runtime.jsx)("span", {
607
- className: SubagentModelSelectionCard_module_css_default.toggleLabel,
608
- children: t("subagentModelSelectionToggle")
609
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
610
- checked: state.enabled,
611
- label: t("subagentModelSelectionToggle"),
612
- disabled: !state.writable || state.saving,
613
- onChange: props.toggleEnabled
614
- })]
615
- }), (0, react_jsx_runtime.jsx)("p", {
616
- className: SubagentModelSelectionCard_module_css_default.hint,
617
- children: t(state.enabled ? "subagentModelSelectionChoose" : "subagentModelSelectionOff")
614
+ return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
615
+ (0, react_jsx_runtime.jsxs)("div", {
616
+ className: SubagentModelSelectionFields_module_css_default.permission,
617
+ children: [(0, react_jsx_runtime.jsxs)("div", {
618
+ className: SubagentModelSelectionFields_module_css_default.toggleRow,
619
+ children: [(0, react_jsx_runtime.jsx)("span", {
620
+ className: SubagentModelSelectionFields_module_css_default.toggleLabel,
621
+ children: t("subagentModelSelectionToggle")
622
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
623
+ checked: state.enabled,
624
+ label: t("subagentModelSelectionToggle"),
625
+ disabled: !state.writable || state.saving,
626
+ onChange: props.toggleEnabled
618
627
  })]
619
- }),
620
- state.enabled ? (0, react_jsx_runtime.jsxs)("div", {
621
- className: SubagentModelSelectionCard_module_css_default.selection,
622
- children: [
623
- state.catalogStatus === "loading" ? (0, react_jsx_runtime.jsx)("p", {
624
- className: SubagentModelSelectionCard_module_css_default.notice,
625
- role: "status",
626
- children: t("subagentModelSelectionLoading")
627
- }) : null,
628
- state.catalogStatus === "error" ? (0, react_jsx_runtime.jsxs)("div", {
629
- className: SubagentModelSelectionCard_module_css_default.catalogError,
630
- role: "alert",
631
- children: [(0, react_jsx_runtime.jsx)("span", { children: t("subagentModelSelectionLoadFailed") }), (0, react_jsx_runtime.jsx)("button", {
632
- type: "button",
633
- disabled: state.saving,
634
- onClick: props.retryCatalog,
635
- children: t("subagentModelSelectionRetry")
636
- })]
637
- }) : null,
638
- state.catalogPartial ? (0, react_jsx_runtime.jsx)("p", {
639
- className: SubagentModelSelectionCard_module_css_default.notice,
640
- children: t("subagentModelSelectionPartial")
641
- }) : null,
642
- state.candidates.length > 0 ? (0, react_jsx_runtime.jsxs)("fieldset", {
643
- className: SubagentModelSelectionCard_module_css_default.models,
644
- children: [
645
- (0, react_jsx_runtime.jsx)("legend", { children: t("subagentModelSelectionAllowed") }),
646
- [...availableGroups].map(([provider, group]) => (0, react_jsx_runtime.jsxs)("div", {
647
- className: SubagentModelSelectionCard_module_css_default.modelGroup,
648
- children: [(0, react_jsx_runtime.jsx)("div", {
649
- className: SubagentModelSelectionCard_module_css_default.providerName,
650
- children: group.providerName
651
- }), group.candidates.map(renderCandidate)]
652
- }, provider)),
653
- unavailable.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
654
- className: SubagentModelSelectionCard_module_css_default.modelGroup,
655
- children: [(0, react_jsx_runtime.jsx)("div", {
656
- className: SubagentModelSelectionCard_module_css_default.providerName,
657
- children: t("subagentModelSelectionUnavailableGroup")
658
- }), unavailable.map(renderCandidate)]
659
- }) : null
660
- ]
661
- }) : state.catalogStatus === "ready" ? (0, react_jsx_runtime.jsx)("p", {
662
- className: SubagentModelSelectionCard_module_css_default.notice,
663
- children: t("subagentModelSelectionEmpty")
664
- }) : null,
665
- state.invalid ? (0, react_jsx_runtime.jsx)("p", {
666
- className: SubagentModelSelectionCard_module_css_default.invalid,
667
- children: t("subagentModelSelectionRequired")
668
- }) : null
669
- ]
670
- }) : null,
671
- state.conflicted ? (0, react_jsx_runtime.jsx)("p", {
672
- className: SubagentModelSelectionCard_module_css_default.conflict,
673
- role: "status",
674
- children: t("subagentModelSelectionConflict")
675
- }) : null
676
- ]
677
- });
628
+ }), (0, react_jsx_runtime.jsx)("p", {
629
+ className: SubagentModelSelectionFields_module_css_default.hint,
630
+ children: t(state.enabled ? "subagentModelSelectionChoose" : "subagentModelSelectionOff")
631
+ })]
632
+ }),
633
+ state.enabled ? (0, react_jsx_runtime.jsxs)("div", {
634
+ className: SubagentModelSelectionFields_module_css_default.selection,
635
+ children: [
636
+ state.catalogStatus === "loading" ? (0, react_jsx_runtime.jsx)("p", {
637
+ className: SubagentModelSelectionFields_module_css_default.notice,
638
+ role: "status",
639
+ children: t("subagentModelSelectionLoading")
640
+ }) : null,
641
+ state.catalogStatus === "error" ? (0, react_jsx_runtime.jsxs)("div", {
642
+ className: SubagentModelSelectionFields_module_css_default.catalogError,
643
+ role: "alert",
644
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("subagentModelSelectionLoadFailed") }), (0, react_jsx_runtime.jsx)("button", {
645
+ type: "button",
646
+ disabled: state.saving,
647
+ onClick: props.retryCatalog,
648
+ children: t("subagentModelSelectionRetry")
649
+ })]
650
+ }) : null,
651
+ state.catalogPartial ? (0, react_jsx_runtime.jsx)("p", {
652
+ className: SubagentModelSelectionFields_module_css_default.notice,
653
+ children: t("subagentModelSelectionPartial")
654
+ }) : null,
655
+ state.candidates.length > 0 ? (0, react_jsx_runtime.jsxs)("fieldset", {
656
+ className: SubagentModelSelectionFields_module_css_default.models,
657
+ children: [
658
+ (0, react_jsx_runtime.jsx)("legend", { children: t("subagentModelSelectionAllowed") }),
659
+ [...availableGroups].map(([provider, group]) => (0, react_jsx_runtime.jsxs)("div", {
660
+ className: SubagentModelSelectionFields_module_css_default.modelGroup,
661
+ children: [(0, react_jsx_runtime.jsx)("div", {
662
+ className: SubagentModelSelectionFields_module_css_default.providerName,
663
+ children: group.providerName
664
+ }), group.candidates.map(renderCandidate)]
665
+ }, provider)),
666
+ unavailable.length > 0 ? (0, react_jsx_runtime.jsxs)("div", {
667
+ className: SubagentModelSelectionFields_module_css_default.modelGroup,
668
+ children: [(0, react_jsx_runtime.jsx)("div", {
669
+ className: SubagentModelSelectionFields_module_css_default.providerName,
670
+ children: t("subagentModelSelectionUnavailableGroup")
671
+ }), unavailable.map(renderCandidate)]
672
+ }) : null
673
+ ]
674
+ }) : state.catalogStatus === "ready" ? (0, react_jsx_runtime.jsx)("p", {
675
+ className: SubagentModelSelectionFields_module_css_default.notice,
676
+ children: t("subagentModelSelectionEmpty")
677
+ }) : null,
678
+ state.invalid ? (0, react_jsx_runtime.jsx)("p", {
679
+ className: SubagentModelSelectionFields_module_css_default.invalid,
680
+ children: t("subagentModelSelectionRequired")
681
+ }) : null
682
+ ]
683
+ }) : null,
684
+ state.conflicted ? (0, react_jsx_runtime.jsx)("p", {
685
+ className: SubagentModelSelectionFields_module_css_default.conflict,
686
+ role: "status",
687
+ children: t("subagentModelSelectionConflict")
688
+ }) : null
689
+ ] });
678
690
  }
679
691
  //#endregion
680
- //#region lib/types/client/WebSearchCard.js
692
+ //#region lib/types/client/subagent-card-controller.js
693
+ /** Shared presentation and actions for the two Host-owned Subagent settings sections. */
681
694
  /**
682
- * Render the web-search card.
683
- * @param props - locale copy, the card snapshot, and its form actions.
684
- * @returns the card.
695
+ * Derive the shared card state without duplicating either form's subscriptions.
696
+ * @param limits - Current delegation-limit form.
697
+ * @param models - Current model-authorization form.
698
+ * @returns Availability and settlement across the sections this Host serves.
685
699
  */
686
- function WebSearchCard(props) {
700
+ function subagentCardShell(limits, models) {
701
+ const sections = [limits, models].filter((section) => section.available);
702
+ return {
703
+ available: sections.length > 0,
704
+ writable: sections.every((section) => section.writable),
705
+ dirty: sections.some((section) => section.dirty),
706
+ invalid: sections.some((section) => section.invalid) || models.available && models.dirty && models.conflicted,
707
+ saving: sections.some((section) => section.saving),
708
+ failed: sections.some((section) => section.failed)
709
+ };
710
+ }
711
+ /**
712
+ * Compose one card from the existing forms; each write retains its namespace revision fence.
713
+ * @param limits - Limit form source and actions.
714
+ * @param models - Model form source and actions.
715
+ * @returns Framework-bound sources and shared save/discard actions.
716
+ */
717
+ function subagentCardFace(limits, models) {
718
+ return {
719
+ hooks: {
720
+ ...limits.hooks,
721
+ ...models.hooks
722
+ },
723
+ editLimit: limits.edit,
724
+ resetLimit: limits.resetField,
725
+ toggleEnabled: models.toggleEnabled,
726
+ toggleModel: models.toggleModel,
727
+ retryCatalog: models.retryCatalog,
728
+ save: () => {
729
+ const limitState = limits.hooks.subagentLimitsCard.getSnapshot();
730
+ const modelState = models.hooks.subagentModelSelectionCard.getSnapshot();
731
+ const state = subagentCardShell(limitState, modelState);
732
+ if (!state.available || !state.writable || !state.dirty || state.invalid || state.saving) return;
733
+ if (modelState.available && modelState.dirty) models.save();
734
+ if (limitState.available && limitState.dirty) limits.save();
735
+ },
736
+ discard: () => {
737
+ if (subagentCardShell(limits.hooks.subagentLimitsCard.getSnapshot(), models.hooks.subagentModelSelectionCard.getSnapshot()).saving) return;
738
+ limits.discard();
739
+ models.discard();
740
+ }
741
+ };
742
+ }
743
+ //#endregion
744
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-plugins/src/client/SubagentCard.module.css.mjs
745
+ const css = ".UYsYFG_section{min-width:0;padding:16px 0}.UYsYFG_heading{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:600;line-height:1.5}";
746
+ const tagId = "@deepseek-ai/dsh-client-ui-settings-plugins/SubagentCard.module.css";
747
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
748
+ const tag = document.createElement("style");
749
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-plugins";
750
+ tag.dataset.pluginCss = tagId;
751
+ tag.textContent = css;
752
+ document.head.appendChild(tag);
753
+ }
754
+ var SubagentCard_module_css_default = {
755
+ "heading": "UYsYFG_heading",
756
+ "section": "UYsYFG_section"
757
+ };
758
+ //#endregion
759
+ //#region lib/types/client/SubagentCard.js
760
+ /** One settings card for Subagent delegation limits and model authorization. */
761
+ /**
762
+ * Render the available Subagent settings with one configuration page and save footer.
763
+ * @param props - Locale, both form snapshots, and their shared actions.
764
+ * @returns The summary or the available settings form.
765
+ */
766
+ function SubagentCard(props) {
687
767
  const { t } = props;
688
- const state = props.useWebSearchCard((snapshot) => snapshot);
689
- const disabled = !state.writable;
690
- return (0, react_jsx_runtime.jsxs)(PluginCard, {
768
+ const limits = props.useSubagentLimitsCard((snapshot) => snapshot);
769
+ const models = props.useSubagentModelSelectionCard((snapshot) => snapshot);
770
+ const headingId = (0, react.useId)();
771
+ if (props.view === "summary") return t("subagentDescription");
772
+ const state = subagentCardShell(limits, models);
773
+ return (0, react_jsx_runtime.jsxs)(PluginConfigForm, {
691
774
  t,
692
- titleKey: "webSearchTitle",
693
- descriptionKey: "webSearchDescription",
694
775
  state,
695
776
  onSave: props.save,
696
777
  onDiscard: props.discard,
697
- children: [
698
- (0, react_jsx_runtime.jsx)(SecretField, {
699
- id: "plugin-config-web-search-key",
700
- label: t("webSearchApiKey"),
701
- hint: t("webSearchApiKeyHint"),
702
- disabled: !state.apiKeyWritable,
703
- text: state.apiKey.text,
704
- configured: state.apiKeyConfigured,
705
- stateLabel: state.apiKeyConfigured ? t("webSearchApiKeySet") : t("webSearchApiKeyUnset"),
706
- onEdit: (text) => {
707
- props.edit("apiKey", text);
708
- }
709
- }),
710
- (0, react_jsx_runtime.jsx)(ValueField, {
711
- id: "plugin-config-web-search-endpoint",
712
- label: t("webSearchBaseUrl"),
713
- hint: t("webSearchBaseUrlHint"),
714
- overriddenLabel: t("overridden"),
715
- resetLabel: t("reset"),
716
- invalidLabel: t("invalidNumber"),
717
- disabled,
718
- ...state.baseURL,
719
- onEdit: (text) => {
720
- props.edit("baseURL", text);
778
+ children: [limits.available ? (0, react_jsx_runtime.jsxs)("section", {
779
+ className: SubagentCard_module_css_default.section,
780
+ "aria-labelledby": `${headingId}-limits`,
781
+ children: [(0, react_jsx_runtime.jsx)("h3", {
782
+ className: SubagentCard_module_css_default.heading,
783
+ id: `${headingId}-limits`,
784
+ children: t("subagentLimitsTitle")
785
+ }), (0, react_jsx_runtime.jsx)(SubagentLimitsFields, {
786
+ t,
787
+ state: {
788
+ ...limits,
789
+ saving: state.saving
721
790
  },
722
- onReset: () => {
723
- props.resetField("baseURL");
724
- }
725
- }),
726
- (0, react_jsx_runtime.jsx)(ValueField, {
727
- id: "plugin-config-web-search-max-uses",
728
- label: t("webSearchMaxUses"),
729
- hint: t("webSearchMaxUsesHint"),
730
- overriddenLabel: t("overridden"),
731
- resetLabel: t("reset"),
732
- invalidLabel: t("invalidNumber"),
733
- numeric: true,
734
- disabled,
735
- ...state.maxUses,
736
- onEdit: (text) => {
737
- props.edit("maxUses", text);
791
+ edit: props.editLimit,
792
+ resetField: props.resetLimit
793
+ })]
794
+ }) : null, models.available ? (0, react_jsx_runtime.jsxs)("section", {
795
+ className: SubagentCard_module_css_default.section,
796
+ "aria-labelledby": `${headingId}-models`,
797
+ children: [(0, react_jsx_runtime.jsx)("h3", {
798
+ className: SubagentCard_module_css_default.heading,
799
+ id: `${headingId}-models`,
800
+ children: t("subagentModelSelectionTitle")
801
+ }), (0, react_jsx_runtime.jsx)(SubagentModelSelectionFields, {
802
+ t,
803
+ state: {
804
+ ...models,
805
+ saving: state.saving
738
806
  },
739
- onReset: () => {
740
- props.resetField("maxUses");
741
- }
742
- })
743
- ]
807
+ toggleEnabled: props.toggleEnabled,
808
+ toggleModel: props.toggleModel,
809
+ retryCatalog: props.retryCatalog
810
+ })]
811
+ }) : null]
744
812
  });
745
813
  }
746
814
  //#endregion
@@ -1014,6 +1082,111 @@ window.__ModuleLoader__.load({
1014
1082
  }
1015
1083
  };
1016
1084
  //#endregion
1085
+ //#region lib/types/client/subagent-limits-card-controller.js
1086
+ /** Staged delegation limits backed by the Host's subagent settings section. */
1087
+ function limitField(field, minimum) {
1088
+ const numeric = numberField(field);
1089
+ return {
1090
+ ...numeric,
1091
+ parse: (text) => {
1092
+ const write = numeric.parse(text);
1093
+ if (write?.kind !== "set") return write;
1094
+ const value = write.value;
1095
+ return Number.isSafeInteger(value) && value >= minimum && !Object.is(value, -0) ? write : void 0;
1096
+ }
1097
+ };
1098
+ }
1099
+ /** Bind two independently resettable limits to one staged settings form. */
1100
+ var SubagentLimitsCardController = class {
1101
+ form;
1102
+ store;
1103
+ /** @param scope - The Host's `subagent` settings section. */
1104
+ constructor(scope) {
1105
+ this.form = new CardForm(scope, [limitField("maxDepth", 0), limitField("maxActiveSubagents", 1)]);
1106
+ this.store = this.form.bind(() => ({
1107
+ ...this.form.shell(),
1108
+ maxDepth: this.form.field("maxDepth"),
1109
+ maxActiveSubagents: this.form.field("maxActiveSubagents")
1110
+ }));
1111
+ }
1112
+ /**
1113
+ * Bind the limits editor to the slot renderer.
1114
+ * @returns The limits snapshot and staged write actions.
1115
+ */
1116
+ inject() {
1117
+ return {
1118
+ hooks: { subagentLimitsCard: this.store },
1119
+ ...this.form.actions()
1120
+ };
1121
+ }
1122
+ };
1123
+ //#endregion
1124
+ //#region lib/types/client/WebSearchCard.js
1125
+ /**
1126
+ * Render the web-search provider's one-liner or its configuration form, as the Plugins page asks.
1127
+ * @param props - the view asked for, locale copy, the form snapshot, and its actions.
1128
+ * @returns the one-liner, or the form.
1129
+ */
1130
+ function WebSearchCard(props) {
1131
+ const { t } = props;
1132
+ const state = props.useWebSearchCard((snapshot) => snapshot);
1133
+ if (props.view === "summary") return t("webSearchDescription");
1134
+ const disabled = !state.writable;
1135
+ return (0, react_jsx_runtime.jsxs)(PluginConfigForm, {
1136
+ t,
1137
+ state,
1138
+ onSave: props.save,
1139
+ onDiscard: props.discard,
1140
+ children: [
1141
+ (0, react_jsx_runtime.jsx)(SecretField, {
1142
+ id: "plugin-config-web-search-key",
1143
+ label: t("webSearchApiKey"),
1144
+ hint: t("webSearchApiKeyHint"),
1145
+ disabled: !state.apiKeyWritable,
1146
+ text: state.apiKey.text,
1147
+ configured: state.apiKeyConfigured,
1148
+ stateLabel: state.apiKeyConfigured ? t("webSearchApiKeySet") : t("webSearchApiKeyUnset"),
1149
+ onEdit: (text) => {
1150
+ props.edit("apiKey", text);
1151
+ }
1152
+ }),
1153
+ (0, react_jsx_runtime.jsx)(ValueField, {
1154
+ id: "plugin-config-web-search-endpoint",
1155
+ label: t("webSearchBaseUrl"),
1156
+ hint: t("webSearchBaseUrlHint"),
1157
+ overriddenLabel: t("overridden"),
1158
+ resetLabel: t("reset"),
1159
+ invalidLabel: t("invalidNumber"),
1160
+ disabled,
1161
+ ...state.baseURL,
1162
+ onEdit: (text) => {
1163
+ props.edit("baseURL", text);
1164
+ },
1165
+ onReset: () => {
1166
+ props.resetField("baseURL");
1167
+ }
1168
+ }),
1169
+ (0, react_jsx_runtime.jsx)(ValueField, {
1170
+ id: "plugin-config-web-search-max-uses",
1171
+ label: t("webSearchMaxUses"),
1172
+ hint: t("webSearchMaxUsesHint"),
1173
+ overriddenLabel: t("overridden"),
1174
+ resetLabel: t("reset"),
1175
+ invalidLabel: t("invalidNumber"),
1176
+ numeric: true,
1177
+ disabled,
1178
+ ...state.maxUses,
1179
+ onEdit: (text) => {
1180
+ props.edit("maxUses", text);
1181
+ },
1182
+ onReset: () => {
1183
+ props.resetField("maxUses");
1184
+ }
1185
+ })
1186
+ ]
1187
+ });
1188
+ }
1189
+ //#endregion
1017
1190
  //#region lib/types/client/agent-loop-card-controller.js
1018
1191
  /** The agent-loop card's staged form over the `agent-loop` settings namespace. */
1019
1192
  /**
@@ -1084,74 +1257,6 @@ window.__ModuleLoader__.load({
1084
1257
  }
1085
1258
  };
1086
1259
  //#endregion
1087
- //#region lib/types/client/tab-store.js
1088
- /**
1089
- * The configurable-plugins tab's card list.
1090
- *
1091
- * The tab dispatches its slot by settings namespace, so what it renders is
1092
- * the intersection of two ledgers: the namespaces the Host serves and the
1093
- * cards registered into `settings.plugin.item`. A served namespace no card
1094
- * claims renders nothing — another surface owns it, or this deployment ships
1095
- * no browser half for it — and a card whose namespace the Host does not serve
1096
- * is never dispatched, so a plugin this deployment did not compose leaves no
1097
- * trace and does not count toward the empty line.
1098
- */
1099
- /** Derives the served namespaces from the shared describe mirror and pairs them with the cards that claim them. */
1100
- var ConfigurablePluginsTabController = class {
1101
- describeFace;
1102
- entries;
1103
- store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
1104
- loaded: false,
1105
- namespaces: []
1106
- });
1107
- disposed = false;
1108
- unsubscribe;
1109
- /**
1110
- * @param describeFace - the shared mirror's describe face; its refreshes
1111
- * (document commits, reconnects) are what keep the served set current.
1112
- * @param entries - reads the cards currently registered into the section's slot.
1113
- */
1114
- constructor(describeFace, entries) {
1115
- this.describeFace = describeFace;
1116
- this.entries = entries;
1117
- this.unsubscribe = describeFace.subscribe(() => {
1118
- this.publish();
1119
- });
1120
- describeFace.ensure();
1121
- this.publish();
1122
- }
1123
- /** Republish after the slot ledger changed; a card registered late joins here. */
1124
- refresh() {
1125
- if (this.disposed) return;
1126
- this.publish();
1127
- }
1128
- /** Stop publishing and stop following the mirror. */
1129
- dispose() {
1130
- this.disposed = true;
1131
- this.unsubscribe();
1132
- }
1133
- /**
1134
- * Build the face the tab's slot registration injects.
1135
- * @returns the tab's snapshot source.
1136
- */
1137
- inject() {
1138
- return { hooks: { configurablePlugins: this.store } };
1139
- }
1140
- publish() {
1141
- if (this.disposed) return;
1142
- const mirrored = this.describeFace.getSnapshot();
1143
- const loaded = mirrored.view !== void 0;
1144
- const served = new Set(mirrored.view?.namespaces.map((view) => view.ns) ?? []);
1145
- const namespaces = this.entries().flatMap((entry) => entry.options.key !== void 0 && served.has(entry.options.key) ? [entry.options.key] : []);
1146
- const previous = this.store.getSnapshot();
1147
- if (previous.loaded === loaded && previous.namespaces.length === namespaces.length && previous.namespaces.every((ns, index) => ns === namespaces[index])) return;
1148
- this.store.set({
1149
- loaded,
1150
- namespaces
1151
- });
1152
- }
1153
- };
1154
- //#endregion
1155
1260
  //#region lib/types/client/subagent-model-selection-card-controller.js
1156
1261
  /** Staged editor for the Host-owned subagent model allowlist. */
1157
1262
  /** Namespace of the Host-owned subagent model-selection preference. */
@@ -1560,24 +1665,20 @@ window.__ModuleLoader__.load({
1560
1665
  }
1561
1666
  //#endregion
1562
1667
  //#region lib/types/client/locales.js
1563
- /** Locale bundles for the plugin configuration section and its plugin cards. */
1668
+ /** Locale bundles for the built-in plugins settings section and the plugin configuration pages. */
1564
1669
  /** English copy. */
1565
1670
  const en = {
1566
- nav: "Plugins",
1567
- title: "Plugins",
1568
- intro: "Configure and inspect the plugins installed in this deployment.",
1671
+ nav: "Built-in plugins",
1672
+ title: "Built-in plugins",
1673
+ intro: "Inspect the plugins this deployment ships.",
1569
1674
  tabs: "Plugin views",
1570
- configurableTab: "Plugin configuration",
1571
- empty: "This deployment exposes no plugin settings.",
1675
+ empty: "This deployment exposes no plugin views.",
1572
1676
  overridden: "Overridden",
1573
1677
  reset: "Reset to default",
1574
1678
  readOnly: "This deployment stores settings read-only.",
1575
- expand: "Show settings",
1576
- collapse: "Hide settings",
1679
+ unavailable: "This plugin is not loaded, so it cannot be configured right now.",
1577
1680
  save: "Save",
1578
1681
  saving: "Saving…",
1579
- discard: "Discard",
1580
- unsaved: "Unsaved",
1581
1682
  saveFailed: "The deployment did not accept these values; they were left for you to correct.",
1582
1683
  invalidNumber: "Enter a number, or leave blank to use the default.",
1583
1684
  bashTitle: "Shell",
@@ -1600,10 +1701,23 @@ window.__ModuleLoader__.load({
1600
1701
  webSearchBaseUrlHint: "Leave blank to use the provider default.",
1601
1702
  webSearchMaxUses: "Max searches per request",
1602
1703
  webSearchMaxUsesHint: "How many times one request may search before it must answer.",
1603
- subagentModelSelectionTitle: "Subagent",
1604
- subagentModelSelectionDescription: "Control which models agents may choose for subagents.",
1605
- subagentModelSelectionToggle: "Allow agents to choose models for subagents",
1606
- subagentModelSelectionChoose: "When enabled, agents can choose a provider, model, and reasoning effort for each subagent from the authorized models below. Applies only to new sessions.",
1704
+ subagentTitle: "Subagent",
1705
+ subagentDescription: "Set Subagent recursion depth, count, and models.",
1706
+ subagentLimitsTitle: "Limits",
1707
+ subagentMaxDepth: "Maximum recursion depth",
1708
+ subagentDepthHelpLabel: "About maximum recursion depth",
1709
+ subagentDepthHelp: "Limits how many levels of Subagents an Agent can create.",
1710
+ subagentDepthZero: "Disable Subagents",
1711
+ subagentDepthOne: "Only the main Agent can create Subagents",
1712
+ subagentDepthOverride: "If a tool defines its own maximum recursion depth, that setting takes precedence.",
1713
+ subagentMaxActive: "Subagent parallelism limit",
1714
+ subagentCapacityHelpLabel: "About the Subagent parallelism limit",
1715
+ subagentCapacityHelp: "Total live Subagents under the same main Agent, across all recursion levels. The main Agent is excluded. New start requests are rejected when the limit is reached.",
1716
+ subagentDepthInvalid: "Enter a whole number of 0 or more.",
1717
+ subagentCapacityInvalid: "Enter a whole number of 1 or more.",
1718
+ subagentModelSelectionTitle: "Model selection",
1719
+ subagentModelSelectionToggle: "Allow agents to choose models for Subagents",
1720
+ subagentModelSelectionChoose: "When enabled, agents can choose a provider, model, and reasoning effort for each Subagent from the authorized models below. Applies only to new sessions.",
1607
1721
  subagentModelSelectionAllowed: "Models agents may choose",
1608
1722
  subagentModelSelectionLoading: "Loading models…",
1609
1723
  subagentModelSelectionLoadFailed: "Models could not be loaded.",
@@ -1618,21 +1732,17 @@ window.__ModuleLoader__.load({
1618
1732
  };
1619
1733
  /** Simplified Chinese copy. */
1620
1734
  const zh = {
1621
- nav: "插件",
1622
- title: "插件",
1623
- intro: "配置和查看本部署已安装的插件。",
1735
+ nav: "内置插件",
1736
+ title: "内置插件",
1737
+ intro: "查看内置部署的插件列表",
1624
1738
  tabs: "插件视图",
1625
- configurableTab: "插件配置",
1626
- empty: "本部署没有开放任何插件设置。",
1739
+ empty: "本部署没有开放任何插件视图。",
1627
1740
  overridden: "已覆盖",
1628
1741
  reset: "恢复默认",
1629
1742
  readOnly: "本部署的设置为只读。",
1630
- expand: "展开设置",
1631
- collapse: "收起设置",
1743
+ unavailable: "该插件当前未加载,暂时无法配置。",
1632
1744
  save: "保存",
1633
1745
  saving: "保存中…",
1634
- discard: "放弃修改",
1635
- unsaved: "未保存",
1636
1746
  saveFailed: "本部署没有接受这些值,已保留供你修改。",
1637
1747
  invalidNumber: "请填数字;留空表示使用默认值。",
1638
1748
  bashTitle: "终端",
@@ -1655,8 +1765,21 @@ window.__ModuleLoader__.load({
1655
1765
  webSearchBaseUrlHint: "留空则使用提供方默认地址。",
1656
1766
  webSearchMaxUses: "单次请求最多搜索次数",
1657
1767
  webSearchMaxUsesHint: "一次请求在必须作答前最多可以搜索多少次。",
1658
- subagentModelSelectionTitle: "Subagent",
1659
- subagentModelSelectionDescription: "控制 Agent 为 Subagent 选择模型的权限。",
1768
+ subagentTitle: "Subagent",
1769
+ subagentDescription: "设置 Subagent 的递归层级、数量和模型。",
1770
+ subagentLimitsTitle: "运行限制",
1771
+ subagentMaxDepth: "最大递归深度",
1772
+ subagentDepthHelpLabel: "最大递归深度说明",
1773
+ subagentDepthHelp: "限制 Agent 创建 Subagent 的递归层级。",
1774
+ subagentDepthZero: "禁用 Subagent",
1775
+ subagentDepthOne: "仅允许主 Agent 创建 Subagent",
1776
+ subagentDepthOverride: "如果某个工具单独设置了最大递归深度,以该工具的设置为准。",
1777
+ subagentMaxActive: "Subagent 并行数量上限",
1778
+ subagentCapacityHelpLabel: "Subagent 并行数量上限说明",
1779
+ subagentCapacityHelp: "同一主 Agent 下,所有递归层级同时存活的 Subagent 总数,主 Agent 不计入。达到上限时,新的启动请求会被拒绝。",
1780
+ subagentDepthInvalid: "请输入不小于 0 的整数。",
1781
+ subagentCapacityInvalid: "请输入不小于 1 的整数。",
1782
+ subagentModelSelectionTitle: "模型选择",
1660
1783
  subagentModelSelectionToggle: "允许 Agent 为 Subagent 选择模型",
1661
1784
  subagentModelSelectionChoose: "开启后,Agent 可以从下方授权模型中,为每个 Subagent 选择提供方、模型和推理强度。仅影响新会话。",
1662
1785
  subagentModelSelectionAllowed: "Agent 可选择的模型",
@@ -1674,14 +1797,14 @@ window.__ModuleLoader__.load({
1674
1797
  //#endregion
1675
1798
  //#region lib/types/client/index.js
1676
1799
  /**
1677
- * Plugins settings surface, browser half — one section whose feature-owned
1678
- * tabs include configurable Host plugin cards and read-only inventory.
1679
- *
1680
- * The section declares `settings.plugins.tab`; its own `configurable` tab then
1681
- * declares `settings.plugin.item` and renders whatever cards were registered
1682
- * into it. The cards this package ships are the host-plane sections the
1683
- * deployment already exposes; each binds its namespace through the client
1684
- * settings scope, which keeps them unaware of one another and of other tabs.
1800
+ * Built-in plugins settings surface and the official plugin configuration
1801
+ * pages, browser half. The Settings section is the shell around the
1802
+ * feature-owned tabs registered into `settings.plugins.tab` (the read-only
1803
+ * inventory ships one); the configuration pages this package ships register
1804
+ * into the Plugins page's `plugins.item` slot, for the host-plane namespaces
1805
+ * the deployment exposes, and appear in the page's Official group. Each form
1806
+ * binds its namespace through the client settings scope, which keeps the
1807
+ * pages unaware of one another and of the section.
1685
1808
  */
1686
1809
  /** Dictionary namespace owned by this plugin. */
1687
1810
  const NS = "settings.plugins";
@@ -1695,7 +1818,7 @@ window.__ModuleLoader__.load({
1695
1818
  "settingsScope"
1696
1819
  ];
1697
1820
  /**
1698
- * Mount the plugin configuration section and the cards this package ships.
1821
+ * Mount the built-in plugins section and the configuration pages this package ships.
1699
1822
  * @param ctx - the browser plugin context.
1700
1823
  */
1701
1824
  function apply(ctx) {
@@ -1707,7 +1830,10 @@ window.__ModuleLoader__.load({
1707
1830
  const bash = new BashCardController(ctx.settingsScope.bind({ namespace: SHELL_NS }));
1708
1831
  const agentLoop = new AgentLoopCardController(ctx.settingsScope.bind({ namespace: AGENT_LOOP_NS }));
1709
1832
  const webSearch = new WebSearchCardController(ctx.settingsScope.bind({ namespace: WEB_SEARCH_NS }), ctx);
1833
+ const subagentLimits = new SubagentLimitsCardController(ctx.settingsScope.bind({ namespace: "subagent" }));
1710
1834
  const subagentModelSelection = new SubagentModelSelectionCardController(ctx.settingsScope.bind({ namespace: SUBAGENT_MODEL_SELECTION_NS }), ctx);
1835
+ const subagentLimitsFace = subagentLimits.inject();
1836
+ const subagentModelsFace = subagentModelSelection.inject();
1711
1837
  ctx.effect(() => ctx.remote.$on("credentials/reference-updated", (ref) => {
1712
1838
  webSearch.refreshCredential(ref);
1713
1839
  }), "ui-settings-plugins: credential invalidations");
@@ -1723,13 +1849,65 @@ window.__ModuleLoader__.load({
1723
1849
  ctx.effect(() => () => {
1724
1850
  subagentModelSelection.dispose();
1725
1851
  }, "ui-settings-plugins: subagent preference");
1726
- const configurable = new ConfigurablePluginsTabController(ctx.settingsScope.describe(), () => ctx.slots.entries("settings.plugin.item"));
1727
- ctx.effect(() => () => {
1728
- configurable.dispose();
1729
- }, "ui-settings-plugins: tab directory");
1730
- ctx.effect(() => ctx.slots.subscribe("settings.plugin.item", () => {
1731
- configurable.refresh();
1732
- }), "ui-settings-plugins: card ledger");
1852
+ const pages = [
1853
+ [[SHELL_NS], () => ctx.slots.inject("plugins.item", () => ctx.slots.register({
1854
+ name: "plugins.item",
1855
+ id: "bash",
1856
+ order: 10,
1857
+ label: () => t("bashTitle"),
1858
+ locale: NS,
1859
+ inject: () => bash.inject()
1860
+ }, BashCard))],
1861
+ [[AGENT_LOOP_NS], () => ctx.slots.inject("plugins.item", () => ctx.slots.register({
1862
+ name: "plugins.item",
1863
+ id: "agent-loop",
1864
+ order: 20,
1865
+ label: () => t("agentLoopTitle"),
1866
+ locale: NS,
1867
+ inject: () => agentLoop.inject()
1868
+ }, AgentLoopCard))],
1869
+ [["subagent", SUBAGENT_MODEL_SELECTION_NS], () => ctx.slots.inject("plugins.item", () => ctx.slots.register({
1870
+ name: "plugins.item",
1871
+ id: "subagent",
1872
+ order: 30,
1873
+ label: () => t("subagentTitle"),
1874
+ locale: NS,
1875
+ inject: () => subagentCardFace(subagentLimitsFace, subagentModelsFace)
1876
+ }, SubagentCard))],
1877
+ [[WEB_SEARCH_NS], () => ctx.slots.inject("plugins.item", () => ctx.slots.register({
1878
+ name: "plugins.item",
1879
+ id: "web-search",
1880
+ order: 40,
1881
+ label: () => t("webSearchTitle"),
1882
+ locale: NS,
1883
+ inject: () => webSearch.inject()
1884
+ }, WebSearchCard))]
1885
+ ];
1886
+ const describeFace = ctx.settingsScope.describe();
1887
+ ctx.effect(() => {
1888
+ const registered = /* @__PURE__ */ new Map();
1889
+ const sync = () => {
1890
+ const served = new Set(describeFace.getSnapshot().view?.namespaces.map((view) => view.ns) ?? []);
1891
+ for (const [namespaces, register] of pages) {
1892
+ const namespace = namespaces[0];
1893
+ const available = namespaces.some((namespace) => served.has(namespace));
1894
+ const off = registered.get(namespace);
1895
+ if (available && off === void 0) registered.set(namespace, register());
1896
+ else if (!available && off !== void 0) {
1897
+ off();
1898
+ registered.delete(namespace);
1899
+ }
1900
+ }
1901
+ };
1902
+ const unsubscribe = describeFace.subscribe(sync);
1903
+ describeFace.ensure();
1904
+ sync();
1905
+ return () => {
1906
+ unsubscribe();
1907
+ for (const off of registered.values()) off();
1908
+ registered.clear();
1909
+ };
1910
+ }, "ui-settings-plugins: configuration pages");
1733
1911
  let tabsVersion = -1;
1734
1912
  let tabsRevision = -1;
1735
1913
  let tabs = [];
@@ -1770,44 +1948,6 @@ window.__ModuleLoader__.load({
1770
1948
  scope: "root"
1771
1949
  } }
1772
1950
  }, PluginsSettingsSection));
1773
- ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
1774
- name: "settings.plugins.tab",
1775
- id: "configurable",
1776
- order: 0,
1777
- label: () => t("configurableTab"),
1778
- locale: NS,
1779
- inject: () => configurable.inject(),
1780
- children: { "settings.plugin.item": {
1781
- kind: "keyed",
1782
- scope: "root"
1783
- } }
1784
- }, ConfigurablePluginsTab));
1785
- ctx.slots.inject("settings.plugin.item", function* () {
1786
- yield ctx.slots.register({
1787
- name: "settings.plugin.item",
1788
- key: SHELL_NS,
1789
- locale: NS,
1790
- inject: () => bash.inject()
1791
- }, BashCard);
1792
- yield ctx.slots.register({
1793
- name: "settings.plugin.item",
1794
- key: AGENT_LOOP_NS,
1795
- locale: NS,
1796
- inject: () => agentLoop.inject()
1797
- }, AgentLoopCard);
1798
- yield ctx.slots.register({
1799
- name: "settings.plugin.item",
1800
- key: SUBAGENT_MODEL_SELECTION_NS,
1801
- locale: NS,
1802
- inject: () => subagentModelSelection.inject()
1803
- }, SubagentModelSelectionCard);
1804
- yield ctx.slots.register({
1805
- name: "settings.plugin.item",
1806
- key: WEB_SEARCH_NS,
1807
- locale: NS,
1808
- inject: () => webSearch.inject()
1809
- }, WebSearchCard);
1810
- });
1811
1951
  }
1812
1952
  //#endregion
1813
1953
  exports.apply = apply;