@matamt-dbg/dsh-studio 0.2.2 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. package/README.md +9 -4
  2. package/client.js +499 -219
  3. package/index.js +340 -64
  4. package/package.json +1 -1
  5. package/policy.js +135 -12
  6. package/roles.js +25 -3
package/client.js CHANGED
@@ -93,6 +93,8 @@ function parsePreviewRequest(value) {
93
93
  if (row.providerConfigs !== void 0 && (!record2(row.providerConfigs) || Object.values(row.providerConfigs).some((v) => !record2(v)))) throw new Error("Invalid provider configs");
94
94
  if (row.configOverrides !== void 0) validateConfigOverrides(row.configOverrides);
95
95
  if (row.execution !== void 0) validateExecutionDefaults(row.execution);
96
+ const filter = row.subagentToolFilter;
97
+ if (filter !== void 0 && (!record2(filter) || !strings(filter.deny) || filter.deny.some((name) => !name))) throw new Error("Invalid subagent tool filter");
96
98
  return {
97
99
  ...row.configOverrides !== void 0 ? { configOverrides: row.configOverrides } : {},
98
100
  ...row.execution !== void 0 ? { execution: row.execution } : {},
@@ -104,7 +106,8 @@ function parsePreviewRequest(value) {
104
106
  ...row.persona !== void 0 ? { persona: row.persona } : {},
105
107
  ...row.sections !== void 0 ? { sections: row.sections } : {},
106
108
  ...row.toolOverrides !== void 0 ? { toolOverrides: row.toolOverrides } : {},
107
- ...row.providerConfigs !== void 0 ? { providerConfigs: row.providerConfigs } : {}
109
+ ...row.providerConfigs !== void 0 ? { providerConfigs: row.providerConfigs } : {},
110
+ ...filter !== void 0 && filter.deny.length ? { subagentToolFilter: { deny: [...new Set(filter.deny)] } } : {}
108
111
  };
109
112
  }
110
113
 
@@ -112,8 +115,7 @@ function parsePreviewRequest(value) {
112
115
  var import_react = __toESM(require("react"), 1);
113
116
 
114
117
  // src/ui-styles.ts
115
- var studioStyles = `/* Only this adapter references host tokens. Studio never installs a global theme. */
116
- .studio-ui {
118
+ var adapter = `.studio-ui {
117
119
  --studio-bg: var(--dsw-alias-bg-base);
118
120
  --studio-surface: var(--dsw-alias-bg-layer-1);
119
121
  --studio-inset: var(--dsw-alias-bg-layer-2);
@@ -145,7 +147,9 @@ var studioStyles = `/* Only this adapter references host tokens. Studio never in
145
147
  min-width: 0;
146
148
  overflow-wrap: anywhere;
147
149
  }
148
- .studio-ui *, .studio-ui *::before, .studio-ui *::after { box-sizing: border-box; }
150
+ `;
151
+ var studioStyles = `/* Only this adapter references host tokens. Studio never installs a global theme. */
152
+ ${adapter}.studio-ui *, .studio-ui *::before, .studio-ui *::after { box-sizing: border-box; }
149
153
  .studio-ui .studio-tool-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 12px; }
150
154
  .studio-ui .studio-tool-card { display: flex; flex-direction: column; gap: 8px; padding: 14px; border: 1px solid var(--studio-line); border-radius: var(--studio-radius); cursor: pointer; min-width: 0; }
151
155
  .studio-ui .studio-tool-card[data-selected="true"] { background: var(--studio-hover); border-color: var(--studio-ink); }
@@ -214,6 +218,14 @@ var studioStyles = `/* Only this adapter references host tokens. Studio never in
214
218
  .studio-ui .studio-service-panel[hidden] { display: none; }
215
219
  .studio-ui .studio-service-panel > .studio-ui { max-width: 960px; margin: 0 auto; padding: 24px clamp(16px, 3vw, 40px) 48px; }
216
220
  .studio-ui .studio-layout { display: grid; grid-template-columns: 208px minmax(0, 1fr); height: 100%; }
221
+ /* Version footer pinned to the bottom of the Studio navigation. */
222
+ .studio-ui .studio-nav-footer { display: flex; align-items: center; gap: 6px; margin-top: auto; padding: 12px 10px 0; color: var(--studio-subtle); font-size: 12px; }
223
+ .studio-ui .studio-icon-button { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: 0; border-radius: var(--studio-small-radius); background: none; color: var(--studio-info); cursor: pointer; }
224
+ .studio-ui .studio-icon-button:hover:not(:disabled) { background: var(--studio-hover); }
225
+ .studio-ui .studio-icon-button[data-tone=success] { color: var(--studio-success); }
226
+ .studio-ui .studio-icon-button[data-tone=error] { color: var(--studio-error); }
227
+ .studio-ui .studio-icon-button:disabled { cursor: default; opacity: 1; }
228
+ .studio-ui .studio-icon-button[aria-busy=true] { opacity: .5; }
217
229
  .studio-ui nav.studio-nav { display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px; margin: 0; padding: 24px 12px; border-inline-end: .5px solid var(--studio-line); overflow-y: auto; }
218
230
  .studio-ui .studio-nav-head { display: grid; gap: 2px; padding: 0 10px 8px; }
219
231
  .studio-ui .studio-nav-title { font-size: 14px; }
@@ -604,6 +616,7 @@ var studioStyles = `/* Only this adapter references host tokens. Studio never in
604
616
  .studio-ui nav.studio-nav { flex-direction: row; overflow-x: auto; padding: 8px 12px; border-inline-end: 0; border-bottom: .5px solid var(--studio-line); }
605
617
  .studio-ui .studio-nav-title, .studio-ui .studio-nav-group { display: none; }
606
618
  .studio-ui .studio-nav-item { flex: 0 0 auto; }
619
+ .studio-ui .studio-nav-footer { margin: 0 0 0 auto; padding: 0 0 0 8px; }
607
620
  .studio-ui .studio-scroller { overflow: visible; }
608
621
  }
609
622
  @container studio-main (max-width: 560px) {
@@ -622,6 +635,13 @@ var studioStyles = `/* Only this adapter references host tokens. Studio never in
622
635
  .studio-ui dd { margin-bottom: 8px; }
623
636
  }
624
637
  `;
638
+ var sidebarMarkStyles = `${adapter}
639
+ .studio-ui.studio-sidebar-glyph { display: inline-flex; color: inherit; line-height: 0; }
640
+ .studio-ui .studio-sidebar-dot { fill: var(--studio-info); }
641
+ /* order keeps the mark last even if the sidebar later appends its own label. */
642
+ .studio-ui.studio-sidebar-update { display: inline-flex; flex: none; align-items: center; order: 99; margin-left: auto; color: var(--studio-info); line-height: 0; }
643
+ .studio-ui.studio-sidebar-update[data-phase=failed] { color: var(--studio-error); }
644
+ `;
625
645
 
626
646
  // src/ui.tsx
627
647
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -699,6 +719,9 @@ function SplitButton({ label, onClick, primary = false, disabled = false, action
699
719
  }, children: action.label }, action.label)) })
700
720
  ] });
701
721
  }
722
+ function IconButton({ label, tone = "info", disabled = false, busy = false, onClick, children }) {
723
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "studio-icon-button", "data-tone": tone, title: label, "aria-label": label, "aria-busy": busy || void 0, disabled, onClick, children });
724
+ }
702
725
  function DetailDialog({ open, title, subtitle, onClose, closeLabel, footer, wide = false, compact = false, children }) {
703
726
  const ref = import_react.default.useRef(null);
704
727
  const opener = import_react.default.useRef(null);
@@ -733,15 +756,76 @@ function DetailDialog({ open, title, subtitle, onClose, closeLabel, footer, wide
733
756
  }
734
757
 
735
758
  // src/role-editor.tsx
759
+ var import_react3 = __toESM(require("react"), 1);
760
+
761
+ // src/item-picker.tsx
736
762
  var import_react2 = __toESM(require("react"), 1);
737
763
  var import_jsx_runtime2 = require("react/jsx-runtime");
738
- function RoleEditor({ locale, roles, onChange, loadModels, defaultPersona = "" }) {
739
- const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
740
- const [selected, setSelected] = import_react2.default.useState();
741
- const [catalog, setCatalog] = import_react2.default.useState();
742
- const [error, setError] = import_react2.default.useState("");
743
- const [loading, setLoading] = import_react2.default.useState(false);
744
- const refresh = import_react2.default.useCallback(async () => {
764
+ function ItemPicker({ label, items, selected, onChange, labels, renderDetails, isModified, hasDetails }) {
765
+ const [query, setQuery] = import_react2.default.useState("");
766
+ const [filter, setFilter] = import_react2.default.useState("all");
767
+ const [detail, setDetail] = import_react2.default.useState();
768
+ const chosen = new Set(selected);
769
+ const isOn = (item) => item.lockedReason !== void 0 || chosen.has(item.name);
770
+ const modified = (name) => chosen.has(name) && (isModified?.(name) ?? false);
771
+ const text = query.trim().toLowerCase();
772
+ const visible = items.filter((item) => (filter === "all" || (filter === "selected" ? isOn(item) : modified(item.name))) && (!text || `${item.name}
773
+ ${item.title ?? ""}
774
+ ${item.description}
775
+ ${item.searchText ?? ""}`.toLowerCase().includes(text)));
776
+ const counts = { all: items.length, selected: items.filter(isOn).length, modified: items.filter((item) => modified(item.name)).length };
777
+ const toggle = (name) => onChange(chosen.has(name) ? selected.filter((value) => value !== name) : [...selected, name]);
778
+ const open = items.find((item) => item.name === detail && chosen.has(item.name));
779
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-picker", children: [
780
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-picker-bar", children: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "search", "aria-label": labels.search, placeholder: labels.searchHint, value: query, onChange: (event) => setQuery(event.target.value) }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "studio-picker-filters", role: "radiogroup", "aria-label": labels.filter, children: ["all", "selected", "modified"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", role: "radio", "aria-checked": filter === value, className: "studio-picker-filter", onClick: () => setFilter(value), children: [
783
+ labels[value],
784
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-count", children: counts[value] })
785
+ ] }, value)) })
786
+ ] }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "studio-picker-list", "aria-label": label, children: [
788
+ visible.map((item) => {
789
+ const locked = item.lockedReason !== void 0;
790
+ const on = locked || chosen.has(item.name);
791
+ const blocked = locked || !on && item.disabledReason !== void 0;
792
+ const details = on && renderDetails !== void 0 && (hasDetails?.(item.name) ?? true);
793
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "studio-picker-row", "data-selected": on, children: [
794
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", role: "switch", "aria-checked": on, disabled: blocked, className: "studio-picker-toggle", "aria-label": `${item.title ?? item.name}`, onClick: () => toggle(item.name), children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-picker-check", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 8.5l3 3 6-7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "studio-picker-text", children: [
797
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "studio-picker-title", children: [
798
+ item.title ?? item.name,
799
+ item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-picker-subtitle studio-mono", children: item.subtitle })
800
+ ] }),
801
+ (locked ? item.lockedReason : item.disabledReason && !on ? item.disabledReason : item.description) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-picker-description", children: locked ? item.lockedReason : item.disabledReason && !on ? item.disabledReason : item.description })
802
+ ] })
803
+ ] }),
804
+ modified(item.name) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-badge", "data-tone": "info", "data-studio-modified": "true", children: labels.modifiedTag }),
805
+ details && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, "aria-label": `${labels.settings}: ${item.name}`, "aria-haspopup": "dialog", onClick: () => setDetail(item.name), children: labels.settings })
806
+ ] }, item.name);
807
+ }),
808
+ !visible.length && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "studio-picker-empty", role: "status", children: [
809
+ text ? labels.noResults : labels.empty,
810
+ (text || filter !== "all") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, onClick: () => {
811
+ setQuery("");
812
+ setFilter("all");
813
+ }, children: labels.clear })
814
+ ] })
815
+ ] }),
816
+ renderDetails && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DetailDialog, { open: open !== void 0, title: open?.title ?? open?.name, subtitle: open?.subtitle, closeLabel: labels.done, onClose: () => setDetail(void 0), children: open && renderDetails(open.name) })
817
+ ] });
818
+ }
819
+
820
+ // src/role-editor.tsx
821
+ var import_jsx_runtime3 = require("react/jsx-runtime");
822
+ function RoleEditor({ locale, roles, onChange, loadModels, defaultPersona = "", tools, hiddenTools = [], onHiddenToolsChange, delegates = true }) {
823
+ const t = (ko3, en3, zh3) => locale.startsWith("ko") ? ko3 : locale.startsWith("zh") ? zh3 : en3;
824
+ const [selected, setSelected] = import_react3.default.useState();
825
+ const [catalog, setCatalog] = import_react3.default.useState();
826
+ const [error, setError] = import_react3.default.useState("");
827
+ const [loading, setLoading] = import_react3.default.useState(false);
828
+ const refresh = import_react3.default.useCallback(async () => {
745
829
  if (!loadModels) return;
746
830
  setLoading(true);
747
831
  setError("");
@@ -753,120 +837,179 @@ function RoleEditor({ locale, roles, onChange, loadModels, defaultPersona = "" }
753
837
  setLoading(false);
754
838
  }
755
839
  }, [loadModels]);
756
- import_react2.default.useEffect(() => {
840
+ import_react3.default.useEffect(() => {
757
841
  void refresh();
758
842
  }, [refresh]);
759
843
  const models = (catalog?.groups ?? []).flatMap((group) => group.models.map((model2) => ({ ...model2, value: `${group.id}/${model2.id}`, label: `${group.name} \xB7 ${model2.name}` })));
760
844
  const role = selected === void 0 ? void 0 : roles[selected];
761
845
  const model = models.find((model2) => model2.value === role?.model);
762
- const update2 = (patch) => onChange(roles.map((role2, index) => index === selected ? { ...role2, ...patch } : role2));
763
- const field2 = (label, content) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "studio-field", children: [
764
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: label }),
846
+ const update2 = (patch) => onChange(roles.map((role2, index) => {
847
+ if (index !== selected) return role2;
848
+ const next = { ...role2, ...patch };
849
+ if (next.toolFilter === void 0) delete next.toolFilter;
850
+ return next;
851
+ }));
852
+ const field2 = (label, content) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "studio-field", children: [
853
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: label }),
765
854
  content
766
855
  ] });
767
856
  const close = () => setSelected(void 0);
768
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SectionHead, { title: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { className: "studio-panel-title", children: [
857
+ const defaultCount = hiddenTools.filter((name) => tools?.some((tool) => tool.name === name)).length;
858
+ const filterTitle = t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 Tool Filter", "Subagent Tool Filter", "\u5B50\u4EE3\u7406 Tool Filter");
859
+ const roleFilterLabel = t("\uC774 \uC5ED\uD560\uC758 Tool Filter", "Tool Filter for this role", "\u6B64\u89D2\u8272\u7684 Tool Filter");
860
+ const filterSummary = (item) => {
861
+ const own2 = item.toolFilter?.deny.filter((name) => tools?.some((tool) => tool.name === name) && !(item.toolFilter?.mode === "extend" && hiddenTools.includes(name))).length ?? 0;
862
+ return !item.toolFilter ? t("\uAE30\uBCF8\uAC12", "default", "\u9ED8\u8BA4") : item.toolFilter.mode === "extend" ? t(`\uAE30\uBCF8\uAC12 + ${own2}`, `default + ${own2}`, `\u9ED8\u8BA4 + ${own2}`) : t(`\uB530\uB85C ${own2}`, `own ${own2}`, `\u5355\u72EC ${own2}`);
863
+ };
864
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
865
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionHead, { title: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("h3", { className: "studio-panel-title", children: [
770
866
  t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8", "Subagents", "\u5B50\u4EE3\u7406"),
771
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "studio-count", children: roles.length })
772
- ] }), description: t("\uBC14\uAFBC \uB0B4\uC6A9\uC740 \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uACE0 DSH\uC5D0 \uB2E4\uC2DC \uBC18\uC601\uD574\uC57C \uC801\uC6A9\uB429\uB2C8\uB2E4.", "Save the preset and reapply it to DSH after changes.", "\u4FEE\u6539\u540E\u9700\u4FDD\u5B58\u9884\u8BBE\u5E76\u91CD\u65B0\u5E94\u7528\u5230 DSH\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
773
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, disabled: loading || !loadModels, onClick: () => void refresh(), children: loading ? t("\uBAA8\uB378 \uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading models\u2026", "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u2026") : t("\uBAA8\uB378 \uBAA9\uB85D \uC0C8\uB85C\uACE0\uCE68", "Refresh models", "\u5237\u65B0\u6A21\u578B\u5217\u8868") }),
774
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, onClick: () => {
867
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "studio-count", children: roles.length })
868
+ ] }), description: t("\uBC14\uAFBC \uB0B4\uC6A9\uC740 \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uACE0 DSH\uC5D0 \uB2E4\uC2DC \uBC18\uC601\uD574\uC57C \uC801\uC6A9\uB429\uB2C8\uB2E4.", "Save the preset and reapply it to DSH after changes.", "\u4FEE\u6539\u540E\u9700\u4FDD\u5B58\u9884\u8BBE\u5E76\u91CD\u65B0\u5E94\u7528\u5230 DSH\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
869
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { small: true, disabled: loading || !loadModels, onClick: () => void refresh(), children: loading ? t("\uBAA8\uB378 \uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading models\u2026", "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u2026") : t("\uBAA8\uB378 \uBAA9\uB85D \uC0C8\uB85C\uACE0\uCE68", "Refresh models", "\u5237\u65B0\u6A21\u578B\u5217\u8868") }),
870
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { small: true, onClick: () => {
775
871
  setSelected(roles.length);
776
872
  onChange([...roles, { name: "", role: "", model: "inherit", reasoning: "inherit", spawn: "fresh", persona: "" }]);
777
873
  }, children: t("\uC5ED\uD560 \uCD94\uAC00", "Add role", "\u6DFB\u52A0\u89D2\u8272") })
778
874
  ] }) }),
779
- error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Note, { children: error }),
780
- catalog?.failures.map((failure) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Note, { children: [
875
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: error }),
876
+ catalog?.failures.map((failure) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Note, { children: [
781
877
  failure.name,
782
878
  ": ",
783
879
  failure.message
784
880
  ] }, failure.name)),
785
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Note, { children: t(
881
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t(
786
882
  "DSH\uC758 \uAE30\uBCF8 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uB3C4\uAD6C(subagent, subagent_fork, list_subagent_models)\uB97C subagent_role \uD558\uB098\uB85C \uD1B5\uD569\uD574 \uC81C\uACF5\uD569\uB2C8\uB2E4. \uAE30\uBCF8 \uB3C4\uAD6C\uB294 \uC790\uB3D9\uC73C\uB85C \uBE60\uC9C0\uACE0, subagent_role\uC758 \uC124\uBA85\uACFC \uC778\uC790\uB294 \uD504\uB86C\uD504\uD2B8\xB7\uB3C4\uAD6C \uD0ED\uC758 \uB3C4\uAD6C \uBAA9\uB85D\uC5D0\uC11C \uD655\uC778\uD558\uACE0 \uC218\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
787
- "Studio provides the DSH default subagent tools (subagent, subagent_fork, list_subagent_models) as a single subagent_role tool. The default tools are removed automatically; review or edit subagent_role in the tool list on the Prompts & tools tab.",
883
+ "Studio replaces the DSH default subagent tools (subagent, subagent_fork, list_subagent_models) with a single subagent_role tool. The default tools are removed automatically; review or edit subagent_role in the tool list on the Prompts & tools tab.",
788
884
  "\u5C06 DSH \u9ED8\u8BA4\u5B50\u4EE3\u7406\u5DE5\u5177\uFF08subagent\u3001subagent_fork\u3001list_subagent_models\uFF09\u5408\u5E76\u4E3A\u4E00\u4E2A subagent_role \u5DE5\u5177\u63D0\u4F9B\u3002\u9ED8\u8BA4\u5DE5\u5177\u4F1A\u88AB\u81EA\u52A8\u79FB\u9664\uFF1B\u53EF\u5728\u201C\u63D0\u793A\u8BCD\u4E0E\u5DE5\u5177\u201D\u9875\u7684\u5DE5\u5177\u5217\u8868\u4E2D\u67E5\u770B\u6216\u7F16\u8F91 subagent_role\u3002"
789
885
  ) }),
790
- !roles.length && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-muted", role: "status", children: t("\uC815\uC758\uB41C \uC5ED\uD560\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 \uC0C1\uD0DC\uC5D0\uC11C\uB294 subagent_role\uC774 \uBD80\uBAA8\uC640 \uAC19\uC740 \uBAA8\uB378\xB7\uCD94\uB860\xB7\uC9C0\uCE68\uC73C\uB85C \uC0C8 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.", "No roles defined. subagent_role then starts a new subagent with the same model, reasoning and instructions as the parent.", "\u5C1A\u672A\u5B9A\u4E49\u89D2\u8272\u3002\u6B64\u65F6 subagent_role \u4F1A\u4EE5\u4E0E\u7236\u4EE3\u7406\u76F8\u540C\u7684\u6A21\u578B\u3001\u63A8\u7406\u548C\u6307\u4EE4\u542F\u52A8\u65B0\u7684\u5B50\u4EE3\u7406\u3002") }),
791
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "studio-tool-cards studio-card-pair", children: roles.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "studio-role-card", children: [
792
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-card-head", children: [
793
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: item.name || t("\uC774\uB984 \uC5C6\uC74C", "Untitled", "\u672A\u547D\u540D") }),
794
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Badge, { tone: "outline", children: item.spawn === "fork" ? t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30", "Continue parent", "\u7EE7\u627F\u7236\u5BF9\u8BDD") : t("\uC0C8 \uB300\uD654", "New conversation", "\u65B0\u5BF9\u8BDD") })
886
+ !roles.length && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted", role: "status", children: t("\uC815\uC758\uB41C \uC5ED\uD560\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 \uC0C1\uD0DC\uC5D0\uC11C\uB294 subagent_role\uC774 \uBD80\uBAA8\uC640 \uAC19\uC740 \uBAA8\uB378\xB7\uCD94\uB860\xB7\uC9C0\uCE68\uC73C\uB85C \uC0C8 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.", "No roles defined. subagent_role then starts a new subagent with the same model, reasoning and instructions as the parent.", "\u5C1A\u672A\u5B9A\u4E49\u89D2\u8272\u3002\u6B64\u65F6 subagent_role \u4F1A\u4EE5\u4E0E\u7236\u4EE3\u7406\u76F8\u540C\u7684\u6A21\u578B\u3001\u63A8\u7406\u548C\u6307\u4EE4\u542F\u52A8\u65B0\u7684\u5B50\u4EE3\u7406\u3002") }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "studio-tool-cards studio-card-pair", children: roles.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "studio-role-card", children: [
888
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-card-head", children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: item.name || t("\uC774\uB984 \uC5C6\uC74C", "Untitled", "\u672A\u547D\u540D") }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Badge, { tone: "outline", children: item.spawn === "fork" ? t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30", "Continue parent", "\u7EE7\u627F\u7236\u5BF9\u8BDD") : t("\uC0C8 \uB300\uD654", "New conversation", "\u65B0\u5BF9\u8BDD") })
795
891
  ] }),
796
- item.role && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-card-text studio-mono", children: item.role }),
797
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "studio-card-meta", children: [
892
+ item.role && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-card-text studio-mono", children: item.role }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "studio-card-meta", children: [
798
894
  item.model === "inherit" ? t("\uBD80\uBAA8 \uBAA8\uB378 \uC0AC\uC6A9", "Inherit parent model", "\u7EE7\u627F\u7236\u7EA7\u6A21\u578B") : models.find((model2) => model2.value === item.model)?.label ?? item.model,
799
895
  " \xB7 ",
800
896
  t("\uCD94\uB860", "Reasoning", "\u63A8\u7406"),
801
897
  " ",
802
- item.reasoning === "inherit" ? t("\uAE30\uBCF8", "inherit", "\u9ED8\u8BA4") : item.reasoning
898
+ item.reasoning === "inherit" ? t("\uAE30\uBCF8\uAC12", "default", "\u9ED8\u8BA4") : item.reasoning,
899
+ tools && delegates && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
900
+ " \xB7 Tool Filter: ",
901
+ filterSummary(item)
902
+ ] })
803
903
  ] }),
804
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "studio-card-footer", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, "aria-haspopup": "dialog", onClick: () => setSelected(index), children: t("\uD3B8\uC9D1", "Edit", "\u7F16\u8F91") }) })
904
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "studio-card-footer", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { small: true, "aria-haspopup": "dialog", onClick: () => setSelected(index), children: t("\uD3B8\uC9D1", "Edit", "\u7F16\u8F91") }) })
805
905
  ] }, index)) }),
806
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
906
+ tools && onHiddenToolsChange && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "studio-form-section", children: [
907
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionHead, { title: filterTitle, description: t("Tool Filter\uC5D0 \uB4F1\uB85D\uD55C \uB3C4\uAD6C\uB294 \uC774 \uD504\uB9AC\uC14B\uC758 \uBAA8\uB4E0 \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uBE60\uC9D1\uB2C8\uB2E4. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC758 \uC2DC\uC2A4\uD15C \uD504\uB86C\uD504\uD2B8\uC5D0\uC11C \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uD544\uC694 \uC5C6\uB294 \uB3C4\uAD6C\uB97C \uC81C\uAC70\uD574 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uAC00\uBCCD\uAC8C \uC720\uC9C0\uD574 \uBCF4\uC138\uC694. \uC5ED\uD560 \uD3B8\uC9D1\uC5D0\uC11C \uC5ED\uD560\uB9C8\uB2E4 \uB530\uB85C \uC815\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4.", "Tools in the Tool Filter are removed from every subagent of this preset. Remove tools a subagent does not need from its system prompt to keep its context light. Each role can also set its own list in the role editor.", "\u52A0\u5165 Tool Filter \u7684\u5DE5\u5177\u4F1A\u4ECE\u6B64\u9884\u8BBE\u7684\u6240\u6709\u5B50\u4EE3\u7406\u4E2D\u79FB\u9664\u3002\u4ECE\u5B50\u4EE3\u7406\u7684\u7CFB\u7EDF\u63D0\u793A\u4E2D\u79FB\u9664\u5176\u4E0D\u9700\u8981\u7684\u5DE5\u5177\uFF0C\u8BA9\u4E0A\u4E0B\u6587\u4FDD\u6301\u7CBE\u7B80\u3002\u4E5F\u53EF\u4EE5\u5728\u89D2\u8272\u7F16\u8F91\u4E2D\u4E3A\u6BCF\u4E2A\u89D2\u8272\u5355\u72EC\u8BBE\u7F6E\u3002") }),
908
+ !delegates ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t("\uD504\uB86C\uD504\uD2B8\xB7\uB3C4\uAD6C \uD0ED\uC5D0\uC11C subagent_role\uC744 \uC120\uD0DD\uD558\uBA74 \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Select subagent_role on the Prompts & tools tab to use the Tool Filter.", "\u5728\u201C\u63D0\u793A\u8BCD\u4E0E\u5DE5\u5177\u201D\u9875\u9009\u62E9 subagent_role \u540E\u5373\u53EF\u4F7F\u7528\u3002") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ToolFilterPicker, { label: filterTitle, tools, selected: hiddenTools, onChange: onHiddenToolsChange, t })
909
+ ] }),
910
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
807
911
  DetailDialog,
808
912
  {
809
913
  open: role !== void 0,
810
914
  title: role?.name || t("\uC0C8 \uC5ED\uD560", "New role", "\u65B0\u89D2\u8272"),
811
915
  subtitle: t("\uC5ED\uD560 \uD3B8\uC9D1", "Edit role", "\u7F16\u8F91\u89D2\u8272"),
812
916
  onClose: close,
813
- footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
814
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, onClick: () => {
917
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
918
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { small: true, onClick: () => {
815
919
  onChange(roles.filter((_, index) => index !== selected));
816
920
  close();
817
921
  }, children: t("\uC5ED\uD560 \uC0AD\uC81C", "Remove role", "\u79FB\u9664\u89D2\u8272") }),
818
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { small: true, primary: true, onClick: close, children: t("\uC644\uB8CC", "Done", "\u5B8C\u6210") })
922
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { small: true, primary: true, onClick: close, children: t("\uC644\uB8CC", "Done", "\u5B8C\u6210") })
819
923
  ] }),
820
- children: role && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
821
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-field-grid", children: [
822
- field2(t("\uC774\uB984", "Name", "\u540D\u79F0"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { value: role.name, onChange: (e) => update2({ name: e.target.value }) })),
823
- field2(t("\uC5ED\uD560 ID", "Role", "\u89D2\u8272"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "studio-mono", value: role.role, onChange: (e) => update2({ role: e.target.value }) }))
924
+ children: role && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
925
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-field-grid", children: [
926
+ field2(t("\uC774\uB984", "Name", "\u540D\u79F0"), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { value: role.name, onChange: (e) => update2({ name: e.target.value }) })),
927
+ field2(t("\uC5ED\uD560 ID", "Role ID", "\u89D2\u8272 ID"), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "studio-mono", value: role.role, onChange: (e) => update2({ role: e.target.value }) }))
824
928
  ] }),
825
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "studio-field-grid", children: [
826
- field2(t("\uBAA8\uB378", "Model", "\u6A21\u578B"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.model, onChange: (e) => update2({ model: e.target.value, reasoning: "inherit" }), children: [
827
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "inherit", children: t("\uBD80\uBAA8 \uBAA8\uB378 \uC0AC\uC6A9", "Inherit parent model", "\u7EE7\u627F\u7236\u7EA7\u6A21\u578B") }),
828
- role.model !== "inherit" && !model && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: role.model, disabled: true, children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-field-grid", children: [
930
+ field2(t("\uBAA8\uB378", "Model", "\u6A21\u578B"), /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: role.model, onChange: (e) => update2({ model: e.target.value, reasoning: "inherit" }), children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "inherit", children: t("\uBD80\uBAA8 \uBAA8\uB378 \uC0AC\uC6A9", "Inherit parent model", "\u7EE7\u627F\u7236\u7EA7\u6A21\u578B") }),
932
+ role.model !== "inherit" && !model && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("option", { value: role.model, disabled: true, children: [
829
933
  role.model,
830
934
  " \xB7 ",
831
- t("\uBAA9\uB85D\uC5D0 \uC5C6\uC74C \xB7 \uBC18\uC601\uD560 \uB54C \uD655\uC778", "Not confirmed in catalog \xB7 checked on apply", "\u76EE\u5F55\u4E2D\u672A\u786E\u8BA4 \xB7 \u5E94\u7528\u65F6\u9A8C\u8BC1")
935
+ t("\uBAA9\uB85D\uC5D0 \uC5C6\uC74C \xB7 \uBC18\uC601\uD560 \uB54C \uD655\uC778", "Not in list \xB7 checked on apply", "\u76EE\u5F55\u4E2D\u672A\u786E\u8BA4 \xB7 \u5E94\u7528\u65F6\u9A8C\u8BC1")
832
936
  ] }),
833
- models.map((model2) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: model2.value, children: model2.label }, model2.value))
937
+ models.map((model2) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: model2.value, children: model2.label }, model2.value))
834
938
  ] })),
835
- field2(t("\uCD94\uB860 \uC218\uC900", "Reasoning", "\u63A8\u7406\u7EA7\u522B"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.reasoning, disabled: !model, onChange: (e) => update2({ reasoning: e.target.value }), children: [
836
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "inherit", children: role.model === "inherit" ? t("\uBD80\uBAA8 \uC124\uC815 \uC0AC\uC6A9", "Inherit parent setting", "\u7EE7\u627F\u7236\u7EA7\u8BBE\u7F6E") : t("\uBAA8\uB378 \uAE30\uBCF8\uAC12", "Model default", "\u6A21\u578B\u9ED8\u8BA4\u503C") }),
837
- role.reasoning !== "inherit" && !model?.reasoning?.efforts.some((e) => e.id === role.reasoning) && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: role.reasoning, disabled: true, children: [
939
+ field2(t("\uCD94\uB860 \uC218\uC900", "Reasoning", "\u63A8\u7406\u7EA7\u522B"), /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: role.reasoning, disabled: !model, onChange: (e) => update2({ reasoning: e.target.value }), children: [
940
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "inherit", children: role.model === "inherit" ? t("\uBD80\uBAA8 \uC124\uC815 \uC0AC\uC6A9", "Inherit parent setting", "\u7EE7\u627F\u7236\u7EA7\u8BBE\u7F6E") : t("\uBAA8\uB378 \uAE30\uBCF8\uAC12", "Model default", "\u6A21\u578B\u9ED8\u8BA4\u503C") }),
941
+ role.reasoning !== "inherit" && !model?.reasoning?.efforts.some((e) => e.id === role.reasoning) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("option", { value: role.reasoning, disabled: true, children: [
838
942
  role.reasoning,
839
943
  " \xB7 ",
840
- t("\uC9C0\uC6D0 \uC5EC\uBD80 \uD655\uC778 \uD544\uC694", "Check support", "\u9700\u786E\u8BA4\u652F\u6301")
944
+ t("\uC9C0\uC6D0 \uC5EC\uBD80 \uD655\uC778 \uD544\uC694", "Support unconfirmed", "\u9700\u786E\u8BA4\u652F\u6301")
841
945
  ] }),
842
- model?.reasoning?.efforts.map((e) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: e.id, children: e.name }, e.id))
946
+ model?.reasoning?.efforts.map((e) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: e.id, children: e.name }, e.id))
843
947
  ] })),
844
- field2(t("\uC2DC\uC791 \uBC29\uC2DD", "Spawn mode", "\u751F\u6210\u65B9\u5F0F"), /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { value: role.spawn, onChange: (e) => update2({ spawn: e.target.value }), children: [
845
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "fresh", children: t("\uC0C8 \uB300\uD654 (Fresh)", "New conversation (Fresh)", "\u65B0\u5BF9\u8BDD\uFF08Fresh\uFF09") }),
846
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "fork", children: t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30 (Fork)", "Continue parent (Fork)", "\u7EE7\u627F\u7236\u5BF9\u8BDD\uFF08Fork\uFF09") })
948
+ field2(t("\uC2DC\uC791 \uBC29\uC2DD", "Spawn mode", "\u751F\u6210\u65B9\u5F0F"), /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { value: role.spawn, onChange: (e) => update2({ spawn: e.target.value }), children: [
949
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "fresh", children: t("\uC0C8 \uB300\uD654 (Fresh)", "New conversation (Fresh)", "\u65B0\u5BF9\u8BDD\uFF08Fresh\uFF09") }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "fork", children: t("\uBD80\uBAA8 \uB300\uD654 \uC774\uC5B4\uBC1B\uAE30 (Fork)", "Continue parent (Fork)", "\u7EE7\u627F\u7236\u5BF9\u8BDD\uFF08Fork\uFF09") })
847
951
  ] }))
848
952
  ] }),
849
- field2(t("\uC5ED\uD560 \uC9C0\uCE68 (persona)", "User instructions (persona)", "\u7528\u6237\u6307\u4EE4\uFF08persona\uFF09"), /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { rows: 6, value: role.persona, placeholder: defaultPersona, onChange: (e) => update2({ persona: e.target.value }) })),
850
- !role.persona && defaultPersona && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "studio-muted studio-hint", children: t("\uD750\uB9B0 \uAE00\uC528\uB294 \uD504\uB9AC\uC14B\uC758 \uAE30\uBCF8 persona\uC785\uB2C8\uB2E4. \uBE44\uC6CC \uB450\uBA74 \uC774 \uB0B4\uC6A9\uC774 \uADF8\uB300\uB85C \uC4F0\uC785\uB2C8\uB2E4.", "The dimmed text is the preset default persona; it is used while this field is empty.", "\u6D45\u8272\u6587\u5B57\u662F\u9884\u8BBE\u7684\u9ED8\u8BA4 persona\uFF0C\u7559\u7A7A\u65F6\u4F7F\u7528\u8BE5\u5185\u5BB9\u3002") })
953
+ tools && delegates && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-field", children: [
954
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: filterTitle }),
955
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": roleFilterLabel, value: role.toolFilter?.mode ?? "default", onChange: (e) => {
956
+ const mode = e.target.value;
957
+ update2({ toolFilter: mode === "extend" || mode === "replace" ? { mode, deny: [] } : void 0 });
958
+ }, children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "default", children: t(`\uD504\uB9AC\uC14B \uAE30\uBCF8\uAC12 \uC0AC\uC6A9 (${defaultCount}\uAC1C)`, `Use preset default (${defaultCount})`, `\u4F7F\u7528\u9884\u8BBE\u9ED8\u8BA4\u503C\uFF08${defaultCount} \u4E2A\uFF09`) }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "extend", children: t("\uAE30\uBCF8\uAC12\uC5D0 \uCD94\uAC00", "Add to preset default", "\u5728\u9ED8\u8BA4\u503C\u57FA\u7840\u4E0A\u6DFB\u52A0") }),
961
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "replace", children: t("\uC774 \uC5ED\uD560\uB9CC \uB530\uB85C (\uAE30\uBCF8\uAC12 \uBB34\uC2DC)", "This role only (ignore default)", "\u4EC5\u6B64\u89D2\u8272\uFF08\u5FFD\u7565\u9ED8\u8BA4\u503C\uFF09") })
962
+ ] }),
963
+ role.toolFilter?.mode === "extend" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted studio-hint", children: t("\uD504\uB9AC\uC14B \uAE30\uBCF8\uAC12\uC758 \uB3C4\uAD6C\uB294 \uC7A0\uAE34 \uCC44\uB85C \uD568\uAED8 \uBE60\uC9D1\uB2C8\uB2E4. \uC774 \uC5ED\uD560\uC5D0\uC11C \uCD94\uAC00\uB85C \uBE84 \uB3C4\uAD6C\uB97C \uACE0\uB974\uC138\uC694. \uAE30\uBCF8\uAC12\uC744 \uBC14\uAFB8\uBA74 \uC774 \uC5ED\uD560\uC5D0\uB3C4 \uBC18\uC601\uB429\uB2C8\uB2E4.", "Preset default tools are locked and stay filtered. Choose the tools this role filters in addition. Changes to the default apply to this role too.", "\u9884\u8BBE\u9ED8\u8BA4\u503C\u4E2D\u7684\u5DE5\u5177\u5DF2\u9501\u5B9A\u5E76\u4E00\u5E76\u6392\u9664\u3002\u8BF7\u9009\u62E9\u6B64\u89D2\u8272\u989D\u5916\u6392\u9664\u7684\u5DE5\u5177\u3002\u9ED8\u8BA4\u503C\u7684\u66F4\u6539\u4E5F\u4F1A\u5E94\u7528\u5230\u6B64\u89D2\u8272\u3002") }),
964
+ role.toolFilter?.mode === "replace" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted studio-hint", children: t("\uD504\uB9AC\uC14B \uAE30\uBCF8\uAC12\uC774 \uC774 \uC5ED\uD560\uC5D0\uB294 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC774 \uC5ED\uD560\uC5D0\uC11C \uBE84 \uB3C4\uAD6C\uB9CC \uACE0\uB974\uC138\uC694.", "The preset default does not apply to this role. Choose only the tools this role filters.", "\u9884\u8BBE\u9ED8\u8BA4\u503C\u4E0D\u9002\u7528\u4E8E\u6B64\u89D2\u8272\u3002\u8BF7\u4EC5\u9009\u62E9\u6B64\u89D2\u8272\u6392\u9664\u7684\u5DE5\u5177\u3002") }),
965
+ role.toolFilter && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
966
+ ToolFilterPicker,
967
+ {
968
+ label: roleFilterLabel,
969
+ tools,
970
+ selected: role.toolFilter.deny,
971
+ locked: role.toolFilter.mode === "extend" ? hiddenTools : [],
972
+ lockedReason: t("\uD504\uB9AC\uC14B \uAE30\uBCF8\uAC12", "Preset default", "\u9884\u8BBE\u9ED8\u8BA4\u503C"),
973
+ onChange: (deny) => update2({ toolFilter: { mode: role.toolFilter.mode, deny } }),
974
+ t
975
+ }
976
+ )
977
+ ] }),
978
+ field2(t("\uC5ED\uD560 \uC9C0\uCE68 (persona)", "Role instructions (persona)", "\u89D2\u8272\u6307\u4EE4\uFF08persona\uFF09"), /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("textarea", { rows: 6, value: role.persona, placeholder: defaultPersona, onChange: (e) => update2({ persona: e.target.value }) })),
979
+ !role.persona && defaultPersona && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted studio-hint", children: t("\uD750\uB9B0 \uAE00\uC528\uB294 \uD504\uB9AC\uC14B\uC758 \uAE30\uBCF8 persona\uC785\uB2C8\uB2E4. \uBE44\uC6CC \uB450\uBA74 \uC774 \uB0B4\uC6A9\uC774 \uADF8\uB300\uB85C \uC4F0\uC785\uB2C8\uB2E4.", "The dimmed text is the preset default persona; it is used while this field is empty.", "\u6D45\u8272\u6587\u5B57\u662F\u9884\u8BBE\u7684\u9ED8\u8BA4 persona\uFF0C\u7559\u7A7A\u65F6\u4F7F\u7528\u8BE5\u5185\u5BB9\u3002") })
851
980
  ] })
852
981
  }
853
982
  )
854
983
  ] });
855
984
  }
985
+ function ToolFilterPicker({ label, tools, selected, onChange, t, locked = [], lockedReason }) {
986
+ const items = tools.map((tool) => locked.includes(tool.name) && lockedReason ? { ...tool, lockedReason } : tool);
987
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
988
+ ItemPicker,
989
+ {
990
+ label,
991
+ items,
992
+ selected: selected.filter((name) => tools.some((tool) => tool.name === name) && !locked.includes(name)),
993
+ onChange: (names2) => onChange(names2.filter((name) => !locked.includes(name))),
994
+ hasDetails: () => false,
995
+ labels: { filter: t("\uBCF4\uAE30", "Show", "\u663E\u793A"), search: t("\uB3C4\uAD6C \uAC80\uC0C9", "Search tools", "\u641C\u7D22\u5DE5\u5177"), searchHint: t("\uC774\uB984, \uC124\uBA85\uC73C\uB85C \uAC80\uC0C9", "Search names or descriptions", "\u6309\u540D\u79F0\u6216\u63CF\u8FF0\u641C\u7D22"), all: t("\uC804\uCCB4", "All", "\u5168\u90E8"), selected: t("\uC81C\uC678\uB428", "Filtered", "\u5DF2\u6392\u9664"), modified: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), settings: t("\uC124\uC815", "Details", "\u8BBE\u7F6E"), done: t("\uC644\uB8CC", "Close", "\u5B8C\u6210"), empty: t("\uC774 \uD504\uB9AC\uC14B\uC5D0\uC11C \uC120\uD0DD\uD55C \uB3C4\uAD6C\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", "This preset selects no tools.", "\u6B64\u9884\u8BBE\u672A\u9009\u62E9\u4EFB\u4F55\u5DE5\u5177\u3002"), noResults: t("\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", "No matching results.", "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\u3002"), modifiedTag: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), clear: t("\uC804\uCCB4 \uBCF4\uAE30", "Show all", "\u663E\u793A\u5168\u90E8") }
996
+ }
997
+ );
998
+ }
856
999
 
857
1000
  // src/optimization-prototype.tsx
858
1001
  var import_react9 = __toESM(require("react"), 1);
859
1002
 
860
1003
  // src/master-extensions-editor.tsx
861
- var import_react3 = __toESM(require("react"), 1);
862
- var import_jsx_runtime3 = require("react/jsx-runtime");
1004
+ var import_react4 = __toESM(require("react"), 1);
1005
+ var import_jsx_runtime4 = require("react/jsx-runtime");
863
1006
  function MasterExtensionsEditor({ locale, actions }) {
864
- const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
865
- const [text, setText] = import_react3.default.useState("[]");
866
- const [error, setError] = import_react3.default.useState("");
867
- const [busy, setBusy] = import_react3.default.useState(false);
868
- const [loaded, setLoaded] = import_react3.default.useState(false);
869
- import_react3.default.useEffect(() => {
1007
+ const t = (ko3, en3, zh3) => locale.startsWith("ko") ? ko3 : locale.startsWith("zh") ? zh3 : en3;
1008
+ const [text, setText] = import_react4.default.useState("[]");
1009
+ const [error, setError] = import_react4.default.useState("");
1010
+ const [busy, setBusy] = import_react4.default.useState(false);
1011
+ const [loaded, setLoaded] = import_react4.default.useState(false);
1012
+ import_react4.default.useEffect(() => {
870
1013
  let active = true;
871
1014
  if (actions) actions.read().then((value) => {
872
1015
  if (active) {
@@ -880,22 +1023,22 @@ function MasterExtensionsEditor({ locale, actions }) {
880
1023
  active = false;
881
1024
  };
882
1025
  }, [actions]);
883
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "studio-page-stack", children: [
884
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageHead, { title: t("\uB3C4\uAD6C \uC81C\uACF5\uC790", "Tool providers", "\u5DE5\u5177\u63D0\u4F9B\u8005"), description: t("\uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778\uC758 \uB3C4\uAD6C\uB97C Studio \uB3C4\uAD6C \uBAA9\uB85D\uC5D0 \uB4F1\uB85D\uD569\uB2C8\uB2E4. \uB4F1\uB85D\uD55C \uB3C4\uAD6C\uB294 \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Register installed plugin tools in the Studio tool list so presets can select them.", "\u5C06\u5DF2\u5B89\u88C5\u63D2\u4EF6\u7684\u5DE5\u5177\u6CE8\u518C\u5230 Studio \u5DE5\u5177\u5217\u8868\uFF0C\u4F9B\u9884\u8BBE\u9009\u62E9") }),
885
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t("\uB4F1\uB85D\uD558\uBA74 \uD50C\uB7EC\uADF8\uC778 \uCF54\uB4DC\uAC00 \uC2E4\uD589\uB418\uBBC0\uB85C \uC2E0\uB8B0\uD558\uB294 \uD50C\uB7EC\uADF8\uC778\uB9CC \uCD94\uAC00\uD558\uC138\uC694. \uD328\uD0A4\uC9C0\uB97C \uC124\uCE58\uD558\uC9C0\uB294 \uC54A\uC73C\uBA70, \uC124\uC815\uC740 \uC774 \uCEF4\uD4E8\uD130\uC5D0 \uD3C9\uBB38\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4.", "Register installed provider compositions as JSON. This does not install packages or generate tool code. Registration executes plugin code: add trusted providers only. Settings are stored locally as plaintext. Applied presets are not automatically changed.", "\u4EE5 JSON \u6CE8\u518C\u5DF2\u5B89\u88C5\u7684\u63D0\u4F9B\u8005\u914D\u7F6E\u3002\u4E0D\u5B89\u88C5\u8F6F\u4EF6\u5305\u6216\u751F\u6210\u5DE5\u5177\u4EE3\u7801\u3002\u6CE8\u518C\u4F1A\u6267\u884C\u63D2\u4EF6\u4EE3\u7801\uFF0C\u8BF7\u4EC5\u6DFB\u52A0\u53EF\u4FE1\u63D0\u4F9B\u8005\u3002\u8BBE\u7F6E\u4EE5\u660E\u6587\u4FDD\u5B58\u5728\u672C\u5730\u3002\u5DF2\u5E94\u7528\u7684\u9884\u8BBE\u4E0D\u4F1A\u81EA\u52A8\u6539\u53D8\u3002") }),
886
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("ol", { className: "studio-steps", children: [
887
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uD50C\uB7EC\uADF8\uC778\uC774 \uD604\uC7AC DSH \uD504\uB85C\uD544\uC5D0 \uC124\uCE58\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.", "Ensure the plugin is installed in this DSH profile. Packages are not installed automatically.", "\u786E\u8BA4\u63D2\u4EF6\u5DF2\u5B89\u88C5\u5728\u5F53\u524D DSH \u914D\u7F6E\u4E2D\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002") }),
888
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uC544\uB798 \uBC30\uC5F4\uC5D0 id(\uACE0\uC720\uD55C \uC18C\uBB38\uC790 \uC2DD\uBCC4\uC790), name(\uD328\uD0A4\uC9C0 \uC774\uB984), config(\uD50C\uB7EC\uADF8\uC778 \uC124\uC815)\uB97C \uC785\uB825\uD569\uB2C8\uB2E4.", "Add an entry with id (unique lowercase identifier), name (exact package name), and config (public plugin settings).", "\u5728\u6570\u7EC4\u4E2D\u586B\u5199 id\uFF08\u552F\u4E00\u5C0F\u5199\u6807\u8BC6\u7B26\uFF09\u3001name\uFF08\u51C6\u786E\u7684\u8F6F\u4EF6\u5305\u540D\uFF09\u548C config\uFF08\u63D2\u4EF6\u516C\u5F00\u8BBE\u7F6E\uFF09\u3002") }),
889
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: t("\uD655\uC778 \uD6C4 \uB4F1\uB85D\uC744 \uB204\uB974\uBA74 \uB3C4\uAD6C \uBAA9\uB85D\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4. \uD544\uC694\uD55C \uB3C4\uAD6C\uB97C \uACE8\uB77C \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uC138\uC694.", "Validate and update master to refresh the tool list, then select the tools and save your preset.", "\u9A8C\u8BC1\u5E76\u66F4\u65B0\u4E3B\u9884\u8BBE\u540E\uFF0C\u9009\u62E9\u6240\u9700\u5DE5\u5177\u5E76\u4FDD\u5B58\u9884\u8BBE\u3002") })
1026
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "studio-page-stack", children: [
1027
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PageHead, { title: t("\uB3C4\uAD6C \uC81C\uACF5\uC790", "Tool providers", "\u5DE5\u5177\u63D0\u4F9B\u8005"), description: t("\uC124\uCE58\uB41C \uD50C\uB7EC\uADF8\uC778\uC758 \uB3C4\uAD6C\uB97C Studio \uB3C4\uAD6C \uBAA9\uB85D\uC5D0 \uB4F1\uB85D\uD569\uB2C8\uB2E4. \uB4F1\uB85D\uD55C \uB3C4\uAD6C\uB294 \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uACE0\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Register installed plugin tools in the Studio tool list so presets can select them.", "\u5C06\u5DF2\u5B89\u88C5\u63D2\u4EF6\u7684\u5DE5\u5177\u6CE8\u518C\u5230 Studio \u5DE5\u5177\u5217\u8868\uFF0C\u4F9B\u9884\u8BBE\u9009\u62E9") }),
1028
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Note, { children: t("\uB4F1\uB85D\uD558\uBA74 \uD50C\uB7EC\uADF8\uC778 \uCF54\uB4DC\uAC00 \uC2E4\uD589\uB418\uBBC0\uB85C \uC2E0\uB8B0\uD558\uB294 \uD50C\uB7EC\uADF8\uC778\uB9CC \uCD94\uAC00\uD558\uC138\uC694. \uD328\uD0A4\uC9C0\uB97C \uC124\uCE58\uD558\uC9C0\uB294 \uC54A\uC73C\uBA70, \uC124\uC815\uC740 \uC774 \uCEF4\uD4E8\uD130\uC5D0 \uD3C9\uBB38\uC73C\uB85C \uC800\uC7A5\uB429\uB2C8\uB2E4. \uC774\uBBF8 \uBC18\uC601\uD55C \uD504\uB9AC\uC14B\uC740 \uC790\uB3D9\uC73C\uB85C \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Registering runs the plugin\u2019s code, so add only plugins you trust. Studio does not install packages, and these settings are stored on this computer in plain text. Presets you already applied do not change automatically.", "\u6CE8\u518C\u4F1A\u6267\u884C\u63D2\u4EF6\u4EE3\u7801\uFF0C\u8BF7\u4EC5\u6DFB\u52A0\u53EF\u4FE1\u63D2\u4EF6\u3002Studio \u4E0D\u4F1A\u5B89\u88C5\u8F6F\u4EF6\u5305\uFF0C\u8BBE\u7F6E\u4EE5\u660E\u6587\u4FDD\u5B58\u5728\u672C\u673A\u3002\u5DF2\u5E94\u7528\u7684\u9884\u8BBE\u4E0D\u4F1A\u81EA\u52A8\u6539\u53D8\u3002") }),
1029
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ol", { className: "studio-steps", children: [
1030
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: t("\uD50C\uB7EC\uADF8\uC778\uC774 \uD604\uC7AC DSH \uD504\uB85C\uD544\uC5D0 \uC124\uCE58\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.", "Check that the plugin is installed in this DSH profile.", "\u786E\u8BA4\u63D2\u4EF6\u5DF2\u5B89\u88C5\u5728\u5F53\u524D DSH \u914D\u7F6E\u4E2D\uFF0C\u4E0D\u4F1A\u81EA\u52A8\u5B89\u88C5\u3002") }),
1031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: t("\uC544\uB798 \uBC30\uC5F4\uC5D0 id(\uACE0\uC720\uD55C \uC18C\uBB38\uC790 \uC2DD\uBCC4\uC790), name(\uD328\uD0A4\uC9C0 \uC774\uB984), config(\uD50C\uB7EC\uADF8\uC778 \uC124\uC815)\uB97C \uC785\uB825\uD569\uB2C8\uB2E4.", "Add an entry with id (unique lowercase identifier), name (exact package name), and config (public plugin settings).", "\u5728\u6570\u7EC4\u4E2D\u586B\u5199 id\uFF08\u552F\u4E00\u5C0F\u5199\u6807\u8BC6\u7B26\uFF09\u3001name\uFF08\u51C6\u786E\u7684\u8F6F\u4EF6\u5305\u540D\uFF09\u548C config\uFF08\u63D2\u4EF6\u516C\u5F00\u8BBE\u7F6E\uFF09\u3002") }),
1032
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children: t("\uD655\uC778 \uD6C4 \uB4F1\uB85D\uC744 \uB204\uB974\uBA74 \uB3C4\uAD6C \uBAA9\uB85D\uC774 \uAC31\uC2E0\uB429\uB2C8\uB2E4. \uD544\uC694\uD55C \uB3C4\uAD6C\uB97C \uACE8\uB77C \uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uC138\uC694.", "Select Validate and update master to refresh the tool list, then choose the tools and save your preset.", "\u9A8C\u8BC1\u5E76\u66F4\u65B0\u4E3B\u9884\u8BBE\u540E\uFF0C\u9009\u62E9\u6240\u9700\u5DE5\u5177\u5E76\u4FDD\u5B58\u9884\u8BBE\u3002") })
890
1033
  ] }),
891
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "studio-muted", children: t("\uC608\uC2DC: \uC124\uCE58\uB41C subagent \uD50C\uB7EC\uADF8\uC778\uC744 \uB2E4\uB978 \uB3C4\uAD6C \uC774\uB984\uC73C\uB85C \uB4F1\uB85D", "Example: register the installed subagent provider under a distinct tool name", "\u793A\u4F8B\uFF1A\u4F7F\u7528\u4E0D\u540C\u5DE5\u5177\u540D\u6CE8\u518C\u5DF2\u5B89\u88C5\u7684 subagent \u63D0\u4F9B\u8005") }),
892
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { children: JSON.stringify([{ id: "extra-agent", name: "@deepseek-ai/dsh-tool-subagent", config: { provider: "spawn", toolName: "studio_extra_agent" } }], null, 2) }),
893
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Note, { children: t("\uBAA9\uB85D \uC804\uCCB4\uAC00 \uC0C8 \uB0B4\uC6A9\uC73C\uB85C \uBC14\uB01D\uB2C8\uB2E4. \uAE30\uC874 \uD56D\uBAA9\uC744 \uC720\uC9C0\uD558\uB824\uBA74 \uBC30\uC5F4\uC5D0 \uB0A8\uACA8 \uB450\uACE0, \uBAA8\uB450 \uC9C0\uC6B0\uB824\uBA74 []\uB97C \uB4F1\uB85D\uD558\uC138\uC694. \uB3C4\uAD6C \uC774\uB984\uC774 \uACB9\uCE58\uC9C0 \uC54A\uAC8C \uD558\uC138\uC694.", "Saving replaces the entire extension list. Keep existing entries to retain them; [] removes all added providers only. Avoid existing tool names. Missing plugin service dependencies can prevent activation.", "\u4FDD\u5B58\u4F1A\u66FF\u6362\u6574\u4E2A\u9644\u52A0\u63D0\u4F9B\u8005\u5217\u8868\u3002\u4FDD\u7559\u5DF2\u6709\u6761\u76EE\u4EE5\u514D\u5220\u9664\uFF1B\u4FDD\u5B58 [] \u4EC5\u79FB\u9664\u5168\u90E8\u9644\u52A0\u63D0\u4F9B\u8005\u3002\u907F\u514D\u5DE5\u5177\u91CD\u540D\uFF1B\u7F3A\u5C11\u4F9D\u8D56\u670D\u52A1\u53EF\u80FD\u5BFC\u81F4\u6FC0\u6D3B\u5931\u8D25\u3002") }),
894
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "studio-field", children: [
895
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("\uB4F1\uB85D \uBAA9\uB85D (JSON)", "Provider composition", "\u63D0\u4F9B\u8005\u914D\u7F6E") }),
896
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("textarea", { rows: 6, className: "studio-mono", value: text, disabled: !loaded || busy, onChange: (event) => setText(event.target.value) })
1034
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "studio-muted", children: t("\uC608\uC2DC: \uC124\uCE58\uB41C subagent \uD50C\uB7EC\uADF8\uC778\uC744 \uB2E4\uB978 \uB3C4\uAD6C \uC774\uB984\uC73C\uB85C \uB4F1\uB85D", "Example: register the installed subagent provider under a distinct tool name", "\u793A\u4F8B\uFF1A\u4F7F\u7528\u4E0D\u540C\u5DE5\u5177\u540D\u6CE8\u518C\u5DF2\u5B89\u88C5\u7684 subagent \u63D0\u4F9B\u8005") }),
1035
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { children: JSON.stringify([{ id: "extra-agent", name: "@deepseek-ai/dsh-tool-subagent", config: { provider: "spawn", toolName: "studio_extra_agent" } }], null, 2) }),
1036
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Note, { children: t("\uBAA9\uB85D \uC804\uCCB4\uAC00 \uC0C8 \uB0B4\uC6A9\uC73C\uB85C \uBC14\uB01D\uB2C8\uB2E4. \uAE30\uC874 \uD56D\uBAA9\uC744 \uC720\uC9C0\uD558\uB824\uBA74 \uBC30\uC5F4\uC5D0 \uB0A8\uACA8 \uB450\uACE0, \uBAA8\uB450 \uC9C0\uC6B0\uB824\uBA74 []\uB97C \uB4F1\uB85D\uD558\uC138\uC694. \uB3C4\uAD6C \uC774\uB984\uC774 \uACB9\uCE58\uC9C0 \uC54A\uAC8C \uD558\uC138\uC694. \uD544\uC694\uD55C \uC11C\uBE44\uC2A4\uAC00 \uC5C6\uB294 \uD50C\uB7EC\uADF8\uC778\uC740 \uD65C\uC131\uD654\uB418\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Registering replaces the whole list. Keep existing entries in the array to retain them, or register [] to remove them all. Avoid tool names that already exist. A plugin whose required services are missing may fail to activate.", "\u6CE8\u518C\u4F1A\u66FF\u6362\u6574\u4E2A\u5217\u8868\u3002\u4FDD\u7559\u5DF2\u6709\u6761\u76EE\u4EE5\u514D\u5220\u9664\uFF1B\u6CE8\u518C [] \u4F1A\u79FB\u9664\u5168\u90E8\u6761\u76EE\u3002\u907F\u514D\u5DE5\u5177\u91CD\u540D\uFF1B\u7F3A\u5C11\u4F9D\u8D56\u670D\u52A1\u7684\u63D2\u4EF6\u53EF\u80FD\u65E0\u6CD5\u6FC0\u6D3B\u3002") }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "studio-field", children: [
1038
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("\uB4F1\uB85D \uBAA9\uB85D (JSON)", "Provider list (JSON)", "\u63D0\u4F9B\u8005\u5217\u8868\uFF08JSON\uFF09") }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("textarea", { rows: 6, className: "studio-mono", value: text, disabled: !loaded || busy, onChange: (event) => setText(event.target.value) })
897
1040
  ] }),
898
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "studio-inline-actions", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { disabled: !loaded || busy, onClick: async () => {
1041
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "studio-inline-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { disabled: !loaded || busy, onClick: async () => {
899
1042
  if (!actions) return;
900
1043
  setBusy(true);
901
1044
  setError("");
@@ -907,10 +1050,35 @@ function MasterExtensionsEditor({ locale, actions }) {
907
1050
  setBusy(false);
908
1051
  }
909
1052
  }, children: t("\uD655\uC778 \uD6C4 \uB4F1\uB85D", "Validate and update master", "\u9A8C\u8BC1\u5E76\u66F4\u65B0\u4E3B\u9884\u8BBE") }) }),
910
- error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "alert", children: error })
1053
+ error && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "alert", children: error })
911
1054
  ] });
912
1055
  }
913
1056
 
1057
+ // src/package-name.ts
1058
+ var PACKAGE_NAME = "@matamt-dbg/dsh-studio";
1059
+ var POLICY_PLUGIN = `${PACKAGE_NAME}/policy`;
1060
+ var ROLE_PLUGIN = `${PACKAGE_NAME}/roles`;
1061
+ var BATCH_PLUGIN = `${PACKAGE_NAME}/batch`;
1062
+ var STUDIO_VERSION = "0.2.8";
1063
+
1064
+ // src/delegation.ts
1065
+ var ROLE_TOOL = "subagent_role";
1066
+ var LEGACY_ROLE_TOOL = "studio_role";
1067
+ var GENERIC_DELEGATION_TOOLS = ["subagent", "subagent_fork", "list_subagent_models"];
1068
+ var SUBAGENT_PLUGIN = "@deepseek-ai/dsh-tool-subagent";
1069
+ var isDelegationPlugin = (name) => name === SUBAGENT_PLUGIN || name === ROLE_PLUGIN;
1070
+ var WITHDRAWN_TOOLS = ["task_run"];
1071
+ function delegationTools(tools, hasRoles = false) {
1072
+ const delegates = hasRoles || tools.some((name) => name === ROLE_TOOL || name === LEGACY_ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name));
1073
+ const rest = tools.filter((name) => name !== ROLE_TOOL && name !== LEGACY_ROLE_TOOL && !GENERIC_DELEGATION_TOOLS.includes(name) && !WITHDRAWN_TOOLS.includes(name));
1074
+ return delegates ? [...rest, ROLE_TOOL] : rest;
1075
+ }
1076
+ function renameLegacyRoleKey(entries) {
1077
+ if (!entries || !Object.hasOwn(entries, LEGACY_ROLE_TOOL)) return entries;
1078
+ const { [LEGACY_ROLE_TOOL]: legacy, ...rest } = entries;
1079
+ return Object.hasOwn(rest, ROLE_TOOL) ? rest : { ...rest, [ROLE_TOOL]: legacy };
1080
+ }
1081
+
914
1082
  // src/config-editor.tsx
915
1083
  var import_react5 = __toESM(require("react"), 1);
916
1084
 
@@ -924,63 +1092,6 @@ function hasChangedOverride(base, override) {
924
1092
  return JSON.stringify(base) !== JSON.stringify(override);
925
1093
  }
926
1094
 
927
- // src/item-picker.tsx
928
- var import_react4 = __toESM(require("react"), 1);
929
- var import_jsx_runtime4 = require("react/jsx-runtime");
930
- function ItemPicker({ label, items, selected, onChange, labels, renderDetails, isModified, hasDetails }) {
931
- const [query, setQuery] = import_react4.default.useState("");
932
- const [filter, setFilter] = import_react4.default.useState("all");
933
- const [detail, setDetail] = import_react4.default.useState();
934
- const chosen = new Set(selected);
935
- const modified = (name) => chosen.has(name) && (isModified?.(name) ?? false);
936
- const text = query.trim().toLowerCase();
937
- const visible = items.filter((item) => (filter === "all" || (filter === "selected" ? chosen.has(item.name) : modified(item.name))) && (!text || `${item.name}
938
- ${item.title ?? ""}
939
- ${item.description}
940
- ${item.searchText ?? ""}`.toLowerCase().includes(text)));
941
- const counts = { all: items.length, selected: items.filter((item) => chosen.has(item.name)).length, modified: items.filter((item) => modified(item.name)).length };
942
- const toggle = (name) => onChange(chosen.has(name) ? selected.filter((value) => value !== name) : [...selected, name]);
943
- const open = items.find((item) => item.name === detail && chosen.has(item.name));
944
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "studio-picker", children: [
945
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "studio-picker-bar", children: [
946
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "search", "aria-label": labels.search, placeholder: labels.searchHint, value: query, onChange: (event) => setQuery(event.target.value) }),
947
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "studio-picker-filters", role: "radiogroup", "aria-label": labels.filter, children: ["all", "selected", "modified"].map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", role: "radio", "aria-checked": filter === value, className: "studio-picker-filter", onClick: () => setFilter(value), children: [
948
- labels[value],
949
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-count", children: counts[value] })
950
- ] }, value)) })
951
- ] }),
952
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ul", { className: "studio-picker-list", "aria-label": label, children: [
953
- visible.map((item) => {
954
- const on = chosen.has(item.name);
955
- const blocked = !on && item.disabledReason !== void 0;
956
- const details = on && renderDetails !== void 0 && (hasDetails?.(item.name) ?? true);
957
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "studio-picker-row", "data-selected": on, children: [
958
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", role: "switch", "aria-checked": on, disabled: blocked, className: "studio-picker-toggle", "aria-label": `${item.title ?? item.name}`, onClick: () => toggle(item.name), children: [
959
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-check", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M3.5 8.5l3 3 6-7", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
960
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "studio-picker-text", children: [
961
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "studio-picker-title", children: [
962
- item.title ?? item.name,
963
- item.subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-subtitle studio-mono", children: item.subtitle })
964
- ] }),
965
- (item.disabledReason && !on ? item.disabledReason : item.description) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-picker-description", children: item.disabledReason && !on ? item.disabledReason : item.description })
966
- ] })
967
- ] }),
968
- modified(item.name) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "studio-badge", "data-tone": "info", "data-studio-modified": "true", children: labels.modifiedTag }),
969
- details && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { small: true, "aria-label": `${labels.settings}: ${item.name}`, "aria-haspopup": "dialog", onClick: () => setDetail(item.name), children: labels.settings })
970
- ] }, item.name);
971
- }),
972
- !visible.length && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "studio-picker-empty", role: "status", children: [
973
- text ? labels.noResults : labels.empty,
974
- (text || filter !== "all") && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { small: true, onClick: () => {
975
- setQuery("");
976
- setFilter("all");
977
- }, children: labels.clear })
978
- ] })
979
- ] }),
980
- renderDetails && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DetailDialog, { open: open !== void 0, title: open?.title ?? open?.name, subtitle: open?.subtitle, closeLabel: labels.done, onClose: () => setDetail(void 0), children: open && renderDetails(open.name) })
981
- ] });
982
- }
983
-
984
1095
  // src/config-editor.tsx
985
1096
  var import_jsx_runtime5 = require("react/jsx-runtime");
986
1097
  var copy = {
@@ -988,10 +1099,11 @@ var copy = {
988
1099
  list: "Configs",
989
1100
  reset: "Reset override",
990
1101
  unavailable: "Schema unavailable; editing is disabled.",
1102
+ delegated: "Merged into subagent_role. Configure subagents in the Subagent settings tab.",
991
1103
  inherited: "Base value (not overridden)",
992
1104
  overridden: "Override",
993
1105
  unset: "Not set",
994
- unsupported: "This schema type is not yet supported by the form. Existing values are preserved; remove the Config to reset them.",
1106
+ unsupported: "This schema type is not yet supported by the form. Existing values are preserved; turn off the Config to reset them.",
995
1107
  json: "Config",
996
1108
  invalid: "Enter valid JSON.",
997
1109
  object: "Enter a JSON object.",
@@ -1003,6 +1115,7 @@ var copy = {
1003
1115
  list: "Config",
1004
1116
  reset: "\uAE30\uBCF8\uAC12\uC73C\uB85C",
1005
1117
  unavailable: "\uC2A4\uD0A4\uB9C8\uB97C \uC77D\uC744 \uC218 \uC5C6\uC5B4 \uD3B8\uC9D1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
1118
+ delegated: "subagent_role\uB85C \uD1B5\uD569\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uD0ED\uC5D0\uC11C \uC124\uC815\uD558\uC138\uC694.",
1006
1119
  inherited: "\uAE30\uBCF8\uAC12",
1007
1120
  overridden: "\uBCC0\uACBD\uB428",
1008
1121
  unset: "\uBBF8\uC124\uC815",
@@ -1018,6 +1131,7 @@ var copy = {
1018
1131
  list: "Config",
1019
1132
  reset: "\u91CD\u7F6E\u8986\u76D6",
1020
1133
  unavailable: "\u67B6\u6784\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u7F16\u8F91",
1134
+ delegated: "\u5DF2\u5408\u5E76\u5230 subagent_role\u3002\u8BF7\u5728\u5B50\u4EE3\u7406\u8BBE\u7F6E\u9875\u4E2D\u914D\u7F6E\u3002",
1021
1135
  inherited: "\u57FA\u7840\u503C\uFF08\u672A\u8986\u76D6\uFF09",
1022
1136
  overridden: "\u8986\u76D6",
1023
1137
  unset: "\u672A\u8BBE\u7F6E",
@@ -1339,7 +1453,7 @@ function ConfigEditor({ entries, value, onChange, locale }) {
1339
1453
  subtitle: entry.id,
1340
1454
  description: "",
1341
1455
  searchText: JSON.stringify(entry.schema ?? {}),
1342
- ...entry.error || !entry.schema ? { disabledReason: entry.error || labels.unavailable } : {}
1456
+ ...isDelegationPlugin(entry.name) ? { disabledReason: labels.delegated } : entry.error || !entry.schema ? { disabledReason: entry.error || labels.unavailable } : {}
1343
1457
  })),
1344
1458
  ...Object.keys(value).filter((id) => !entries.some((entry) => entry.id === id)).map((id) => ({ name: id, title: id, description: labels.unavailable }))
1345
1459
  ];
@@ -1374,7 +1488,7 @@ var import_react6 = __toESM(require("react"), 1);
1374
1488
  var import_jsx_runtime6 = require("react/jsx-runtime");
1375
1489
  var message = (error) => error instanceof Error ? error.message : String(error);
1376
1490
  function PresetList({ locale, api, revision, onEdit, onCopy }) {
1377
- const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1491
+ const t = (ko3, en3, zh3) => locale.startsWith("ko") ? ko3 : locale.startsWith("zh") ? zh3 : en3;
1378
1492
  const [load, setLoad] = import_react6.default.useState({ phase: "loading" });
1379
1493
  const [action, setAction] = import_react6.default.useState();
1380
1494
  const [deleting, setDeleting] = import_react6.default.useState();
@@ -1419,7 +1533,7 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1419
1533
  const warnings = [...items.reduce((groups, item) => item.applyWarning ? groups.set(item.applyWarning, [...groups.get(item.applyWarning) ?? [], item.name]) : groups, /* @__PURE__ */ new Map())];
1420
1534
  const retry = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: load.phase === "loading", onClick: () => void refresh(), children: t("\uB2E4\uC2DC \uC2DC\uB3C4", "Retry", "\u91CD\u8BD5") });
1421
1535
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1422
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PageHead, { title: t("\uD504\uB9AC\uC14B \uBAA9\uB85D", "Preset list", "\u9884\u8BBE\u5217\u8868"), description: t("\uC800\uC7A5\uD55C \uD504\uB9AC\uC14B\uC744 DSH\uC5D0 \uBC18\uC601\uD558\uBA74 \uC0C8 \uC138\uC158\uC5D0\uC11C \uC120\uD0DD\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Apply saved configurations to DSH agent presets or delete them.", "\u5C06\u5DF2\u4FDD\u5B58\u7684\u914D\u7F6E\u5E94\u7528\u5230 DSH \u4EE3\u7406\u9884\u8BBE\u6216\u5220\u9664\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { disabled: busy || !api || load.phase === "loading", onClick: () => void refresh(), children: load.phase === "loading" && load.items ? t("\uC0C8\uB85C\uACE0\uCE68 \uC911\u2026", "Refreshing\u2026", "\u5237\u65B0\u4E2D\u2026") : t("\uC0C8\uB85C\uACE0\uCE68", "Refresh", "\u5237\u65B0") }) }),
1536
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PageHead, { title: t("\uD504\uB9AC\uC14B \uBAA9\uB85D", "Preset list", "\u9884\u8BBE\u5217\u8868"), description: t("\uC0C8 \uC138\uC158\uC5D0 \uC801\uC6A9\uD558\uB824\uBA74 DSH\uC5D0 \uBC18\uC601\uD574 \uC8FC\uC138\uC694. \uC774\uBBF8 \uC2E4\uD589 \uC911\uC778 \uC138\uC158\uC5D0\uB294 \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Apply to DSH to use these settings in new sessions. Changes do not affect sessions that are already running.", "\u8BF7\u5E94\u7528\u5230 DSH\uFF0C\u4EE5\u4FBF\u5728\u65B0\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u8FD9\u4E9B\u8BBE\u7F6E\u3002\u66F4\u6539\u4E0D\u4F1A\u5F71\u54CD\u6B63\u5728\u8FD0\u884C\u7684\u4F1A\u8BDD\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { disabled: busy || !api || load.phase === "loading", onClick: () => void refresh(), children: load.phase === "loading" && load.items ? t("\uC0C8\uB85C\uACE0\uCE68 \uC911\u2026", "Refreshing\u2026", "\u5237\u65B0\u4E2D\u2026") : t("\uC0C8\uB85C\uACE0\uCE68", "Refresh", "\u5237\u65B0") }) }),
1423
1537
  !api && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Notice, { children: t("Studio \uC11C\uBC84\uC5D0 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC544 \uD504\uB9AC\uC14B\uC744 \uBD88\uB7EC\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Presets are unavailable because the Studio Host API is not connected.", "Studio Host API \u672A\u8FDE\u63A5\uFF0C\u65E0\u6CD5\u52A0\u8F7D\u9884\u8BBE") }),
1424
1538
  api && load.phase === "loading" && !load.items && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "status", className: "studio-muted", children: t("\uD504\uB9AC\uC14B\uC744 \uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading presets\u2026", "\u6B63\u5728\u52A0\u8F7D\u9884\u8BBE\u2026") }),
1425
1539
  load.phase === "failed" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Notice, { action: retry, children: load.items ? t(`\uBAA9\uB85D\uC744 \uC0C8\uB85C \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD574 \uC774\uC804 \uBAA9\uB85D\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4. (${load.error})`, `Could not reload the list. Showing the last loaded list. (${load.error})`, `\u65E0\u6CD5\u91CD\u65B0\u52A0\u8F7D\u5217\u8868\uFF0C\u6B63\u5728\u663E\u793A\u4E0A\u6B21\u52A0\u8F7D\u7684\u5217\u8868\uFF08${load.error}\uFF09`) : t(`\uD504\uB9AC\uC14B \uBAA9\uB85D\uC744 \uBD88\uB7EC\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uC5D0\uB294 \uC601\uD5A5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. (${load.error})`, `Could not load presets. Saved presets were not changed. (${load.error})`, `\u65E0\u6CD5\u52A0\u8F7D\u9884\u8BBE\uFF0C\u5DF2\u4FDD\u5B58\u7684\u9884\u8BBE\u672A\u88AB\u66F4\u6539\uFF08${load.error}\uFF09`) }),
@@ -1429,7 +1543,7 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1429
1543
  action.error,
1430
1544
  ")"
1431
1545
  ] }),
1432
- load.phase === "ready" && !items.length && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "status", className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uC0C8\uB85C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694.", "No saved presets. Create one from Create preset.", "\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u9884\u8BBE\u3002\u8BF7\u4ECE\u521B\u5EFA\u9884\u8BBE\u83DC\u5355\u5F00\u59CB\u3002") }),
1546
+ load.phase === "ready" && !items.length && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { role: "status", className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30\uC5D0\uC11C \uC0C8\uB85C \uB9CC\uB4E4\uC5B4 \uBCF4\uC138\uC694.", "No saved presets. Use Create preset to make one.", "\u6CA1\u6709\u5DF2\u4FDD\u5B58\u7684\u9884\u8BBE\u3002\u8BF7\u4ECE\u521B\u5EFA\u9884\u8BBE\u83DC\u5355\u5F00\u59CB\u3002") }),
1433
1547
  warnings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { className: "studio-preset-warnings", "aria-label": t("\uC8FC\uC758\uC0AC\uD56D", "Notices", "\u6CE8\u610F\u4E8B\u9879"), children: warnings.map(([text, names2]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
1434
1548
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: "warning", children: t("\uC8FC\uC758", "Notice", "\u6CE8\u610F") }),
1435
1549
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
@@ -1437,7 +1551,7 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1437
1551
  " ",
1438
1552
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "studio-card-meta", children: [
1439
1553
  "(",
1440
- t("\uD574\uB2F9", "Affects", "\u6D89\u53CA"),
1554
+ t("\uB300\uC0C1", "Affects", "\u6D89\u53CA"),
1441
1555
  ": ",
1442
1556
  names2.join(", "),
1443
1557
  ")"
@@ -1451,7 +1565,7 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1451
1565
  item.template && !item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: "info", children: t("\uAE30\uBCF8 \uC81C\uACF5", "Built-in", "\u5185\u7F6E") }),
1452
1566
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Badge, { tone: tone[item.status], children: status(item) })
1453
1567
  ] }),
1454
- item.template ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: t("Studio\uAC00 \uAD8C\uC7A5\uD558\uB294 \uCF54\uB529 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uAE30\uBCF8 \uAD6C\uC131\uC785\uB2C8\uB2E4. \uACE0\uCE58\uB824\uBA74 \uBCF5\uC0AC\uD574\uC11C \uC4F0\uC138\uC694.", "Studio optimized default for coding agents. Create a copy to customize it.", "Studio \u63A8\u8350\u7684\u7F16\u7801\u4EE3\u7406\u9ED8\u8BA4\u914D\u7F6E\u3002\u5982\u9700\u4FEE\u6539\uFF0C\u8BF7\u521B\u5EFA\u526F\u672C") }) : item.description && !item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: item.description }),
1568
+ item.template ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: t("Studio\uAC00 \uAD8C\uC7A5\uD558\uB294 \uCF54\uB529 \uC5D0\uC774\uC804\uD2B8\uC6A9 \uAD6C\uC131\uC785\uB2C8\uB2E4. \uACE0\uCE58\uB824\uBA74 \uBCF5\uC0AC\uD574\uC11C \uC4F0\uC138\uC694.", "Studio\u2019s recommended setup for coding agents. Create a copy to customize it.", "Studio \u63A8\u8350\u7684\u7F16\u7801\u4EE3\u7406\u914D\u7F6E\u3002\u5982\u9700\u4FEE\u6539\uFF0C\u8BF7\u521B\u5EFA\u526F\u672C") }) : item.description && !item.readOnly && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-text", children: item.description }),
1455
1569
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "studio-card-meta", children: [
1456
1570
  t("\uB3C4\uAD6C", "Tools", "\u5DE5\u5177"),
1457
1571
  " ",
@@ -1461,7 +1575,7 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1461
1575
  " ",
1462
1576
  Array.isArray(item.agents) ? item.agents.length : 0
1463
1577
  ] }),
1464
- item.readOnly ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-meta studio-card-note", children: t("DSH\uC5D0 \uC124\uCE58\uB41C \uBAA8\uB4E0 \uB3C4\uAD6C\uB97C \uB2F4\uC740 \uAE30\uC900 \uD504\uB9AC\uC14B\uC785\uB2C8\uB2E4. \uD3B8\uC9D1\uD558\uAC70\uB098 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Reference preset with every installed DSH tool. It cannot be edited or deleted.", "\u5305\u542B\u6240\u6709\u5DF2\u5B89\u88C5 DSH \u5DE5\u5177\u7684\u57FA\u51C6\u9884\u8BBE\uFF0C\u65E0\u6CD5\u7F16\u8F91\u6216\u5220\u9664") }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "studio-card-footer", children: [
1578
+ item.readOnly ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-card-meta studio-card-note", children: t("\uC774 \uD504\uB85C\uD544\uC5D0\uC11C \uC4F8 \uC218 \uC788\uB294 \uBAA8\uB4E0 \uB3C4\uAD6C\uB97C \uB2F4\uC740 \uAE30\uC900 \uD504\uB9AC\uC14B\uC785\uB2C8\uB2E4. \uD3B8\uC9D1\uD558\uAC70\uB098 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Reference preset with every tool available in this profile. It cannot be edited or deleted.", "\u5305\u542B\u6B64\u914D\u7F6E\u6587\u4EF6\u4E2D\u6240\u6709\u53EF\u7528\u5DE5\u5177\u7684\u57FA\u51C6\u9884\u8BBE\uFF0C\u65E0\u6CD5\u7F16\u8F91\u6216\u5220\u9664") }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "studio-card-footer", children: [
1465
1579
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || item.readOnly || item.template || !onEdit, title: item.template ? t("\uAE30\uBCF8 \uC81C\uACF5 \uD504\uB9AC\uC14B\uC740 \uD3B8\uC9D1\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBCF5\uC0AC\uD574\uC11C \uACE0\uCCD0 \uC4F0\uC138\uC694.", "Built-in presets cannot be edited. Create a copy to customize it.", "\u5185\u7F6E\u9884\u8BBE\u65E0\u6CD5\u7F16\u8F91\uFF0C\u8BF7\u521B\u5EFA\u526F\u672C\u540E\u4FEE\u6539") : void 0, onClick: () => onEdit?.(item), children: t("\uD3B8\uC9D1", "Edit", "\u7F16\u8F91") }),
1466
1580
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || !onCopy, onClick: () => onCopy?.(item), children: t("\uBCF5\uC0AC", "Create copy", "\u521B\u5EFA\u526F\u672C") }),
1467
1581
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Button, { small: true, disabled: busy || !api || item.readOnly || item.nonDeletable, title: item.nonDeletable ? t("\uAE30\uBCF8 \uC81C\uACF5 \uD504\uB9AC\uC14B\uC740 \uC0AD\uC81C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "Built-in presets cannot be deleted.", "\u5185\u7F6E\u9884\u8BBE\u65E0\u6CD5\u5220\u9664") : void 0, onClick: () => {
@@ -1497,43 +1611,46 @@ function PresetList({ locale, api, revision, onEdit, onCopy }) {
1497
1611
  void run(target.id, "delete", () => api.delete(target.id)).then(() => setDeleting(void 0));
1498
1612
  }, children: t("\uC0AD\uC81C \uD655\uC778", "Confirm delete", "\u786E\u8BA4\u5220\u9664") })
1499
1613
  ] }),
1500
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uACFC DSH\uC5D0 \uBC18\uC601\uB41C \uD504\uB9AC\uC14B\uC744 \uD568\uAED8 \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uAE30\uC874 \uB300\uD654\uB294 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4.", "Delete the saved preset and its Studio-owned DSH registration. Existing conversations are not deleted.", "\u5220\u9664\u5DF2\u4FDD\u5B58\u9884\u8BBE\u53CA Studio \u6CE8\u518C\u7684 DSH \u9884\u8BBE\uFF0C\u4E0D\u5220\u9664\u73B0\u6709\u5BF9\u8BDD\u3002") })
1614
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "studio-muted", children: t("\uC800\uC7A5\uB41C \uD504\uB9AC\uC14B\uACFC DSH\uC5D0 \uBC18\uC601\uB41C \uD504\uB9AC\uC14B\uC744 \uD568\uAED8 \uC0AD\uC81C\uD569\uB2C8\uB2E4. \uAE30\uC874 \uB300\uD654\uB294 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4.", "Deletes the saved preset and removes it from DSH. Existing conversations are kept.", "\u5220\u9664\u5DF2\u4FDD\u5B58\u9884\u8BBE\u53CA Studio \u6CE8\u518C\u7684 DSH \u9884\u8BBE\uFF0C\u4E0D\u5220\u9664\u73B0\u6709\u5BF9\u8BDD\u3002") })
1501
1615
  }
1502
1616
  )
1503
1617
  ] });
1504
1618
  }
1505
1619
 
1506
- // src/package-name.ts
1507
- var PACKAGE_NAME = "@matamt-dbg/dsh-studio";
1508
- var POLICY_PLUGIN = `${PACKAGE_NAME}/policy`;
1509
- var ROLE_PLUGIN = `${PACKAGE_NAME}/roles`;
1510
- var BATCH_PLUGIN = `${PACKAGE_NAME}/batch`;
1511
-
1512
- // src/delegation.ts
1513
- var ROLE_TOOL = "subagent_role";
1514
- var LEGACY_ROLE_TOOL = "studio_role";
1515
- var GENERIC_DELEGATION_TOOLS = ["subagent", "subagent_fork", "list_subagent_models"];
1516
- var WITHDRAWN_TOOLS = ["task_run"];
1517
- function delegationTools(tools, hasRoles = false) {
1518
- const delegates = hasRoles || tools.some((name) => name === ROLE_TOOL || name === LEGACY_ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name));
1519
- const rest = tools.filter((name) => name !== ROLE_TOOL && name !== LEGACY_ROLE_TOOL && !GENERIC_DELEGATION_TOOLS.includes(name) && !WITHDRAWN_TOOLS.includes(name));
1520
- return delegates ? [...rest, ROLE_TOOL] : rest;
1521
- }
1522
- function renameLegacyRoleKey(entries) {
1523
- if (!entries || !Object.hasOwn(entries, LEGACY_ROLE_TOOL)) return entries;
1524
- const { [LEGACY_ROLE_TOOL]: legacy, ...rest } = entries;
1525
- return Object.hasOwn(rest, ROLE_TOOL) ? rest : { ...rest, [ROLE_TOOL]: legacy };
1526
- }
1527
-
1528
1620
  // src/optimized-preset.ts
1529
1621
  var BUILTIN_TOOL_PRESETS = ["standard", "ptc", "minimal", "cordis"];
1622
+ var OPTIMIZED_TOOLS = [
1623
+ "read_batch",
1624
+ "read",
1625
+ "write",
1626
+ "edit",
1627
+ "glob",
1628
+ "grep",
1629
+ "job_output",
1630
+ "job_list",
1631
+ "job_kill",
1632
+ "skill",
1633
+ "ask_user_question",
1634
+ "web_search",
1635
+ "web_fetch",
1636
+ "present",
1637
+ "bash",
1638
+ "read_image",
1639
+ "exit_plan_mode",
1640
+ "send_message",
1641
+ "interrupt_agent",
1642
+ "list_agents",
1643
+ "workflow",
1644
+ "subagent_role"
1645
+ ];
1646
+ var ALLOWED_TOOLS = /* @__PURE__ */ new Set([...OPTIMIZED_TOOLS, ...GENERIC_DELEGATION_TOOLS, LEGACY_ROLE_TOOL]);
1530
1647
 
1531
1648
  // src/prompt-tools-prototype.tsx
1532
1649
  var import_react7 = __toESM(require("react"), 1);
1533
1650
  var import_jsx_runtime7 = require("react/jsx-runtime");
1534
1651
  var defaultProvidedTools = [];
1535
1652
  function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProvidedChange, catalog, catalogError, onPreview, onRequestChange, initialRequest }) {
1536
- const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1653
+ const t = (ko3, en3, zh3) => locale.startsWith("ko") ? ko3 : locale.startsWith("zh") ? zh3 : en3;
1537
1654
  const base = initialRequest?.preset ?? "studio-master";
1538
1655
  const [toolOverrides, setToolOverrides] = import_react7.default.useState(renameLegacyRoleKey(initialRequest?.toolOverrides) ?? {});
1539
1656
  const [sections, setSections] = import_react7.default.useState(initialRequest?.sections ?? {});
@@ -1568,7 +1685,8 @@ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProv
1568
1685
  onRequestChange?.(request);
1569
1686
  }, [requestKey, onRequestChange]);
1570
1687
  const labels = { filter: t("\uBCF4\uAE30", "Show", "\u663E\u793A"), search: t("\uB3C4\uAD6C \uAC80\uC0C9", "Search tools", "\u641C\u7D22\u5DE5\u5177"), searchHint: t("\uC774\uB984, \uC124\uBA85, \uC778\uC790\uB85C \uAC80\uC0C9", "Search names, descriptions or parameters", "\u6309\u540D\u79F0\u3001\u63CF\u8FF0\u6216\u53C2\u6570\u641C\u7D22"), all: t("\uC804\uCCB4", "All", "\u5168\u90E8"), selected: t("\uC120\uD0DD\uB428", "Selected", "\u5DF2\u9009"), modified: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), settings: t("\uC124\uC815", "Details", "\u8BE6\u60C5"), done: t("\uC644\uB8CC", "Close", "\u5173\u95ED"), empty: t("\uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.", "No items", "\u65E0\u9879\u76EE"), noResults: t("\uAC80\uC0C9 \uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.", "No matching results.", "\u6CA1\u6709\u5339\u914D\u7ED3\u679C\u3002"), modifiedTag: t("\uBCC0\uACBD\uB428", "Modified", "\u5DF2\u4FEE\u6539"), clear: t("\uC804\uCCB4 \uBCF4\uAE30", "Show all", "\u663E\u793A\u5168\u90E8") };
1571
- const presetTools = (presetId) => delegationTools(presets.find((p) => p.id === presetId)?.tools.map((tool) => tool.name) ?? []);
1688
+ const unsupportedToolReason = t("\uD604\uC7AC Studio\uC5D0\uC11C run_code\uB294 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. PTC\uB97C \uC0AC\uC6A9\uD558\uB824\uBA74 DSH\uC758 PTC \uD504\uB9AC\uC14B\uC73C\uB85C \uC0C8 \uC138\uC158\uC744 \uC2DC\uC791\uD574 \uC8FC\uC138\uC694. \uC774\uBBF8 \uC120\uD0DD\uB41C \uACBD\uC6B0\uC5D0\uB294 \uD574\uC81C\uD574 \uC8FC\uC138\uC694.", "Studio does not currently support run_code. To use PTC, start a new session with DSH\u2019s PTC preset. If run_code is already selected, deselect it.", "Studio \u76EE\u524D\u4E0D\u652F\u6301 run_code\u3002\u5982\u9700\u4F7F\u7528 PTC\uFF0C\u8BF7\u4F7F\u7528 DSH \u7684 PTC \u9884\u8BBE\u542F\u52A8\u65B0\u4F1A\u8BDD\u3002\u82E5\u5DF2\u9009\u62E9 run_code\uFF0C\u8BF7\u53D6\u6D88\u9009\u62E9\u3002");
1689
+ const presetTools = (presetId) => delegationTools(presets.find((p) => p.id === presetId)?.tools.map((tool) => tool.name) ?? []).filter((name) => name !== "run_code");
1572
1690
  const togglePreset = (presetId) => {
1573
1691
  const names2 = presetTools(presetId);
1574
1692
  onProvidedChange(includesPreset(presetId) ? provided.filter((name) => !names2.includes(name)) : [.../* @__PURE__ */ new Set([...provided, ...names2])]);
@@ -1600,7 +1718,7 @@ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProv
1600
1718
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.default.Fragment, { children: [
1601
1719
  catalogError ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: catalogError }) : !catalog ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Note, { children: t("\uBD88\uB7EC\uC624\uB294 \uC911\u2026", "Loading\u2026", "\u6B63\u5728\u52A0\u8F7D\u2026") }) : null,
1602
1720
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-form-section", children: [
1603
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uD504\uB86C\uD504\uD2B8", "Prompt composition", "\u63D0\u793A\u8BCD\u7EC4\u88C5"), description: t("\uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uC804\uB2EC\uD560 \uC9C0\uCE68\uC744 \uD3B8\uC9D1\uD569\uB2C8\uB2E4. \uBBF8\uB9AC\uBCF4\uAE30\uB85C \uC870\uB9BD \uACB0\uACFC\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Edit Persona and Studio instructions, then preview the assembled result.", "\u7F16\u8F91 Persona \u4E0E Studio \u4E13\u7528\u6307\u4EE4\uFF0C\u5E76\u9884\u89C8\u7EC4\u88C5\u7ED3\u679C\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !onPreview || !current || busy, onClick: () => void preview(), children: busy ? t("\uC870\uB9BD \uC911\u2026", "Assembling\u2026", "\u7EC4\u88C5\u4E2D\u2026") : t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Preview prompt", "\u9884\u89C8\u63D0\u793A\u8BCD") }) }),
1721
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uD504\uB86C\uD504\uD2B8", "Prompt composition", "\u63D0\u793A\u8BCD\u7EC4\u88C5"), description: t("\uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uC804\uB2EC\uD560 \uC9C0\uCE68\uC744 \uD3B8\uC9D1\uD569\uB2C8\uB2E4. \uBBF8\uB9AC\uBCF4\uAE30\uB85C \uC870\uB9BD \uACB0\uACFC\uB97C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.", "Edit the instructions the agent receives, then preview the assembled result.", "\u7F16\u8F91 Persona \u4E0E Studio \u4E13\u7528\u6307\u4EE4\uFF0C\u5E76\u9884\u89C8\u7EC4\u88C5\u7ED3\u679C\u3002"), actions: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !onPreview || !current || busy, onClick: () => void preview(), children: busy ? t("\uC870\uB9BD \uC911\u2026", "Assembling\u2026", "\u7EC4\u88C5\u4E2D\u2026") : t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Preview prompt", "\u9884\u89C8\u63D0\u793A\u8BCD") }) }),
1604
1722
  ["prefix", "suffix"].map((key) => {
1605
1723
  const name = providers[0];
1606
1724
  const config = providerConfigs[name] ?? {};
@@ -1625,12 +1743,12 @@ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProv
1625
1743
  ] })
1626
1744
  ] }),
1627
1745
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "studio-form-section", children: [
1628
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), description: t("\uC5D0\uC774\uC804\uD2B8\uAC00 \uC4F8 \uB3C4\uAD6C\uB97C \uACE0\uB985\uB2C8\uB2E4. \uC124\uC815\uC5D0\uC11C \uBAA8\uB378\uC5D0 \uBCF4\uC774\uB294 \uC124\uBA85\uC744 \uBC14\uAFC0 \uC218 \uC788\uC73C\uBA70, \uB3C4\uAD6C \uB3D9\uC791\uC740 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Choose tools. Details edits model-facing descriptions only; tool behavior is unchanged.", "\u9009\u62E9\u5DE5\u5177\u3002\u8BE6\u60C5\u53EA\u7F16\u8F91\u6A21\u578B\u53EF\u89C1\u7684\u63CF\u8FF0\uFF0C\u5DE5\u5177\u884C\u4E3A\u4E0D\u53D8\u3002") }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionHead, { title: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), description: t("\uC5D0\uC774\uC804\uD2B8\uAC00 \uC4F8 \uB3C4\uAD6C\uB97C \uACE0\uB985\uB2C8\uB2E4. \uC124\uC815\uC5D0\uC11C \uBAA8\uB378\uC5D0 \uBCF4\uC774\uB294 \uC124\uBA85\uC744 \uBC14\uAFC0 \uC218 \uC788\uC73C\uBA70, \uB3C4\uAD6C \uB3D9\uC791\uC740 \uBC14\uB00C\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "Choose the tools the agent can use. Details changes only the descriptions the model sees; tool behavior stays the same.", "\u9009\u62E9\u5DE5\u5177\u3002\u8BE6\u60C5\u53EA\u7F16\u8F91\u6A21\u578B\u53EF\u89C1\u7684\u63CF\u8FF0\uFF0C\u5DE5\u5177\u884C\u4E3A\u4E0D\u53D8\u3002") }),
1629
1747
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "studio-field", children: [
1630
1748
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: t("\uAE30\uBCF8 \uD504\uB9AC\uC14B\uC5D0\uC11C \uD55C \uBC88\uC5D0 \uCD94\uAC00", "Include a built-in preset tool set", "\u5F15\u5165\u5185\u7F6E\u9884\u8BBE\u5DE5\u5177\u96C6") }),
1631
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "studio-inline-actions", children: toolSources.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !!p.error, "aria-pressed": includesPreset(p.id), onClick: () => togglePreset(p.id), children: p.id === "cordis" ? "creator (cordis)" : p.id }, p.id)) })
1749
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "studio-inline-actions", children: toolSources.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { small: true, disabled: !!p.error || !presetTools(p.id).length, "aria-pressed": includesPreset(p.id), onClick: () => togglePreset(p.id), children: p.id === "cordis" ? "creator (cordis)" : p.id }, p.id)) })
1632
1750
  ] }),
1633
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ItemPicker, { label: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), items: [...tools.map((tool) => ({ ...tool, searchText: JSON.stringify(tool.parameters), ...GENERIC_DELEGATION_TOOLS.includes(tool.name) ? { disabledReason: t(`${ROLE_TOOL}\uB85C \uD1B5\uD569\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784\uC740 ${ROLE_TOOL}\uC744 \uC120\uD0DD\uD558\uC138\uC694.`, `Merged into ${ROLE_TOOL}. Select ${ROLE_TOOL} for subagent delegation.`, `\u5DF2\u5408\u5E76\u5230 ${ROLE_TOOL}\u3002\u8BF7\u9009\u62E9 ${ROLE_TOOL} \u8FDB\u884C\u5B50\u4EE3\u7406\u59D4\u6D3E\u3002`) } : {} })), ...provided.filter((name) => !tools.some((tool) => tool.name === name)).map((name) => ({ name, description: t("\uD604\uC7AC \uC124\uCE58\uB41C \uB3C4\uAD6C \uBAA9\uB85D\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.", "Absent from current catalog", "\u4E0D\u5728\u5F53\u524D\u76EE\u5F55\u4E2D") }))], selected: provided, onChange: changeTools, labels, isModified: (name) => {
1751
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ItemPicker, { label: t("\uB3C4\uAD6C", "Provided tools", "\u63D0\u4F9B\u7684\u5DE5\u5177"), items: [...tools.map((tool) => ({ ...tool, searchText: JSON.stringify(tool.parameters), ...tool.name === "run_code" ? { description: unsupportedToolReason, disabledReason: unsupportedToolReason } : {}, ...GENERIC_DELEGATION_TOOLS.includes(tool.name) ? { disabledReason: t(`${ROLE_TOOL}\uB85C \uD1B5\uD569\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC704\uC784\uC740 ${ROLE_TOOL}\uC744 \uC120\uD0DD\uD558\uC138\uC694.`, `Merged into ${ROLE_TOOL}. Select ${ROLE_TOOL} for subagent delegation.`, `\u5DF2\u5408\u5E76\u5230 ${ROLE_TOOL}\u3002\u8BF7\u9009\u62E9 ${ROLE_TOOL} \u8FDB\u884C\u5B50\u4EE3\u7406\u59D4\u6D3E\u3002`) } : {} })), ...provided.filter((name) => !tools.some((tool) => tool.name === name)).map((name) => ({ name, description: name === "run_code" ? unsupportedToolReason : t("\uD604\uC7AC \uD504\uB85C\uD544\uC5D0 \uC5C6\uB294 \uB3C4\uAD6C\uC785\uB2C8\uB2E4. \uC774\uB300\uB85C \uBC18\uC601\uD558\uBA74 \uC2E4\uD328\uD569\uB2C8\uB2E4.", "Not available in this profile. Applying will fail.", "\u5F53\u524D\u914D\u7F6E\u6587\u4EF6\u4E2D\u6CA1\u6709\u6B64\u5DE5\u5177\uFF0C\u76F4\u63A5\u5E94\u7528\u4F1A\u5931\u8D25\u3002"), ...name === "run_code" ? { disabledReason: unsupportedToolReason } : {} }))], selected: provided, onChange: changeTools, labels, hasDetails: (name) => name !== "run_code", isModified: (name) => {
1634
1752
  const tool = tools.find((item) => item.name === name);
1635
1753
  const override = toolOverrides[name];
1636
1754
  const parameters = parameterFields(tool?.parameters);
@@ -1676,7 +1794,7 @@ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProv
1676
1794
  const next = { ...toolOverrides };
1677
1795
  delete next[name];
1678
1796
  setToolOverrides(next);
1679
- }, children: t("\uC124\uBA85 \uBAA8\uB450 \uC6D0\uB798\uB300\uB85C", "Use original", "\u4F7F\u7528\u539F\u59CB\u503C") })
1797
+ }, children: t("\uC124\uBA85 \uBAA8\uB450 \uC6D0\uB798\uB300\uB85C", "Reset all descriptions", "\u5168\u90E8\u6062\u590D\u539F\u59CB\u63CF\u8FF0") })
1680
1798
  ] });
1681
1799
  } })
1682
1800
  ] }),
@@ -1685,7 +1803,7 @@ function PromptToolsPrototype({ locale, prompt, onPromptChange, provided, onProv
1685
1803
  {
1686
1804
  wide: true,
1687
1805
  open: previewOpen,
1688
- title: t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Prompt result preview", "\u63D0\u793A\u8BCD\u7ED3\u679C\u9884\u89C8"),
1806
+ title: t("\uD504\uB86C\uD504\uD2B8 \uBBF8\uB9AC\uBCF4\uAE30", "Prompt preview", "\u63D0\u793A\u8BCD\u7ED3\u679C\u9884\u89C8"),
1689
1807
  closeLabel: t("\uB2EB\uAE30", "Close preview", "\u5173\u95ED\u9884\u89C8"),
1690
1808
  onClose: closePreview,
1691
1809
  subtitle: result ? t(`\uC139\uC158 ${result.sections.length}\uAC1C \xB7 \uB3C4\uAD6C ${result.tools.length}\uAC1C`, `${result.sections.length} sections \xB7 ${result.tools.length} tools`, `${result.sections.length} \u4E2A\u7AE0\u8282 \xB7 ${result.tools.length} \u4E2A\u5DE5\u5177`) : void 0,
@@ -1744,7 +1862,7 @@ function parameterFields(schema, prefix = "") {
1744
1862
  // src/detail-layout.tsx
1745
1863
  var import_react8 = __toESM(require("react"), 1);
1746
1864
  var import_jsx_runtime8 = require("react/jsx-runtime");
1747
- function DetailLayout({ title, subtitle, navigation, pages, selected, onSelect }) {
1865
+ function DetailLayout({ title, subtitle, navigation, pages, selected, onSelect, footer }) {
1748
1866
  const id = import_react8.default.useId();
1749
1867
  const scroller = import_react8.default.useRef(null);
1750
1868
  import_react8.default.useLayoutEffect(() => {
@@ -1769,7 +1887,8 @@ function DetailLayout({ title, subtitle, navigation, pages, selected, onSelect }
1769
1887
  children: page.label
1770
1888
  }
1771
1889
  )
1772
- ] }, page.id))
1890
+ ] }, page.id)),
1891
+ footer
1773
1892
  ] }),
1774
1893
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "studio-scroller", ref: scroller, "data-studio-scroller": "", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "studio-content", children: pages.map((page) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1775
1894
  "section",
@@ -1786,13 +1905,17 @@ function DetailLayout({ title, subtitle, navigation, pages, selected, onSelect }
1786
1905
 
1787
1906
  // src/optimization-prototype.tsx
1788
1907
  var import_jsx_runtime9 = require("react/jsx-runtime");
1908
+ var withoutSubagentFilter = (request) => {
1909
+ const { subagentToolFilter: _filter, ...rest } = request;
1910
+ return rest;
1911
+ };
1789
1912
  var initial = { prompt: "Report evidence, uncertainty and remaining work.", tools: "read / grep / glob", spill: "2048", wait: "60000" };
1790
1913
  var seeds = [
1791
1914
  { name: "Researcher", role: "research", model: "inherit", reasoning: "inherit", spawn: "fresh", persona: "Investigate primary sources and cite the evidence." },
1792
1915
  { name: "Reviewer", role: "review", model: "inherit", reasoning: "inherit", spawn: "fork", persona: "Review correctness and identify concrete risks." }
1793
1916
  ];
1794
- function OptimizationPrototype({ locale, catalog, catalogError, onPreview, presetActions, extensionActions }) {
1795
- const t = (ko2, en2, zh2) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
1917
+ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, presetActions, extensionActions, navFooter }) {
1918
+ const t = (ko3, en3, zh3) => locale.startsWith("ko") ? ko3 : locale.startsWith("zh") ? zh3 : en3;
1796
1919
  const [page, setPage] = import_react9.default.useState("list");
1797
1920
  const [common, setCommon] = import_react9.default.useState({ ...initial });
1798
1921
  const [agents, setAgents] = import_react9.default.useState(() => seeds.map((a) => ({ ...a })));
@@ -1807,6 +1930,8 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1807
1930
  const [configDraft, setConfigDraft] = import_react9.default.useState();
1808
1931
  const configOverrides = configDraft ?? defaults;
1809
1932
  const [request, setRequest] = import_react9.default.useState();
1933
+ const [hiddenFromSubagents, setHiddenFromSubagents] = import_react9.default.useState([]);
1934
+ const selectable = (names2) => names2.filter((name2) => provided.includes(name2) && name2 !== ROLE_TOOL);
1810
1935
  const [saving, setSaving] = import_react9.default.useState(false);
1811
1936
  const [saveError, setSaveError] = import_react9.default.useState("");
1812
1937
  const save = async () => {
@@ -1814,7 +1939,7 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1814
1939
  setSaving(true);
1815
1940
  setSaveError("");
1816
1941
  try {
1817
- await presetActions.save(JSON.stringify({ ...editing?.id ? { id: editing.id } : {}, name: name.trim(), description, request: { ...request, configOverrides }, conveniences: { spill: common.spill }, agents }));
1942
+ await presetActions.save(JSON.stringify({ ...editing?.id ? { id: editing.id } : {}, name: name.trim(), description, request: { ...withoutSubagentFilter(request), configOverrides, ...selectable(hiddenFromSubagents).length ? { subagentToolFilter: { deny: selectable(hiddenFromSubagents) } } : {} }, conveniences: { spill: common.spill }, agents: agents.map((role) => role.toolFilter ? { ...role, toolFilter: { ...role.toolFilter, deny: selectable(role.toolFilter.deny) } } : role) }));
1818
1943
  setRevision((n) => n + 1);
1819
1944
  setPage("list");
1820
1945
  setName("");
@@ -1823,6 +1948,7 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1823
1948
  setCommon({ ...initial });
1824
1949
  setProvided([]);
1825
1950
  setAgents(seeds.map((a) => ({ ...a })));
1951
+ setHiddenFromSubagents([]);
1826
1952
  setEditing(void 0);
1827
1953
  setTab("tools");
1828
1954
  setDraftKey((n) => n + 1);
@@ -1832,15 +1958,16 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1832
1958
  setSaving(false);
1833
1959
  }
1834
1960
  };
1835
- const editPreset = (item, copy2 = false) => {
1836
- setEditing(copy2 ? { ...item, id: "" } : item);
1837
- setName(copy2 ? `${item.name} Copy` : item.name);
1961
+ const editPreset = (item, copy3 = false) => {
1962
+ setEditing(copy3 ? { ...item, id: "" } : item);
1963
+ setName(copy3 ? `${item.name} Copy` : item.name);
1838
1964
  setDescription(item.description);
1839
1965
  setCommon({ ...initial, prompt: item.request.prompt, spill: item.conveniences?.spill ?? initial.spill });
1840
1966
  setProvided([...item.request.tools]);
1841
1967
  setConfigDraft(item.request.configOverrides ?? {});
1842
1968
  setAgents(item.agents.map((role) => ({ ...role })));
1843
1969
  setRequest(item.request);
1970
+ setHiddenFromSubagents([...item.request.subagentToolFilter?.deny ?? []]);
1844
1971
  setDraftKey((n) => n + 1);
1845
1972
  setPage("create");
1846
1973
  };
@@ -1848,7 +1975,9 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1848
1975
  const presetPersona = request?.providerConfigs?.[persona]?.prefix;
1849
1976
  const masterPersona = catalog?.presets?.find((p) => p.id === (request?.preset ?? "studio-master"))?.promptProviders?.find((p) => p.name === persona)?.fields?.find((f) => f.key === "prefix")?.value;
1850
1977
  const defaultPersona = typeof presetPersona === "string" ? presetPersona : typeof masterPersona === "string" ? masterPersona : "";
1851
- const agentPage = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RoleEditor, { locale, roles: agents, onChange: setAgents, loadModels: presetActions?.models, defaultPersona }, draftKey);
1978
+ const toolChoices = (catalog?.tools ?? []).filter((tool) => provided.includes(tool.name) && tool.name !== ROLE_TOOL).map((tool) => ({ name: tool.name, description: tool.description }));
1979
+ const delegates = provided.some((name2) => name2 === ROLE_TOOL || GENERIC_DELEGATION_TOOLS.includes(name2));
1980
+ const agentPage = /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RoleEditor, { locale, roles: agents, onChange: setAgents, loadModels: presetActions?.models, defaultPersona, tools: toolChoices, hiddenTools: hiddenFromSubagents, onHiddenToolsChange: setHiddenFromSubagents, delegates }, draftKey);
1852
1981
  const tabs = [
1853
1982
  { id: "tools", label: t("\uD504\uB86C\uD504\uD2B8\xB7\uB3C4\uAD6C", "Prompts & tools", "\u63D0\u793A\u8BCD\u4E0E\u5DE5\u5177"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PromptToolsPrototype, { initialRequest: editing?.request, locale, catalog, catalogError, onPreview: onPreview ? (request2) => onPreview({ ...request2, configOverrides }) : void 0, onRequestChange: setRequest, prompt: common.prompt, onPromptChange: (value) => setCommon((c) => ({ ...c, prompt: value })), provided, onProvidedChange: setProvided }, draftKey) },
1854
1983
  { id: "convenience", label: t("\uC2E4\uD589 \uC124\uC815", "Execution settings", "\u6267\u884C\u8BBE\u7F6E"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
@@ -1858,7 +1987,7 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1858
1987
  { id: "agents", label: t("\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8", "Subagent settings", "\u5B50\u4EE3\u7406\u8BBE\u7F6E"), content: agentPage }
1859
1988
  ];
1860
1989
  const tabId = import_react9.default.useId();
1861
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DetailLayout, { title: "DSH Studio", subtitle: t("\uC5D0\uC774\uC804\uD2B8 \uD504\uB9AC\uC14B \uD3B8\uC9D1\uAE30", "Agent preset editor", "\u4EE3\u7406\u9884\u8BBE\u7F16\u8F91\u5668"), navigation: t("Studio \uBA54\uB274", "Preset navigation", "\u9884\u8BBE\u5BFC\u822A"), selected: page, onSelect: (next) => {
1990
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DetailLayout, { footer: navFooter, title: "DSH Studio", subtitle: t("\uC5D0\uC774\uC804\uD2B8 \uD504\uB9AC\uC14B \uD3B8\uC9D1\uAE30", "Agent preset editor", "\u4EE3\u7406\u9884\u8BBE\u7F16\u8F91\u5668"), navigation: t("Studio \uBA54\uB274", "Studio menu", "\u9884\u8BBE\u5BFC\u822A"), selected: page, onSelect: (next) => {
1862
1991
  if (next === "create" && editing) {
1863
1992
  setEditing(void 0);
1864
1993
  setName("");
@@ -1873,7 +2002,7 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1873
2002
  }, pages: [
1874
2003
  { id: "list", group: t("\uD504\uB9AC\uC14B", "Presets", "\u9884\u8BBE"), label: t("\uD504\uB9AC\uC14B \uBAA9\uB85D", "Preset list", "\u9884\u8BBE\u5217\u8868"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PresetList, { locale, api: presetActions, revision, onEdit: (item) => editPreset(item), onCopy: (item) => editPreset(item, true) }) },
1875
2004
  { id: "create", group: t("\uD504\uB9AC\uC14B", "Presets", "\u9884\u8BBE"), label: editing ? t("\uD504\uB9AC\uC14B \uD3B8\uC9D1", "Edit preset", "\u7F16\u8F91\u9884\u8BBE") : t("\uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30", "Create preset", "\u521B\u5EFA\u9884\u8BBE"), content: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "studio-page-stack", children: [
1876
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PageHead, { title: editing ? t("\uD504\uB9AC\uC14B \uD3B8\uC9D1", "Edit preset", "\u7F16\u8F91\u9884\u8BBE") : t("\uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30", "Create preset", "\u521B\u5EFA\u9884\u8BBE"), description: editing ? t("\uC800\uC7A5\uD55C \uB4A4 \uD504\uB9AC\uC14B \uBAA9\uB85D\uC5D0\uC11C \uB2E4\uC2DC \uBC18\uC601\uD574\uC57C DSH\uC5D0 \uC801\uC6A9\uB429\uB2C8\uB2E4.", "Saving updates this preset. If it was applied, reapply it from the list to update DSH.", "\u4FDD\u5B58\u4F1A\u66F4\u65B0\u6B64\u9884\u8BBE\u3002\u82E5\u5DF2\u5E94\u7528\uFF0C\u9700\u8981\u5728\u5217\u8868\u4E2D\u91CD\u65B0\u5E94\u7528\u624D\u4F1A\u66F4\u65B0 DSH") : t("\uD504\uB86C\uD504\uD2B8, \uB3C4\uAD6C, \uC2E4\uD589 \uC124\uC815, \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uAD6C\uC131\uD574 \uC0C8 \uD504\uB9AC\uC14B\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.", "Configure tools, execution settings and subagent roles, then save a new preset.", "\u914D\u7F6E\u5DE5\u5177\u3001\u6267\u884C\u8BBE\u7F6E\u548C\u5B50\u4EE3\u7406\u89D2\u8272\uFF0C\u5E76\u4FDD\u5B58\u4E3A\u65B0\u9884\u8BBE"), actions: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { primary: true, disabled: saving || !presetActions || !request || !name.trim(), onClick: () => void save(), children: saving ? t("\uC800\uC7A5 \uC911\u2026", "Saving\u2026", "\u4FDD\u5B58\u4E2D\u2026") : t("\uD504\uB9AC\uC14B \uC800\uC7A5", "Save preset", "\u4FDD\u5B58\u9884\u8BBE") }) }),
2005
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PageHead, { title: editing ? t("\uD504\uB9AC\uC14B \uD3B8\uC9D1", "Edit preset", "\u7F16\u8F91\u9884\u8BBE") : t("\uD504\uB9AC\uC14B \uB9CC\uB4E4\uAE30", "Create preset", "\u521B\u5EFA\u9884\u8BBE"), description: editing ? t("\uC800\uC7A5 \uD6C4 \uC0C8 \uC138\uC158\uC5D0 \uC801\uC6A9\uD558\uB824\uBA74 \uD504\uB9AC\uC14B \uBAA9\uB85D\uC5D0\uC11C DSH\uC5D0 \uBC18\uC601\uD574 \uC8FC\uC138\uC694. \uC774\uBBF8 \uC2E4\uD589 \uC911\uC778 \uC138\uC158\uC5D0\uB294 \uBCC0\uACBD \uC0AC\uD56D\uC774 \uC801\uC6A9\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "After saving, apply to DSH from the preset list to use these settings in new sessions. Changes do not affect sessions that are already running.", "\u4FDD\u5B58\u540E\uFF0C\u8BF7\u5728\u9884\u8BBE\u5217\u8868\u4E2D\u5E94\u7528\u5230 DSH\uFF0C\u4EE5\u4FBF\u5728\u65B0\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u8FD9\u4E9B\u8BBE\u7F6E\u3002\u66F4\u6539\u4E0D\u4F1A\u5F71\u54CD\u6B63\u5728\u8FD0\u884C\u7684\u4F1A\u8BDD\u3002") : t("\uD504\uB86C\uD504\uD2B8, \uB3C4\uAD6C, \uC2E4\uD589 \uC124\uC815, \uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8\uB97C \uAD6C\uC131\uD574 \uC0C8 \uD504\uB9AC\uC14B\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.", "Configure prompts, tools, execution settings and subagents, then save a new preset.", "\u914D\u7F6E\u5DE5\u5177\u3001\u6267\u884C\u8BBE\u7F6E\u548C\u5B50\u4EE3\u7406\u89D2\u8272\uFF0C\u5E76\u4FDD\u5B58\u4E3A\u65B0\u9884\u8BBE"), actions: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Button, { primary: true, disabled: saving || !presetActions || !request || !name.trim(), onClick: () => void save(), children: saving ? t("\uC800\uC7A5 \uC911\u2026", "Saving\u2026", "\u4FDD\u5B58\u4E2D\u2026") : t("\uD504\uB9AC\uC14B \uC800\uC7A5", "Save preset", "\u4FDD\u5B58\u9884\u8BBE") }) }),
1877
2006
  saveError && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Notice, { children: t(`\uD504\uB9AC\uC14B\uC744 \uC800\uC7A5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uD3B8\uC9D1\uD55C \uB0B4\uC6A9\uC740 \uD654\uBA74\uC5D0 \uADF8\uB300\uB85C \uC788\uC2B5\uB2C8\uB2E4. (${saveError})`, `Could not save the preset. Your edits remain on this screen. (${saveError})`, `\u65E0\u6CD5\u4FDD\u5B58\u9884\u8BBE\uFF0C\u7F16\u8F91\u5185\u5BB9\u4ECD\u4FDD\u7559\u5728\u6B64\u9875\u9762\uFF08${saveError}\uFF09`) }),
1878
2007
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "studio-identity", children: [
1879
2008
  /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "studio-field studio-identity-name", children: [
@@ -1899,7 +2028,7 @@ function OptimizationPrototype({ locale, catalog, catalogError, onPreview, prese
1899
2028
  }
1900
2029
 
1901
2030
  // src/shell.tsx
1902
- var import_react11 = __toESM(require("react"), 1);
2031
+ var import_react12 = __toESM(require("react"), 1);
1903
2032
  var import_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
1904
2033
 
1905
2034
  // src/service-layout.tsx
@@ -1943,8 +2072,145 @@ function ServiceLayout({ locale, services, renderSlot }) {
1943
2072
  ] });
1944
2073
  }
1945
2074
 
1946
- // src/shell.tsx
2075
+ // src/update-store.ts
2076
+ function createUpdateStore(actions, current) {
2077
+ let state = { phase: "idle", current };
2078
+ const listeners = /* @__PURE__ */ new Set();
2079
+ const set = (next) => {
2080
+ state = next;
2081
+ listeners.forEach((listener) => listener());
2082
+ };
2083
+ return {
2084
+ getSnapshot: () => state,
2085
+ subscribe(listener) {
2086
+ listeners.add(listener);
2087
+ return () => {
2088
+ listeners.delete(listener);
2089
+ };
2090
+ },
2091
+ async refresh(force = false) {
2092
+ if (state.phase === "updating" || state.phase === "installed") return;
2093
+ try {
2094
+ const status = await actions.status(force);
2095
+ const phase = () => state.phase;
2096
+ if (phase() === "updating" || phase() === "installed") return;
2097
+ if (status.installed) {
2098
+ set({ phase: "installed", current: status.current, version: status.installed });
2099
+ return;
2100
+ }
2101
+ const version = status.available && !status.blocked ? status.available : void 0;
2102
+ set({ phase: "idle", current: status.current, ...version ? { version } : {} });
2103
+ } catch {
2104
+ }
2105
+ },
2106
+ async update() {
2107
+ if (state.phase !== "idle" && state.phase !== "failed") return;
2108
+ const version = state.version;
2109
+ if (!version) return;
2110
+ set({ phase: "updating", current: state.current, version });
2111
+ try {
2112
+ const result = await actions.update(version);
2113
+ if (result.status === "restart-required" || result.status === "applied") set({ phase: "installed", current: state.current, version: result.version });
2114
+ else set({ phase: "failed", current: state.current, version, message: result.message ?? result.status });
2115
+ } catch (error) {
2116
+ set({ phase: "failed", current: state.current, version, message: error instanceof Error ? error.message : String(error) });
2117
+ }
2118
+ }
2119
+ };
2120
+ }
2121
+
2122
+ // src/update-indicator.tsx
2123
+ var import_react11 = __toESM(require("react"), 1);
2124
+ var import_react_dom = require("react-dom");
1947
2125
  var import_jsx_runtime11 = require("react/jsx-runtime");
2126
+ var ko2 = {
2127
+ available: (v) => `v${v}\uB85C \uC5C5\uB370\uC774\uD2B8`,
2128
+ updating: (v) => `v${v} \uC124\uCE58 \uC911`,
2129
+ installed: (v) => `v${v}\uB97C \uC124\uCE58\uD588\uC2B5\uB2C8\uB2E4. DSH\uB97C \uB2E4\uC2DC \uC2DC\uC791\uD558\uBA74 \uC801\uC6A9\uB429\uB2C8\uB2E4.`,
2130
+ failed: (v, message2) => `v${v}\uB85C \uC5C5\uB370\uC774\uD2B8\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uB824\uBA74 \uB204\uB974\uC138\uC694. (${message2})`
2131
+ };
2132
+ var en2 = {
2133
+ available: (v) => `Update to v${v}`,
2134
+ updating: (v) => `Installing v${v}`,
2135
+ installed: (v) => `Installed v${v}. Restart DSH to use it.`,
2136
+ failed: (v, message2) => `Could not update to v${v}. Select to try again. (${message2})`
2137
+ };
2138
+ var zh2 = {
2139
+ available: (v) => `\u66F4\u65B0\u5230 v${v}`,
2140
+ updating: (v) => `\u6B63\u5728\u5B89\u88C5 v${v}`,
2141
+ installed: (v) => `\u5DF2\u5B89\u88C5 v${v}\u3002\u91CD\u542F DSH \u540E\u751F\u6548`,
2142
+ failed: (v, message2) => `\u65E0\u6CD5\u66F4\u65B0\u5230 v${v}\u3002\u70B9\u51FB\u91CD\u8BD5\uFF08${message2}\uFF09`
2143
+ };
2144
+ var copy2 = (locale) => locale.startsWith("ko") ? ko2 : locale.startsWith("zh") ? zh2 : en2;
2145
+ function useUpdateState(store) {
2146
+ return import_react11.default.useSyncExternalStore(store.subscribe, store.getSnapshot);
2147
+ }
2148
+ function updateLabel(state, locale) {
2149
+ const t = copy2(locale);
2150
+ if (state.phase === "updating") return t.updating(state.version);
2151
+ if (state.phase === "installed") return t.installed(state.version);
2152
+ if (state.phase === "failed") return t.failed(state.version, state.message);
2153
+ return state.version ? t.available(state.version) : void 0;
2154
+ }
2155
+ function UpdateGlyph({ phase, size = 14 }) {
2156
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { width: size, height: size, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2157
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "8", cy: "8", r: "6.5" }),
2158
+ phase === "installed" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M5.2 8.2l1.9 1.9 3.7-4" }) : phase === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M8 4.8v3.8M8 11.2v.01" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M8 11V5.2M5.4 7.6L8 5l2.6 2.6" })
2159
+ ] });
2160
+ }
2161
+ function VersionFooter({ store, locale }) {
2162
+ const state = useUpdateState(store);
2163
+ const label = updateLabel(state, locale);
2164
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "studio-nav-footer", children: [
2165
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "studio-mono", children: [
2166
+ "v",
2167
+ state.current
2168
+ ] }),
2169
+ label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2170
+ IconButton,
2171
+ {
2172
+ label,
2173
+ tone: state.phase === "failed" ? "error" : state.phase === "installed" ? "success" : "info",
2174
+ disabled: state.phase === "updating" || state.phase === "installed",
2175
+ busy: state.phase === "updating",
2176
+ onClick: () => {
2177
+ void store.update();
2178
+ },
2179
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UpdateGlyph, { phase: state.phase })
2180
+ }
2181
+ )
2182
+ ] });
2183
+ }
2184
+ function SidebarIcon({ size = 20, store, locale }) {
2185
+ const state = useUpdateState(store);
2186
+ const label = updateLabel(state, locale);
2187
+ const glyph = import_react11.default.useRef(null);
2188
+ const [row, setRow] = import_react11.default.useState(null);
2189
+ import_react11.default.useLayoutEffect(() => {
2190
+ setRow(glyph.current?.closest("button") ?? null);
2191
+ }, [size]);
2192
+ const wide = size <= 16;
2193
+ const marked = label !== void 0 && state.phase !== "installed";
2194
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "studio-ui studio-sidebar-glyph", children: [
2196
+ marked && !wide && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: sidebarMarkStyles }),
2197
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("svg", { ref: glyph, width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.7", "aria-hidden": "true", children: [
2198
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "3", width: "7", height: "7", rx: "2" }),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "14", y: "3", width: "7", height: "7", rx: "2" }),
2200
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "14", width: "7", height: "7", rx: "2" }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M17.5 14v7M14 17.5h7" }),
2202
+ marked && !wide && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { className: "studio-sidebar-dot", cx: "21", cy: "3", r: "3", stroke: "none" })
2203
+ ] })
2204
+ ] }),
2205
+ marked && wide && row && (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "studio-ui studio-sidebar-update", "data-phase": state.phase, title: label, role: "img", "aria-label": label, children: [
2206
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: sidebarMarkStyles }),
2207
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(UpdateGlyph, { phase: state.phase, size: 14 })
2208
+ ] }), row)
2209
+ ] });
2210
+ }
2211
+
2212
+ // src/shell.tsx
2213
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1948
2214
  var inject = ["slots", "locale", "remote"];
1949
2215
  var panel = "local-dsh-studio";
1950
2216
  async function apply(ctx) {
@@ -1965,7 +2231,18 @@ async function apply(ctx) {
1965
2231
  invocation: { kind: "direct" },
1966
2232
  parameters: [{ name: "request", wire: "request", source: "json", codec: { mode: "strict", typeSymbol: "PreviewRequest", create: () => ({ parse: parsePreviewRequest }) } }],
1967
2233
  result: { mode: "src-json" }
1968
- }, ...["listPresets", "savePreset", "applyPreset", "unapplyPreset", "roleModels", "deletePreset", "readExtensions", "saveExtensions"].map((method) => ({
2234
+ }, ...["updateStatus", "updateStudio"].map((method) => ({
2235
+ id: `${PACKAGE_NAME}#studioCatalog/${method}`,
2236
+ service: "studioCatalog",
2237
+ namespace: "studioCatalog",
2238
+ method,
2239
+ invocation: { kind: "direct" },
2240
+ parameters: [{ name: method === "updateStatus" ? "force" : "version", wire: method === "updateStatus" ? "force" : "version", source: "json", codec: { mode: "strict", typeSymbol: "string", create: () => ({ parse: (value) => {
2241
+ if (typeof value !== "string") throw new TypeError("Expected a string");
2242
+ return value;
2243
+ } }) } }],
2244
+ result: { mode: "src-json" }
2245
+ })), ...["listPresets", "savePreset", "applyPreset", "unapplyPreset", "roleModels", "deletePreset", "readExtensions", "saveExtensions"].map((method) => ({
1969
2246
  id: `${PACKAGE_NAME}#studioCatalog/${method}`,
1970
2247
  service: "studioCatalog",
1971
2248
  namespace: "studioCatalog",
@@ -1989,35 +2266,35 @@ async function apply(ctx) {
1989
2266
  };
1990
2267
  ctx.effect(() => ctx.slots.subscribe("local.dsh-studio.services", syncServices));
1991
2268
  ctx.effect(() => ctx.locale.subscribe(syncServices));
2269
+ const updateActions = /* @__PURE__ */ (() => {
2270
+ const call = async (method, arg) => {
2271
+ const service = ctx.get("remote.studioCatalog");
2272
+ const fn = service?.[method];
2273
+ if (!fn) throw new Error("Studio update API unavailable");
2274
+ const result = await fn(arg);
2275
+ if (!result.ok) throw new Error(result.error?.message ?? "Update request failed");
2276
+ return result.value;
2277
+ };
2278
+ return {
2279
+ status: (force) => call("updateStatus", force ? "force" : ""),
2280
+ update: (version) => call("updateStudio", version)
2281
+ };
2282
+ })();
2283
+ const updates = createUpdateStore(updateActions, STUDIO_VERSION);
2284
+ void updates.refresh();
1992
2285
  function StudioServices({ renderSlot }) {
1993
- const items = import_react11.default.useSyncExternalStore((listener) => {
2286
+ const items = import_react12.default.useSyncExternalStore((listener) => {
1994
2287
  serviceListeners.add(listener);
1995
2288
  return () => {
1996
2289
  serviceListeners.delete(listener);
1997
2290
  };
1998
2291
  }, () => services);
1999
- const locale = import_react11.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2000
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StudioSurface, { className: "studio-shell", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("main", { className: "studio-page", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ServiceLayout, { locale, services: items, renderSlot }) }) });
2292
+ const locale = import_react12.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2293
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(StudioSurface, { className: "studio-shell", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("main", { className: "studio-page", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ServiceLayout, { locale, services: items, renderSlot }) }) });
2001
2294
  }
2002
2295
  function Icon({ size = 20 }) {
2003
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2004
- "svg",
2005
- {
2006
- width: size,
2007
- height: size,
2008
- viewBox: "0 0 24 24",
2009
- fill: "none",
2010
- stroke: "currentColor",
2011
- strokeWidth: "1.7",
2012
- "aria-hidden": "true",
2013
- children: [
2014
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "3", width: "7", height: "7", rx: "2" }),
2015
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "14", y: "3", width: "7", height: "7", rx: "2" }),
2016
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("rect", { x: "3", y: "14", width: "7", height: "7", rx: "2" }),
2017
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M17.5 14v7M14 17.5h7" })
2018
- ]
2019
- }
2020
- );
2296
+ const locale = import_react12.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2297
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SidebarIcon, { size, store: updates, locale });
2021
2298
  }
2022
2299
  ctx.slots.inject(
2023
2300
  "main",
@@ -2033,10 +2310,10 @@ async function apply(ctx) {
2033
2310
  )
2034
2311
  );
2035
2312
  function OptimizationPage() {
2036
- const locale = import_react11.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2037
- const [catalog, setCatalog] = import_react11.default.useState();
2038
- const [catalogError, setCatalogError] = import_react11.default.useState("");
2039
- import_react11.default.useEffect(() => {
2313
+ const locale = import_react12.default.useSyncExternalStore((listener) => ctx.locale.subscribe(listener), () => ctx.locale.getLocale().active);
2314
+ const [catalog, setCatalog] = import_react12.default.useState();
2315
+ const [catalogError, setCatalogError] = import_react12.default.useState("");
2316
+ import_react12.default.useEffect(() => {
2040
2317
  let active = true;
2041
2318
  const remote2 = ctx.get("remote.studioCatalog");
2042
2319
  if (!remote2) {
@@ -2054,7 +2331,7 @@ async function apply(ctx) {
2054
2331
  active = false;
2055
2332
  };
2056
2333
  }, []);
2057
- const presetActions = import_react11.default.useMemo(() => {
2334
+ const presetActions = import_react12.default.useMemo(() => {
2058
2335
  const call = async (method, ...args) => {
2059
2336
  const service = ctx.get("remote.studioCatalog");
2060
2337
  const result = await service[method](...args);
@@ -2070,7 +2347,7 @@ async function apply(ctx) {
2070
2347
  delete: (id) => call("deletePreset", id)
2071
2348
  };
2072
2349
  }, []);
2073
- const extensionActions = import_react11.default.useMemo(() => {
2350
+ const extensionActions = import_react12.default.useMemo(() => {
2074
2351
  const call = async (method, ...args) => {
2075
2352
  const service = ctx.get("remote.studioCatalog");
2076
2353
  const result = await service[method](...args);
@@ -2081,7 +2358,10 @@ async function apply(ctx) {
2081
2358
  setCatalog(await call("saveExtensions", payload));
2082
2359
  } };
2083
2360
  }, []);
2084
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(OptimizationPrototype, { extensionActions, presetActions, locale, catalog, catalogError, onPreview: async (request) => {
2361
+ import_react12.default.useEffect(() => {
2362
+ void updates.refresh();
2363
+ }, []);
2364
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(OptimizationPrototype, { navFooter: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(VersionFooter, { store: updates, locale }), extensionActions, presetActions, locale, catalog, catalogError, onPreview: async (request) => {
2085
2365
  const service = ctx.get("remote.studioCatalog");
2086
2366
  const response = await service.preview(request);
2087
2367
  if (!response.ok) throw new Error(response.error?.message ?? "Preview failed");