dsh-advisor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +7 -0
  3. package/README.md +303 -0
  4. package/README.zh.md +166 -0
  5. package/cordis.patch.yml +6 -0
  6. package/lib/advisor-runtime.d.ts +242 -0
  7. package/lib/advisor-runtime.js +662 -0
  8. package/lib/advisor-runtime.js.map +1 -0
  9. package/lib/client/advisor-card.d.ts +90 -0
  10. package/lib/client/advisor-store.d.ts +310 -0
  11. package/lib/client/index.d.ts +39 -0
  12. package/lib/client/locales.d.ts +40 -0
  13. package/lib/client.d.ts +1 -0
  14. package/lib/client.js +840 -0
  15. package/lib/commands.d.ts +136 -0
  16. package/lib/commands.js +185 -0
  17. package/lib/commands.js.map +1 -0
  18. package/lib/config.d.ts +74 -0
  19. package/lib/config.js +93 -0
  20. package/lib/config.js.map +1 -0
  21. package/lib/delivery.d.ts +129 -0
  22. package/lib/delivery.js +169 -0
  23. package/lib/delivery.js.map +1 -0
  24. package/lib/emission-guard.d.ts +99 -0
  25. package/lib/emission-guard.js +155 -0
  26. package/lib/emission-guard.js.map +1 -0
  27. package/lib/gateway.d.ts +116 -0
  28. package/lib/gateway.js +214 -0
  29. package/lib/gateway.js.map +1 -0
  30. package/lib/index.d.ts +48 -0
  31. package/lib/index.js +485 -0
  32. package/lib/index.js.map +1 -0
  33. package/lib/kinds.d.ts +38 -0
  34. package/lib/kinds.js +24 -0
  35. package/lib/kinds.js.map +1 -0
  36. package/lib/prompts.d.ts +22 -0
  37. package/lib/prompts.js +38 -0
  38. package/lib/prompts.js.map +1 -0
  39. package/lib/settings.d.ts +96 -0
  40. package/lib/settings.js +141 -0
  41. package/lib/settings.js.map +1 -0
  42. package/lib/transcript.d.ts +257 -0
  43. package/lib/transcript.js +530 -0
  44. package/lib/transcript.js.map +1 -0
  45. package/package.json +90 -0
  46. package/scripts/build-client.mjs +268 -0
package/lib/client.js ADDED
@@ -0,0 +1,840 @@
1
+ window.__ModuleLoader__.load({ id: "dsh-advisor", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ "use strict";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
23
+
24
+ // src/client/index.ts
25
+ var index_exports = {};
26
+ __export(index_exports, {
27
+ apply: () => apply,
28
+ inject: () => inject,
29
+ refreshIfLoaded: () => refreshIfLoaded
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+ var import_dsh_client_web_react = require("@deepseek-ai/dsh-client-web-react");
33
+
34
+ // src/client/advisor-card.tsx
35
+ var import_react = require("react");
36
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
37
+
38
+ var css = `.fgpdlq_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.fgpdlq_card:hover{border-color:var(--dsw-alias-label-dimmed)}.fgpdlq_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.fgpdlq_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.fgpdlq_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.fgpdlq_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.fgpdlq_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.fgpdlq_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.fgpdlq_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.fgpdlq_chevronOpen{transform:rotate(180deg)}.fgpdlq_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.fgpdlq_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.fgpdlq_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.fgpdlq_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.fgpdlq_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.fgpdlq_discard,.fgpdlq_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.fgpdlq_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.fgpdlq_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.fgpdlq_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.fgpdlq_discard:disabled,.fgpdlq_save:disabled{opacity:.4;cursor:default}.fgpdlq_discard:focus-visible,.fgpdlq_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.fgpdlq_notice{color:var(--dsw-alias-state-warn-label);margin:12px 0 0;font-size:12px;line-height:18px}.fgpdlq_savedNotice{color:var(--dsw-alias-state-success-primary);margin:12px 0 0;font-size:12px;line-height:18px}.fgpdlq_error{color:var(--dsw-alias-state-error-primary);margin:12px 0 0;font-size:12px;line-height:18px}.fgpdlq_form{flex-direction:column;gap:12px;padding:12px 0 0;display:flex}.fgpdlq_checkboxRow{align-items:center;gap:8px;display:flex}.fgpdlq_checkLabel{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.fgpdlq_checkbox{width:16px;height:16px;accent-color:var(--dsw-alias-brand-primary);cursor:pointer;margin:0}.fgpdlq_checkbox:disabled{opacity:.4;cursor:default}.fgpdlq_checkbox:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}.fgpdlq_fieldset{border:none;flex-direction:column;gap:12px;margin:0;padding:0;display:flex}.fgpdlq_field{flex-direction:column;gap:6px;display:flex}.fgpdlq_fieldLabel{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;font-size:12px;font-weight:500;line-height:18px;display:inline-flex}.fgpdlq_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:100%;height:32px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 10px;font-size:14px;line-height:22px}select.fgpdlq_input{cursor:pointer;max-width:240px}.fgpdlq_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.fgpdlq_input::placeholder{color:var(--dsw-alias-label-dimmed)}.fgpdlq_input:disabled{opacity:.6;cursor:default}.fgpdlq_selectInput{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-position:right 12px center;background-repeat:no-repeat;background-size:12px 12px;padding-right:32px}.fgpdlq_textarea{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);width:100%;min-height:96px;font:inherit;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);resize:vertical;border-radius:8px;padding:8px 10px;font-size:14px;line-height:22px}.fgpdlq_textarea:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.fgpdlq_textarea:disabled{opacity:.6;cursor:default}.fgpdlq_numberFields{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;display:grid}.fgpdlq_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.fgpdlq_warnHint{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}`;
39
+ var tagId = "dsh-advisor/advisor-card.module.css";
40
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
41
+ const tag = document.createElement("style");
42
+ tag.dataset.plugin = "dsh-advisor";
43
+ tag.dataset.pluginCss = tagId;
44
+ tag.textContent = css;
45
+ document.head.appendChild(tag);
46
+ }
47
+ var advisor_card_module_css_default = { "body": "fgpdlq_body", "card": "fgpdlq_card", "cardOpen": "fgpdlq_cardOpen", "checkLabel": "fgpdlq_checkLabel", "checkbox": "fgpdlq_checkbox", "checkboxRow": "fgpdlq_checkboxRow", "chevron": "fgpdlq_chevron", "chevronOpen": "fgpdlq_chevronOpen", "description": "fgpdlq_description", "discard": "fgpdlq_discard", "error": "fgpdlq_error", "failed": "fgpdlq_failed", "field": "fgpdlq_field", "fieldLabel": "fgpdlq_fieldLabel", "fieldset": "fgpdlq_fieldset", "footer": "fgpdlq_footer", "form": "fgpdlq_form", "headText": "fgpdlq_headText", "header": "fgpdlq_header", "hint": "fgpdlq_hint", "input": "fgpdlq_input", "name": "fgpdlq_name", "notice": "fgpdlq_notice", "numberFields": "fgpdlq_numberFields", "pending": "fgpdlq_pending", "readOnly": "fgpdlq_readOnly", "save": "fgpdlq_save", "savedNotice": "fgpdlq_savedNotice", "selectInput": "fgpdlq_selectInput", "textarea": "fgpdlq_textarea", "warnHint": "fgpdlq_warnHint" };
48
+
49
+ // src/client/advisor-card.tsx
50
+ var import_jsx_runtime = require("react/jsx-runtime");
51
+ function failureCopy(failure, t) {
52
+ switch (failure.kind) {
53
+ case "gate":
54
+ return void 0;
55
+ case "message":
56
+ return failure.message;
57
+ }
58
+ }
59
+ function AdvisorCard(props) {
60
+ const { controller, useSnapshot, t } = props;
61
+ const state = useSnapshot((snapshot) => snapshot);
62
+ const [userOpen, setUserOpen] = (0, import_react.useState)(false);
63
+ const degraded = state.status === "ready" ? !state.advisorPresent : state.degraded;
64
+ const open = userOpen || state.status === "error" || degraded;
65
+ if (state.status === "idle") void controller.load();
66
+ const title = t("title");
67
+ const header = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
68
+ "button",
69
+ {
70
+ type: "button",
71
+ className: advisor_card_module_css_default["header"],
72
+ "aria-expanded": open,
73
+ "aria-label": `${t(open ? "collapse" : "expand")}: ${title}`,
74
+ onClick: () => {
75
+ if (!degraded && state.status !== "error") setUserOpen(!userOpen);
76
+ },
77
+ children: [
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: advisor_card_module_css_default["headText"], children: [
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: advisor_card_module_css_default["name"], children: title }),
80
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: advisor_card_module_css_default["description"], children: t("intro") })
81
+ ] }),
82
+ state.dirty ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: advisor_card_module_css_default["pending"], children: t("unsaved") }) : null,
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
84
+ import_dsh_client_ui_primitives.IconChevronDownOutline14,
85
+ {
86
+ className: open ? `${advisor_card_module_css_default["chevron"]} ${advisor_card_module_css_default["chevronOpen"]}` : advisor_card_module_css_default["chevron"]
87
+ }
88
+ )
89
+ ]
90
+ }
91
+ );
92
+ let body;
93
+ if (state.status === "error") {
94
+ body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["body"], children: [
95
+ state.applyState.kind === "saved" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["savedNotice"], role: "status", children: t("saved") }) : null,
96
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["error"], children: `${t("loadFailed")}: ${state.error ?? ""}` }),
97
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: advisor_card_module_css_default["footer"], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: advisor_card_module_css_default["discard"], onClick: () => {
98
+ void controller.load();
99
+ }, children: t("retry") }) })
100
+ ] });
101
+ } else if (degraded) {
102
+ body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["body"], children: [
103
+ state.applyState.kind === "saved" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["savedNotice"], role: "status", children: t("saved") }) : null,
104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["notice"], role: "status", children: t("namespaceUnavailable") }),
105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: advisor_card_module_css_default["footer"], children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: advisor_card_module_css_default["discard"], onClick: () => {
106
+ void controller.load();
107
+ }, children: t("retry") }) })
108
+ ] });
109
+ } else if (state.status === "ready") {
110
+ const { draft, providers, writable, applyState } = state;
111
+ const providerEmpty = draft.provider === void 0;
112
+ const modelEmpty = draft.model === void 0;
113
+ const gateFailed = draft.enabled && (providerEmpty || modelEmpty);
114
+ const saving = applyState.kind === "saving";
115
+ const busy = !writable || saving;
116
+ const selectedModels = draft.provider === void 0 ? [] : state.modelsByProvider[draft.provider] ?? [];
117
+ const modelsEmpty = draft.provider !== void 0 && Object.hasOwn(state.modelsEmptyReason, draft.provider);
118
+ const providerStale = draft.provider !== void 0 && !providers.some((option) => option.provider === draft.provider);
119
+ const modelStale = !providerStale && draft.model !== void 0 && selectedModels.length > 0 && !selectedModels.some((option) => option.id === draft.model);
120
+ const errorText = applyState.kind === "error" ? failureCopy(applyState.failure, t) : void 0;
121
+ const saveDisabled = !state.dirty || gateFailed || saving || !writable;
122
+ const discardDisabled = !state.dirty || saving;
123
+ body = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["body"], children: [
124
+ !writable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["readOnly"], role: "status", children: t("readOnly") }) : null,
125
+ applyState.kind === "saved" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["savedNotice"], role: "status", children: t("saved") }) : null,
126
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["form"], children: [
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["checkboxRow"], children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-enabled", className: advisor_card_module_css_default["checkLabel"], children: t("enabled") }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
130
+ "input",
131
+ {
132
+ id: "advisor-enabled",
133
+ type: "checkbox",
134
+ className: advisor_card_module_css_default["checkbox"],
135
+ checked: draft.enabled,
136
+ disabled: busy,
137
+ onChange: (event) => {
138
+ controller.setEnabled(event.target.checked);
139
+ }
140
+ }
141
+ )
142
+ ] }),
143
+ draft.enabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("fieldset", { className: advisor_card_module_css_default["fieldset"], children: [
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["field"], children: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-provider", className: advisor_card_module_css_default["fieldLabel"], children: t("provider") }),
146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
147
+ "select",
148
+ {
149
+ id: "advisor-provider",
150
+ "aria-label": t("provider"),
151
+ className: `${advisor_card_module_css_default["input"]} ${advisor_card_module_css_default["selectInput"]}`,
152
+ value: draft.provider ?? "",
153
+ disabled: busy,
154
+ onChange: (event) => {
155
+ controller.setProvider(event.target.value);
156
+ },
157
+ children: [
158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: t("providerPlaceholder") }),
159
+ providers.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.provider, children: option.displayName }, option.provider))
160
+ ]
161
+ }
162
+ ),
163
+ providers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["hint"], children: t("noProviders") }) : null,
164
+ providerEmpty ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["warnHint"], children: t("providerRequired") }) : null,
165
+ providerStale ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["hint"], children: t("staleProvider") }) : null
166
+ ] }),
167
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["field"], children: [
168
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-model", className: advisor_card_module_css_default["fieldLabel"], children: t("model") }),
169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
170
+ "select",
171
+ {
172
+ id: "advisor-model",
173
+ "aria-label": t("model"),
174
+ className: `${advisor_card_module_css_default["input"]} ${advisor_card_module_css_default["selectInput"]}`,
175
+ value: draft.model ?? "",
176
+ disabled: busy || draft.provider === void 0 || selectedModels.length === 0,
177
+ onChange: (event) => {
178
+ controller.setModel(event.target.value);
179
+ },
180
+ children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: t("modelPlaceholder") }),
182
+ selectedModels.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option.id, children: option.name }, option.id))
183
+ ]
184
+ }
185
+ ),
186
+ modelsEmpty ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["hint"], children: t("noModels") }) : null,
187
+ modelStale ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["hint"], children: t("staleModel") }) : null,
188
+ !providerEmpty && modelEmpty ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["warnHint"], children: t("modelRequired") }) : null
189
+ ] })
190
+ ] }) : null,
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["field"], children: [
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-system-prompt", className: advisor_card_module_css_default["fieldLabel"], children: t("systemPrompt") }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
194
+ "textarea",
195
+ {
196
+ id: "advisor-system-prompt",
197
+ "aria-label": t("systemPrompt"),
198
+ placeholder: t("systemPromptPlaceholder"),
199
+ className: advisor_card_module_css_default["textarea"],
200
+ value: draft.systemPrompt,
201
+ disabled: busy,
202
+ onChange: (event) => {
203
+ controller.setSystemPrompt(event.target.value);
204
+ }
205
+ }
206
+ )
207
+ ] }),
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["numberFields"], children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["field"], children: [
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-immune-turns", className: advisor_card_module_css_default["fieldLabel"], children: t("immuneTurns") }),
211
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
212
+ "input",
213
+ {
214
+ id: "advisor-immune-turns",
215
+ "aria-label": t("immuneTurns"),
216
+ className: advisor_card_module_css_default["input"],
217
+ type: "number",
218
+ min: 0,
219
+ step: 1,
220
+ value: draft.immuneTurns ?? "",
221
+ disabled: busy,
222
+ onChange: (event) => {
223
+ controller.setImmuneTurns(event.target.value === "" ? void 0 : Number(event.target.value));
224
+ }
225
+ }
226
+ )
227
+ ] }),
228
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["field"], children: [
229
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-max-delta-messages", className: advisor_card_module_css_default["fieldLabel"], children: t("maxDeltaMessages") }),
230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
231
+ "input",
232
+ {
233
+ id: "advisor-max-delta-messages",
234
+ "aria-label": t("maxDeltaMessages"),
235
+ className: advisor_card_module_css_default["input"],
236
+ type: "number",
237
+ min: 0,
238
+ step: 1,
239
+ value: draft.maxDeltaMessages ?? "",
240
+ disabled: busy,
241
+ onChange: (event) => {
242
+ controller.setMaxDeltaMessages(event.target.value === "" ? void 0 : Number(event.target.value));
243
+ }
244
+ }
245
+ )
246
+ ] })
247
+ ] })
248
+ ] }),
249
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: advisor_card_module_css_default["footer"], children: [
250
+ errorText === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: advisor_card_module_css_default["failed"], role: "status", children: errorText }),
251
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
252
+ "button",
253
+ {
254
+ type: "button",
255
+ className: advisor_card_module_css_default["discard"],
256
+ disabled: discardDisabled,
257
+ onClick: () => {
258
+ controller.discard();
259
+ },
260
+ children: t("discard")
261
+ }
262
+ ),
263
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
264
+ "button",
265
+ {
266
+ type: "button",
267
+ className: advisor_card_module_css_default["save"],
268
+ disabled: saveDisabled,
269
+ onClick: () => {
270
+ void controller.apply();
271
+ },
272
+ children: t(saving ? "saving" : "save")
273
+ }
274
+ )
275
+ ] })
276
+ ] });
277
+ } else {
278
+ body = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: advisor_card_module_css_default["body"] });
279
+ }
280
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: open ? `${advisor_card_module_css_default["card"]} ${advisor_card_module_css_default["cardOpen"]}` : advisor_card_module_css_default["card"], children: [
281
+ header,
282
+ open ? body : null
283
+ ] });
284
+ }
285
+
286
+ // src/client/advisor-store.ts
287
+ var import_client = require("@deepseek-ai/dsh-client-runtime/client");
288
+ var import_dsh_client_schema_form = require("@deepseek-ai/dsh-client-schema-form");
289
+ function defaultDraft() {
290
+ return { enabled: false, systemPrompt: "", immuneTurns: 3, maxDeltaMessages: 60 };
291
+ }
292
+ function stringField(value) {
293
+ return typeof value === "string" && value.trim().length > 0 ? value : void 0;
294
+ }
295
+ function numberField(value, fallback) {
296
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
297
+ }
298
+ function draftOfConfig(config) {
299
+ return {
300
+ enabled: config?.enabled === true,
301
+ provider: stringField(config?.provider),
302
+ model: stringField(config?.model),
303
+ systemPrompt: typeof config?.systemPrompt === "string" ? config.systemPrompt : "",
304
+ immuneTurns: numberField(config?.immuneTurns, 3),
305
+ maxDeltaMessages: numberField(config?.maxDeltaMessages, 60)
306
+ };
307
+ }
308
+ function gateFailure(draft) {
309
+ if (!draft.enabled) return void 0;
310
+ if (draft.provider === void 0) return "provider";
311
+ if (draft.model === void 0) return "model";
312
+ return void 0;
313
+ }
314
+ function profileModels(profile) {
315
+ const models = (0, import_dsh_client_schema_form.getPath)(profile, ["models"]);
316
+ if (!Array.isArray(models)) return void 0;
317
+ return models.flatMap((entry) => {
318
+ if (typeof entry !== "object" || entry === null) return [];
319
+ const model = entry;
320
+ if (typeof model.id !== "string" || model.id.length === 0) return [];
321
+ return [{
322
+ id: model.id,
323
+ name: typeof model.name === "string" ? model.name : model.id,
324
+ ...typeof model.description === "string" ? { description: model.description } : {}
325
+ }];
326
+ });
327
+ }
328
+ var AdvisorSettingsStore = class {
329
+ /**
330
+ * @param api - the wire face (settings/llm domains) for the provider/model
331
+ * directory (the advisor namespace is NOT on that wire).
332
+ * @param rpc - the connection's generic RPC caller for the host gateway
333
+ * channel (`/api`), injected from the connection handle.
334
+ */
335
+ constructor(api, rpc) {
336
+ __publicField(this, "api", api);
337
+ __publicField(this, "rpc", rpc);
338
+ /** The snapshot the card renders from (uSES-safe store). */
339
+ __publicField(this, "store", (0, import_client.createSnapshotStore)({
340
+ status: "idle",
341
+ error: null,
342
+ writable: false,
343
+ providers: [],
344
+ modelsByProvider: {},
345
+ modelsEmptyReason: {},
346
+ namespaces: {},
347
+ advisorPresent: false,
348
+ // qc1 S-2: the degraded latch defaults false — a first mount / healthy
349
+ // card is never degraded, so the healthy card stays collapsed through its
350
+ // first load (the latch only flips on a settled degraded ready state).
351
+ degraded: false,
352
+ // KD-U2: dirty derives from the patch diff against the seed
353
+ // (recomputeDirty below — patchFor non-empty), recomputed on
354
+ // load/mutation/discard/apply; the store default is clean.
355
+ dirty: false,
356
+ draft: defaultDraft(),
357
+ applyState: { kind: "idle" }
358
+ }));
359
+ /** Latest load wins; an older response never overwrites a newer one. */
360
+ __publicField(this, "generation", 0);
361
+ /** Host-scoped model catalog; cached only on success (a transient failure stays uncached and is refetched). */
362
+ __publicField(this, "catalog");
363
+ /** In-flight host-scoped catalog fetch; one promise shared across providers. */
364
+ __publicField(this, "catalogPromise");
365
+ /**
366
+ * Bumped by every successful load(): an in-flight catalog fetch started
367
+ * before a bump is stale (the models/changed invalidation that triggered
368
+ * the reload happened mid-fetch) and caches nothing (qc1 W-1 / qc3 S-1).
369
+ */
370
+ __publicField(this, "catalogGeneration", 0);
371
+ /** In-flight model resolutions (one per provider). */
372
+ __publicField(this, "inflightModels", /* @__PURE__ */ new Set());
373
+ /** The draft is seeded once (first load); refreshes never clobber edits. */
374
+ __publicField(this, "draftSeeded", false);
375
+ /** The resolved advisor config at the last get/apply (patch diff baseline). */
376
+ __publicField(this, "seed", defaultDraft());
377
+ }
378
+ /**
379
+ * Refresh the whole page snapshot: the provider directory and the settings
380
+ * namespaces in parallel, then the advisor config over the gateway channel,
381
+ * then the provider join and the draft seed. A provider-directory failure
382
+ * keeps the last good rows and surfaces the error; an `advisor.get` failure
383
+ * is NOT a page error (KD-G5 — the card shows the config-channel notice,
384
+ * the provider directory stays usable). The draft is seeded only on the
385
+ * first load — pushed refreshes never discard in-progress edits (mirror the
386
+ * ProviderEditor draft lifetime).
387
+ * @returns nothing; the snapshot carries the outcome.
388
+ */
389
+ async load() {
390
+ const generation = ++this.generation;
391
+ this.store.update((s) => {
392
+ s.status = "loading";
393
+ s.error = null;
394
+ });
395
+ let providers;
396
+ let writable;
397
+ let views;
398
+ try {
399
+ const [providersResponse, settingsResponse] = await Promise.all([
400
+ this.api.llm.providers({}),
401
+ this.api.settings.describe({})
402
+ ]);
403
+ if (!providersResponse.result.ok) throw new Error(providersResponse.result.error.message);
404
+ if (!settingsResponse.result.ok) throw new Error(settingsResponse.result.error.message);
405
+ providers = providersResponse.result.value.providers;
406
+ writable = settingsResponse.result.value.writable;
407
+ views = settingsResponse.result.value.namespaces;
408
+ } catch (error) {
409
+ if (generation !== this.generation) return;
410
+ this.store.update((s) => {
411
+ s.status = "error";
412
+ s.error = error instanceof Error ? error.message : String(error);
413
+ });
414
+ return;
415
+ }
416
+ if (generation !== this.generation) return;
417
+ let config;
418
+ try {
419
+ const getResult = await this.rpc.call("/api", "advisor/get", { args: {} });
420
+ if (getResult.ok) {
421
+ config = getResult.value.config;
422
+ }
423
+ } catch {
424
+ }
425
+ if (generation !== this.generation) return;
426
+ const namespaces = Object.fromEntries(
427
+ views.map((view) => [view.ns, view])
428
+ );
429
+ const options = [];
430
+ for (const entry of providers) {
431
+ const namespace = namespaces[entry.settingsNs];
432
+ const configured = namespace !== void 0 && (entry.settingsPath.length === 0 || (0, import_dsh_client_schema_form.getPath)(namespace.value, entry.settingsPath) !== void 0);
433
+ if (!configured) continue;
434
+ options.push({
435
+ provider: entry.provider,
436
+ displayName: entry.displayName,
437
+ settingsNs: entry.settingsNs,
438
+ settingsPath: entry.settingsPath,
439
+ configured
440
+ });
441
+ }
442
+ this.seed = draftOfConfig(config);
443
+ if (!this.draftSeeded && config !== void 0) {
444
+ this.store.update((s) => {
445
+ s.draft = this.seed;
446
+ });
447
+ this.draftSeeded = true;
448
+ }
449
+ this.catalogGeneration += 1;
450
+ this.catalog = void 0;
451
+ this.catalogPromise = void 0;
452
+ this.inflightModels.clear();
453
+ this.store.update((s) => {
454
+ s.status = "ready";
455
+ s.error = null;
456
+ s.writable = writable;
457
+ s.providers = options;
458
+ s.namespaces = namespaces;
459
+ s.advisorPresent = config !== void 0;
460
+ s.degraded = config === void 0;
461
+ s.modelsByProvider = {};
462
+ s.modelsEmptyReason = {};
463
+ if (config !== void 0) s.dirty = this.recomputeDirty(s.draft);
464
+ });
465
+ const selected = this.seed.provider;
466
+ if (selected !== void 0 && options.some((option) => option.provider === selected)) {
467
+ void this.ensureModels(selected);
468
+ }
469
+ }
470
+ /**
471
+ * Resolve the model options for one provider (KD-S2): profile-declared
472
+ * `models` win; otherwise the `llm.models` catalog group for that provider;
473
+ * neither → empty options + a reason. The host-scoped catalog is fetched at
474
+ * most once (concurrent first resolutions share one in-flight fetch) and
475
+ * cached only on success. No-op for unconfigured providers.
476
+ * @param provider - provider route id.
477
+ * @returns nothing; the snapshot carries the outcome.
478
+ */
479
+ async ensureModels(provider) {
480
+ if (this.inflightModels.has(provider)) return;
481
+ const state = this.store.getSnapshot();
482
+ if (Object.hasOwn(state.modelsByProvider, provider) || Object.hasOwn(state.modelsEmptyReason, provider)) return;
483
+ const option = state.providers.find((candidate) => candidate.provider === provider);
484
+ if (option === void 0) return;
485
+ const namespace = state.namespaces[option.settingsNs];
486
+ const profile = namespace !== void 0 ? (0, import_dsh_client_schema_form.getPath)(namespace.value, option.settingsPath) : void 0;
487
+ const declared = profileModels(profile);
488
+ if (declared !== void 0) {
489
+ this.store.update((s) => {
490
+ if (declared.length > 0) {
491
+ s.modelsByProvider = { ...s.modelsByProvider, [provider]: declared };
492
+ } else {
493
+ s.modelsEmptyReason = { ...s.modelsEmptyReason, [provider]: "profile-empty" };
494
+ }
495
+ });
496
+ return;
497
+ }
498
+ this.inflightModels.add(provider);
499
+ try {
500
+ let group;
501
+ for (; ; ) {
502
+ if (this.catalog === void 0) {
503
+ if (this.catalogPromise === void 0) {
504
+ const fetch = this.fetchCatalog();
505
+ const tracked = fetch.finally(() => {
506
+ if (this.catalogPromise === tracked) this.catalogPromise = void 0;
507
+ });
508
+ this.catalogPromise = tracked;
509
+ }
510
+ const generation = this.catalogGeneration;
511
+ await this.catalogPromise;
512
+ if (generation !== this.catalogGeneration) continue;
513
+ }
514
+ group = this.catalog?.find((candidate) => candidate.id === provider);
515
+ break;
516
+ }
517
+ const options = group?.models ?? [];
518
+ this.store.update((s) => {
519
+ if (options.length > 0) {
520
+ s.modelsByProvider = { ...s.modelsByProvider, [provider]: options };
521
+ } else {
522
+ const reason = group === void 0 ? "unavailable" : "catalog-empty";
523
+ s.modelsEmptyReason = { ...s.modelsEmptyReason, [provider]: reason };
524
+ }
525
+ });
526
+ } finally {
527
+ this.inflightModels.delete(provider);
528
+ }
529
+ }
530
+ /**
531
+ * Fetch the host-scoped model catalog. A failure (wire rejection or throw)
532
+ * leaves the cache empty so the next resolution refetches — a transient
533
+ * failure is never sticky for the store lifetime. A fetch started before a
534
+ * load() bumped `catalogGeneration` is stale (the invalidation that caused
535
+ * the reload happened mid-fetch) and caches nothing.
536
+ */
537
+ async fetchCatalog() {
538
+ const generation = this.catalogGeneration;
539
+ try {
540
+ const response = await this.api.llm.models({});
541
+ if (generation !== this.catalogGeneration) return;
542
+ this.catalog = response.result.ok ? response.result.value.groups : void 0;
543
+ } catch {
544
+ if (generation === this.catalogGeneration) this.catalog = void 0;
545
+ }
546
+ }
547
+ /** Set the enabled switch (gate fields become required while on). */
548
+ setEnabled(enabled) {
549
+ this.setField("enabled", enabled);
550
+ }
551
+ /** Set or clear the provider ('' clears); switches invalidate the chosen model. */
552
+ setProvider(provider) {
553
+ const trimmed = provider.trim();
554
+ this.store.update((s) => {
555
+ const draft = s.draft;
556
+ const withoutProvider = trimmed.length === 0 ? (0, import_dsh_client_schema_form.deletePath)(draft, ["provider"]) : (0, import_dsh_client_schema_form.setPath)(draft, ["provider"], trimmed);
557
+ s.draft = (0, import_dsh_client_schema_form.deletePath)(withoutProvider, ["model"]);
558
+ s.applyState = { kind: "idle" };
559
+ s.dirty = this.recomputeDirty(s.draft);
560
+ });
561
+ if (trimmed.length > 0) void this.ensureModels(trimmed);
562
+ }
563
+ /** Set or clear the model ('' clears). */
564
+ setModel(model) {
565
+ const trimmed = model.trim();
566
+ if (trimmed.length === 0) this.clearField("model");
567
+ else this.setField("model", trimmed);
568
+ }
569
+ /** Set the system prompt override. */
570
+ setSystemPrompt(prompt) {
571
+ this.setField("systemPrompt", prompt);
572
+ }
573
+ /** Set the immune-turns cooldown (int ≥ 0; non-numeric input keeps the current value; undefined clears → omitted from patch). */
574
+ setImmuneTurns(value) {
575
+ if (value === void 0) {
576
+ this.clearField("immuneTurns");
577
+ return;
578
+ }
579
+ this.setField("immuneTurns", this.clampInt(value, this.store.getSnapshot().draft.immuneTurns ?? 0));
580
+ }
581
+ /** Set the delta-message window (int ≥ 0; non-numeric input keeps the current value; undefined clears → omitted from patch). */
582
+ setMaxDeltaMessages(value) {
583
+ if (value === void 0) {
584
+ this.clearField("maxDeltaMessages");
585
+ return;
586
+ }
587
+ this.setField("maxDeltaMessages", this.clampInt(value, this.store.getSnapshot().draft.maxDeltaMessages ?? 0));
588
+ }
589
+ /**
590
+ * Refuse in a read-only environment (store-side defense-in-depth for the
591
+ * W-1 invariant — a write must never be issued when the UI declares the
592
+ * settings service read-only), then validate the draft (KD-S4 gate) and
593
+ * write the changed keys as a config patch through the gateway channel
594
+ * (`/api/advisor/set`). Any failure (business rejection or transport)
595
+ * surfaces the message failure and keeps the form for retry — the gateway
596
+ * merge has no revision guard, so the old settings-conflict branch is
597
+ * replaced by plain error handling (KD-G3). Nothing to write → the apply
598
+ * reports saved without a call.
599
+ * @returns nothing; the apply state carries the outcome.
600
+ */
601
+ async apply() {
602
+ const state = this.store.getSnapshot();
603
+ if (!state.writable) {
604
+ this.store.update((s) => {
605
+ s.applyState = {
606
+ kind: "error",
607
+ failure: { kind: "message", message: "advisor: settings service is read-only \u2014 configuration cannot be written" }
608
+ };
609
+ });
610
+ return;
611
+ }
612
+ const gate = gateFailure(state.draft);
613
+ if (gate !== void 0) {
614
+ this.store.update((s) => {
615
+ s.applyState = { kind: "error", failure: { kind: "gate", reason: gate } };
616
+ });
617
+ return;
618
+ }
619
+ const patch = this.patchFor(state.draft);
620
+ if (Object.keys(patch).length === 0) {
621
+ this.store.update((s) => {
622
+ s.applyState = { kind: "saved" };
623
+ s.dirty = this.recomputeDirty(s.draft);
624
+ });
625
+ return;
626
+ }
627
+ this.store.update((s) => {
628
+ s.applyState = { kind: "saving" };
629
+ });
630
+ try {
631
+ const result = await this.rpc.call("/api", "advisor/set", { args: { patch } });
632
+ if (!result.ok) {
633
+ this.store.update((s) => {
634
+ s.applyState = { kind: "error", failure: { kind: "message", message: result.error.message } };
635
+ });
636
+ return;
637
+ }
638
+ this.seed = draftOfConfig(result.value.config);
639
+ this.store.update((s) => {
640
+ s.applyState = { kind: "saved" };
641
+ s.dirty = this.recomputeDirty(s.draft);
642
+ });
643
+ await this.load();
644
+ } catch (error) {
645
+ this.store.update((s) => {
646
+ s.applyState = {
647
+ kind: "error",
648
+ failure: { kind: "message", message: error instanceof Error ? error.message : String(error) }
649
+ };
650
+ });
651
+ }
652
+ }
653
+ /**
654
+ * Drop the current draft edits and rewind the form to the last-known host
655
+ * config (the seed from the last get/apply). The card chrome mirrors the
656
+ * upstream plugin cards' Save/Discard pair (KD-4): unlike the upstream
657
+ * staged CardForm, this store's edits mutate the draft in place, so discard
658
+ * rewinds the draft instead of dropping a staging layer. Pure client-side
659
+ * revert — no gateway write (the host stays the single fact source; a
660
+ * follow-up apply diffs against the restored seed and sends nothing).
661
+ *
662
+ * Invariant (qc3 N-2): a mid-apply discard is UNGUARDED at the store level
663
+ * BY DESIGN — the UI disables Discard while a save is in flight
664
+ * (`disabled={busy}` on the card, `busy = !writable || saving`), so the
665
+ * interleaving cannot occur from the UI and no behavior guard is added
666
+ * here. A programmatic mid-apply discard would only rewind the draft before
667
+ * the in-flight apply adopts the returned config as the new seed — the
668
+ * host write is unaffected.
669
+ */
670
+ discard() {
671
+ this.store.update((s) => {
672
+ s.draft = this.seed;
673
+ s.applyState = { kind: "idle" };
674
+ s.dirty = this.recomputeDirty(s.draft);
675
+ });
676
+ }
677
+ /**
678
+ * The minimal config patch making the effective config match the draft —
679
+ * only keys whose value differs from the last-read seed are sent, so a
680
+ * stale read never clobbers concurrent changes to untouched keys and base
681
+ * (plugin-row) values stay in place. A cleared provider/model the seed pins
682
+ * becomes an explicit `''` override (the gateway merge cannot express an
683
+ * unset, and the resolver treats '' as absent); a cleared number field is
684
+ * omitted (the stored value stays unchanged).
685
+ */
686
+ patchFor(draft) {
687
+ const patch = {};
688
+ const always = ["enabled", "systemPrompt", "immuneTurns", "maxDeltaMessages"];
689
+ for (const key of always) {
690
+ const next = draft[key];
691
+ if (next === void 0) continue;
692
+ if (JSON.stringify(this.seed[key]) !== JSON.stringify(next)) {
693
+ patch[key] = next;
694
+ }
695
+ }
696
+ for (const key of ["provider", "model"]) {
697
+ const next = draft[key];
698
+ if (next !== void 0) {
699
+ if (JSON.stringify(this.seed[key]) !== JSON.stringify(next)) {
700
+ patch[key] = next;
701
+ }
702
+ } else if (this.seed[key] !== void 0) {
703
+ patch[key] = "";
704
+ }
705
+ }
706
+ return patch;
707
+ }
708
+ /**
709
+ * The dirty derivation (KD-U2, plan dsh-advisor-plugin-config-card-ux task
710
+ * 2): the draft holds edits a save would write iff the minimal patch
711
+ * against the current seed is non-empty. Takes the DRAFT being mutated and
712
+ * reads `this.seed` — it must be called INSIDE the same `store.update`
713
+ * callback that mutates the draft/seed, never via a snapshot read + second
714
+ * write (the frozen-snapshot write would be dropped by the snapshot store).
715
+ */
716
+ recomputeDirty(draft) {
717
+ return Object.keys(this.patchFor(draft)).length > 0;
718
+ }
719
+ /** Edit one always-present draft field via the schema-form path writer. */
720
+ setField(key, value) {
721
+ this.store.update((s) => {
722
+ s.draft = (0, import_dsh_client_schema_form.setPath)(s.draft, [key], value);
723
+ s.applyState = { kind: "idle" };
724
+ s.dirty = this.recomputeDirty(s.draft);
725
+ });
726
+ }
727
+ /** Remove one optional draft field via the schema-form path writer. */
728
+ clearField(key) {
729
+ this.store.update((s) => {
730
+ s.draft = (0, import_dsh_client_schema_form.deletePath)(s.draft, [key]);
731
+ s.applyState = { kind: "idle" };
732
+ s.dirty = this.recomputeDirty(s.draft);
733
+ });
734
+ }
735
+ clampInt(value, fallback) {
736
+ return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
737
+ }
738
+ };
739
+ function refreshIfLoaded(controller) {
740
+ if (controller.store.getSnapshot().status === "idle") return;
741
+ void controller.load();
742
+ }
743
+
744
+ // src/client/locales.ts
745
+ var en = {
746
+ title: "Advisor",
747
+ intro: "Configure the advisor reviewer model that observes the primary transcript and injects severity-ranked advice.",
748
+ collapse: "Hide settings",
749
+ expand: "Show settings",
750
+ unsaved: "Unsaved",
751
+ loadFailed: "Loading advisor settings failed",
752
+ retry: "Retry",
753
+ enabled: "Enable the advisor",
754
+ provider: "Provider",
755
+ providerPlaceholder: "Select a provider",
756
+ providerRequired: "Provider is required when the advisor is enabled.",
757
+ model: "Model",
758
+ modelPlaceholder: "Select a model",
759
+ modelRequired: "Model is required when the advisor is enabled.",
760
+ noProviders: "No configured providers. Configure one on the Models page first.",
761
+ noModels: "This provider has no available models. Configure models for it on the Models page.",
762
+ staleProvider: "The stored provider is no longer configured. Reselect one or keep the stored value.",
763
+ staleModel: "The stored model is no longer available for this provider. Reselect one or keep the stored value.",
764
+ systemPrompt: "System prompt",
765
+ systemPromptPlaceholder: "Leave empty to use the default system prompt",
766
+ immuneTurns: "Immune turns",
767
+ maxDeltaMessages: "Max delta messages",
768
+ save: "Save",
769
+ saving: "Saving\u2026",
770
+ discard: "Discard",
771
+ saved: "Advisor settings saved. New sessions pick them up immediately.",
772
+ readOnly: "Settings are read-only in this environment.",
773
+ namespaceUnavailable: "The advisor configuration channel is not available yet \u2014 the settings gateway is not ready on this host. Configure the advisor via the plugin config row \u2014 `- id: advisor` whose `config:` map sets `enabled`/`provider`/`model` (e.g. `config: { enabled: true, provider: \u2026, model: \u2026 }`) in `$DSH_HOME/profiles/<name>/cordis.patch.yml`. The advisor plugin runs fully from its plugin config row; the web settings form becomes available again on the next load once the gateway is reachable. Note: `/advisor` only toggles the advisor per session; it cannot supply provider/model."
774
+ };
775
+ var zh = {
776
+ title: "\u987E\u95EE",
777
+ intro: "\u914D\u7F6E\u987E\u95EE\u5BA1\u9605\u6A21\u578B\uFF1A\u5B83\u89C2\u5BDF\u4E3B\u4F1A\u8BDD\u8BB0\u5F55\uFF0C\u5E76\u6CE8\u5165\u6309\u4E25\u91CD\u7A0B\u5EA6\u6392\u5E8F\u7684\u5EFA\u8BAE\u3002",
778
+ collapse: "\u6536\u8D77\u8BBE\u7F6E",
779
+ expand: "\u5C55\u5F00\u8BBE\u7F6E",
780
+ unsaved: "\u672A\u4FDD\u5B58",
781
+ loadFailed: "\u52A0\u8F7D\u987E\u95EE\u8BBE\u7F6E\u5931\u8D25",
782
+ retry: "\u91CD\u8BD5",
783
+ enabled: "\u542F\u7528\u987E\u95EE",
784
+ provider: "\u63D0\u4F9B\u5546",
785
+ providerPlaceholder: "\u9009\u62E9\u63D0\u4F9B\u5546",
786
+ providerRequired: "\u542F\u7528\u987E\u95EE\u65F6\u5FC5\u987B\u9009\u62E9\u63D0\u4F9B\u5546\u3002",
787
+ model: "\u6A21\u578B",
788
+ modelPlaceholder: "\u9009\u62E9\u6A21\u578B",
789
+ modelRequired: "\u542F\u7528\u987E\u95EE\u65F6\u5FC5\u987B\u9009\u62E9\u6A21\u578B\u3002",
790
+ noProviders: "\u6CA1\u6709\u5DF2\u914D\u7F6E\u7684\u63D0\u4F9B\u5546\u3002\u8BF7\u5148\u5728 Models \u9875\u9762\u914D\u7F6E\u3002",
791
+ noModels: "\u8BE5\u63D0\u4F9B\u5546\u6CA1\u6709\u53EF\u7528\u6A21\u578B\u3002\u8BF7\u5230 Models \u9875\u9762\u4E3A\u5176\u914D\u7F6E\u6A21\u578B\u3002",
792
+ staleProvider: "\u5B58\u50A8\u7684\u63D0\u4F9B\u5546\u5DF2\u4E0D\u5728\u914D\u7F6E\u4E2D\u3002\u8BF7\u91CD\u65B0\u9009\u62E9\uFF0C\u6216\u4FDD\u7559\u5B58\u50A8\u7684\u503C\u3002",
793
+ staleModel: "\u5B58\u50A8\u7684\u6A21\u578B\u5728\u8BE5\u63D0\u4F9B\u5546\u4E0B\u5DF2\u4E0D\u53EF\u7528\u3002\u8BF7\u91CD\u65B0\u9009\u62E9\uFF0C\u6216\u4FDD\u7559\u5B58\u50A8\u7684\u503C\u3002",
794
+ systemPrompt: "\u7CFB\u7EDF\u63D0\u793A\u8BCD",
795
+ systemPromptPlaceholder: "\u7559\u7A7A\u5219\u4F7F\u7528\u9ED8\u8BA4\u7CFB\u7EDF\u63D0\u793A\u8BCD",
796
+ immuneTurns: "\u514D\u75AB\u8F6E\u6B21",
797
+ maxDeltaMessages: "\u6700\u5927\u589E\u91CF\u6D88\u606F\u6570",
798
+ save: "\u4FDD\u5B58",
799
+ saving: "\u4FDD\u5B58\u4E2D\u2026",
800
+ discard: "\u653E\u5F03\u4FEE\u6539",
801
+ saved: "\u987E\u95EE\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\u3002\u65B0\u4F1A\u8BDD\u7ACB\u5373\u751F\u6548\u3002",
802
+ readOnly: "\u5F53\u524D\u73AF\u5883\u4E2D\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\u3002",
803
+ namespaceUnavailable: "\u987E\u95EE\u914D\u7F6E\u901A\u9053\u6682\u4E0D\u53EF\u7528\u2014\u2014\u672C\u5BBF\u4E3B\u4E0A\u7684\u8BBE\u7F6E\u7F51\u5173\u5C1A\u672A\u5C31\u7EEA\u3002\u8BF7\u901A\u8FC7\u63D2\u4EF6\u914D\u7F6E\u884C\u914D\u7F6E\u987E\u95EE\u2014\u2014\u5728 `$DSH_HOME/profiles/<name>/cordis.patch.yml` \u4E2D\u5199 `- id: advisor`\uFF0C\u5176 `config:` \u6620\u5C04\u4E2D\u8BBE\u7F6E `enabled`/`provider`/`model`\uFF08\u4F8B\u5982 `config: { enabled: true, provider: \u2026, model: \u2026 }`\uFF09\u3002\u987E\u95EE\u63D2\u4EF6\u5B8C\u5168\u901A\u8FC7\u63D2\u4EF6\u914D\u7F6E\u884C\u8FD0\u884C\uFF1B\u7F51\u5173\u53EF\u7528\u540E\uFF0Cweb \u8BBE\u7F6E\u8868\u5355\u5C06\u5728\u4E0B\u6B21\u52A0\u8F7D\u65F6\u91CD\u65B0\u663E\u793A\u3002\u6CE8\u610F\uFF1A`/advisor` \u4EC5\u5207\u6362\u5F53\u524D\u4F1A\u8BDD\u7684\u987E\u95EE\u5F00\u5173\uFF0C\u65E0\u6CD5\u63D0\u4F9B provider/model\u3002"
804
+ };
805
+
806
+ // src/client/index.ts
807
+ var NS = "settings.advisor";
808
+ var inject = ["slots", "locale", "connection"];
809
+ function apply(ctx) {
810
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), "advisor: copy dictionaries");
811
+ const connection = ctx.get("connection");
812
+ const controller = new AdvisorSettingsStore(connection.api, connection.rpc);
813
+ const useSnapshot = (0, import_dsh_client_web_react.bindSnapshotSelector)(controller.store);
814
+ ctx.effect(() => {
815
+ let pending = false;
816
+ const refresh = () => {
817
+ if (pending) return;
818
+ pending = true;
819
+ queueMicrotask(() => {
820
+ pending = false;
821
+ refreshIfLoaded(controller);
822
+ });
823
+ };
824
+ const disposers = [ctx.on("connection/reset", refresh)];
825
+ return () => {
826
+ for (const dispose of disposers) dispose();
827
+ };
828
+ }, "advisor: pushed invalidations");
829
+ ctx.slots.inject("settings.plugin.item", function* () {
830
+ yield ctx.slots.register({
831
+ name: "settings.plugin.item",
832
+ id: "advisor",
833
+ order: 30,
834
+ // bash 0 / agent-loop 10 / web-search 20 / advisor 30
835
+ locale: NS,
836
+ inject: () => ({ controller, useSnapshot })
837
+ }, AdvisorCard);
838
+ });
839
+ }
840
+ return module.exports; } });