dsh-advisor-plugin 0.2.1

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/cordis.patch.yml +6 -0
  4. package/lib/advisor-prompt.d.ts +28 -0
  5. package/lib/advisor-prompt.js +91 -0
  6. package/lib/client/AdvisorCard.d.ts +15 -0
  7. package/lib/client/AdvisorCard.js +60 -0
  8. package/lib/client/AdvisorToolRow.d.ts +40 -0
  9. package/lib/client/AdvisorToolRow.js +52 -0
  10. package/lib/client/PatrolNodeView.d.ts +18 -0
  11. package/lib/client/PatrolNodeView.js +57 -0
  12. package/lib/client/controller.d.ts +161 -0
  13. package/lib/client/controller.js +287 -0
  14. package/lib/client/index.d.ts +20 -0
  15. package/lib/client/index.js +841 -0
  16. package/lib/client/locales.d.ts +54 -0
  17. package/lib/client/locales.js +101 -0
  18. package/lib/client/patrol-chat.d.ts +78 -0
  19. package/lib/client/patrol-chat.js +54 -0
  20. package/lib/client/store.d.ts +11 -0
  21. package/lib/client/store.js +23 -0
  22. package/lib/command.d.ts +7 -0
  23. package/lib/command.js +52 -0
  24. package/lib/config.d.ts +37 -0
  25. package/lib/config.js +83 -0
  26. package/lib/emission-guard.d.ts +32 -0
  27. package/lib/emission-guard.js +74 -0
  28. package/lib/gating.d.ts +8 -0
  29. package/lib/gating.js +48 -0
  30. package/lib/history.d.ts +20 -0
  31. package/lib/history.js +95 -0
  32. package/lib/index.d.ts +25 -0
  33. package/lib/index.js +96 -0
  34. package/lib/llm-call.d.ts +69 -0
  35. package/lib/llm-call.js +483 -0
  36. package/lib/patrol-event.d.ts +88 -0
  37. package/lib/patrol-event.js +68 -0
  38. package/lib/patrol.d.ts +51 -0
  39. package/lib/patrol.js +202 -0
  40. package/lib/prompt-section.d.ts +7 -0
  41. package/lib/prompt-section.js +16 -0
  42. package/lib/settings.d.ts +18 -0
  43. package/lib/settings.js +50 -0
  44. package/lib/tool.d.ts +22 -0
  45. package/lib/tool.js +91 -0
  46. package/package.json +91 -0
  47. package/src/advisor-prompt.ts +102 -0
  48. package/src/client/AdvisorCard.tsx +253 -0
  49. package/src/client/AdvisorToolRow.tsx +83 -0
  50. package/src/client/PatrolNodeView.tsx +85 -0
  51. package/src/client/controller.ts +399 -0
  52. package/src/client/index.ts +100 -0
  53. package/src/client/locales.ts +105 -0
  54. package/src/client/patrol-chat.ts +109 -0
  55. package/src/client/store.ts +29 -0
  56. package/src/command.ts +61 -0
  57. package/src/config.ts +113 -0
  58. package/src/emission-guard.ts +76 -0
  59. package/src/gating.ts +54 -0
  60. package/src/history.ts +102 -0
  61. package/src/index.ts +106 -0
  62. package/src/llm-call.ts +552 -0
  63. package/src/patrol-event.ts +115 -0
  64. package/src/patrol.ts +229 -0
  65. package/src/prompt-section.ts +21 -0
  66. package/src/settings.ts +74 -0
  67. package/src/tool.ts +114 -0
@@ -0,0 +1,841 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-advisor",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ "use strict";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
24
+
25
+ // src/client/index.ts
26
+ var index_exports = {};
27
+ __export(index_exports, {
28
+ apply: () => apply,
29
+ inject: () => inject
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/client/AdvisorCard.tsx
34
+ var import_jsx_runtime = require("react/jsx-runtime");
35
+ function PatrolSection(props) {
36
+ const { t } = props;
37
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...style.card, background: "rgba(128,128,128,.06)" }, children: [
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { style: { ...style.title, fontSize: 13 }, children: t("patrolTitle") }),
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.description, children: t("patrolDescription") }),
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.effortRow, children: [
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("patrolToggle") }),
42
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
43
+ "button",
44
+ {
45
+ type: "button",
46
+ role: "switch",
47
+ "aria-checked": props.enabled,
48
+ "aria-label": t("patrolToggle"),
49
+ disabled: props.disabled,
50
+ onClick: props.togglePatrol,
51
+ style: {
52
+ ...style.button,
53
+ width: 40,
54
+ height: 22,
55
+ borderRadius: 11,
56
+ padding: 0,
57
+ position: "relative",
58
+ background: props.enabled ? "#16a34a" : "rgba(128,128,128,.4)",
59
+ border: "none"
60
+ },
61
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: {
62
+ position: "absolute",
63
+ top: 2,
64
+ left: props.enabled ? 20 : 2,
65
+ width: 18,
66
+ height: 18,
67
+ borderRadius: 9,
68
+ background: "#fff",
69
+ transition: "left .15s"
70
+ } })
71
+ }
72
+ )
73
+ ] }),
74
+ props.enabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.effortRow, children: [
76
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-patrol-steps", children: t("patrolEverySteps") }),
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
78
+ "input",
79
+ {
80
+ id: "advisor-patrol-steps",
81
+ type: "number",
82
+ min: 2,
83
+ max: 500,
84
+ value: props.everySteps,
85
+ disabled: props.disabled,
86
+ onChange: (event) => {
87
+ props.setPatrolEverySteps(Number(event.target.value));
88
+ },
89
+ style: { width: 64 }
90
+ }
91
+ )
92
+ ] }),
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.effortRow, children: [
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-patrol-immune", children: t("patrolImmuneTurns") }),
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
+ "input",
97
+ {
98
+ id: "advisor-patrol-immune",
99
+ type: "number",
100
+ min: 0,
101
+ max: 20,
102
+ value: props.immuneTurns,
103
+ disabled: props.disabled,
104
+ onChange: (event) => {
105
+ props.setPatrolImmuneTurns(Number(event.target.value));
106
+ },
107
+ style: { width: 64 }
108
+ }
109
+ )
110
+ ] }),
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { ...style.effortRow, marginTop: 8 }, children: [
112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("investigateToggle") }),
113
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ role: "switch",
118
+ "aria-checked": props.investigate,
119
+ "aria-label": t("investigateToggle"),
120
+ disabled: props.disabled,
121
+ onClick: props.toggleInvestigate,
122
+ style: {
123
+ ...style.button,
124
+ width: 40,
125
+ height: 22,
126
+ borderRadius: 11,
127
+ padding: 0,
128
+ position: "relative",
129
+ background: props.investigate ? "#16a34a" : "rgba(128,128,128,.4)",
130
+ border: "none"
131
+ },
132
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: {
133
+ position: "absolute",
134
+ top: 2,
135
+ left: props.investigate ? 20 : 2,
136
+ width: 18,
137
+ height: 18,
138
+ borderRadius: 9,
139
+ background: "#fff",
140
+ transition: "left .15s"
141
+ } })
142
+ }
143
+ )
144
+ ] }),
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...style.notice, marginTop: -2 }, children: t("investigateHint") })
146
+ ] }) : null,
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...style.notice, marginTop: -2 }, children: t("patrolEveryStepsHint") })
148
+ ] });
149
+ }
150
+ var EFFORT_OPTIONS = ["", "off", "minimal", "low", "medium", "high", "xhigh", "max"];
151
+ var style = {
152
+ card: { border: "1px solid rgba(128,128,128,.35)", borderRadius: 8, padding: "14px 16px", display: "flex", flexDirection: "column", gap: 10 },
153
+ title: { fontSize: 14, fontWeight: 600, margin: 0 },
154
+ description: { fontSize: 12, opacity: 0.75, margin: 0, lineHeight: 1.6 },
155
+ current: { fontSize: 12, margin: 0 },
156
+ groupTitle: { fontSize: 12, fontWeight: 600, opacity: 0.85, margin: "8px 0 2px" },
157
+ row: { display: "flex", alignItems: "center", gap: 8, padding: "3px 0", fontSize: 13 },
158
+ modelName: { display: "flex", flexDirection: "column" },
159
+ route: { fontSize: 11, opacity: 0.65 },
160
+ badge: { fontSize: 11, color: "#b45309" },
161
+ notice: { fontSize: 12, opacity: 0.7, margin: 0 },
162
+ error: { fontSize: 12, color: "#b91c1c", display: "flex", gap: 8, alignItems: "center" },
163
+ effortRow: { display: "flex", alignItems: "center", gap: 8, fontSize: 13, marginTop: 6 },
164
+ actions: { display: "flex", gap: 8, alignItems: "center", marginTop: 4 },
165
+ button: { fontSize: 13, padding: "4px 14px", borderRadius: 6, cursor: "pointer" },
166
+ primary: { background: "#2563eb", color: "#fff", border: "none" },
167
+ secondary: { background: "transparent", border: "1px solid rgba(128,128,128,.5)" },
168
+ status: { fontSize: 12 },
169
+ list: { maxHeight: 260, overflowY: "auto", border: "1px solid rgba(128,128,128,.25)", borderRadius: 6, padding: "4px 10px" }
170
+ };
171
+ function AdvisorCard(props) {
172
+ const state = props.useAdvisorCard((s) => s);
173
+ const effortOptions = state.efforts.known ? ["", ...state.efforts.levels] : EFFORT_OPTIONS;
174
+ const disabled = !state.writable || state.saving;
175
+ const armed = state.effective.provider !== "" && state.effective.model !== "";
176
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.card, children: [
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { style: style.title, children: props.t("title") }),
178
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.description, children: props.t("description") }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: style.current, children: [
180
+ props.t("armed"),
181
+ "\uFF1A",
182
+ armed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: `${state.effective.provider}/${state.effective.model}${state.effective.effort === "" ? "" : ` (${state.effective.effort})`}` }) : props.t("offCurrent")
183
+ ] }),
184
+ !state.writable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.notice, children: props.t("readonly") }) : null,
185
+ state.catalogStatus === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.notice, children: props.t("loading") }) : null,
186
+ state.catalogStatus === "error" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.error, children: [
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: props.t("loadFailed") }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", style: { ...style.button, ...style.secondary }, disabled, onClick: props.retryCatalog, children: props.t("retry") })
189
+ ] }) : null,
190
+ state.catalogPartial ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.notice, children: props.t("partial") }) : null,
191
+ state.groups.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
192
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.notice, children: props.t("choose") }),
193
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: style.list, children: state.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
194
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: style.groupTitle, children: group.name }),
195
+ group.candidates.map((candidate) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: style.row, children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
197
+ "input",
198
+ {
199
+ type: "radio",
200
+ name: "advisor-route",
201
+ checked: candidate.selected,
202
+ disabled,
203
+ onChange: () => {
204
+ props.pickRoute(candidate.key);
205
+ }
206
+ }
207
+ ),
208
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: style.modelName, children: [
209
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: candidate.modelName }),
210
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: style.route, children: `${candidate.provider}/${candidate.model}` })
211
+ ] }),
212
+ !candidate.available ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: style.badge, children: props.t("unavailable") }) : null
213
+ ] }, candidate.key))
214
+ ] }, group.id)) })
215
+ ] }) : state.catalogStatus === "ready" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: style.notice, children: props.t("empty") }) : null,
216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.effortRow, children: [
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "advisor-effort", children: props.t("effort") }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
219
+ "select",
220
+ {
221
+ id: "advisor-effort",
222
+ value: state.effective.effort,
223
+ disabled,
224
+ onChange: (event) => {
225
+ props.setEffort(event.target.value);
226
+ },
227
+ children: effortOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: option, children: option === "" ? props.t("effortDefault") : option }, option))
228
+ }
229
+ )
230
+ ] }),
231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { ...style.notice, marginTop: -4 }, children: state.efforts.known ? props.t("effortHintKnown") : props.t("effortHint") }),
232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
233
+ PatrolSection,
234
+ {
235
+ ...props,
236
+ enabled: state.patrol.enabled,
237
+ everySteps: state.patrol.everySteps,
238
+ immuneTurns: state.patrol.immuneTurns,
239
+ investigate: state.patrol.investigate,
240
+ disabled
241
+ }
242
+ ),
243
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: style.actions, children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
245
+ "button",
246
+ {
247
+ type: "button",
248
+ style: { ...style.button, ...style.primary },
249
+ disabled: disabled || !state.dirty,
250
+ onClick: props.save,
251
+ children: props.t("save")
252
+ }
253
+ ),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
255
+ "button",
256
+ {
257
+ type: "button",
258
+ style: { ...style.button, ...style.secondary },
259
+ disabled: disabled || !state.dirty,
260
+ onClick: props.discard,
261
+ children: props.t("discard")
262
+ }
263
+ ),
264
+ state.saving ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: style.status, children: props.t("saving") }) : null,
265
+ state.failed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { ...style.status, color: "#b91c1c" }, children: props.t("saveFailed") }) : null
266
+ ] })
267
+ ] });
268
+ }
269
+
270
+ // src/client/AdvisorToolRow.tsx
271
+ var import_react = require("react");
272
+ var import_jsx_runtime2 = require("react/jsx-runtime");
273
+ var style2 = {
274
+ row: { display: "flex", flexDirection: "column", gap: 4, margin: "6px 0", border: "1px solid rgba(37,99,235,.35)", borderRadius: 8, overflow: "hidden" },
275
+ head: { display: "flex", alignItems: "center", gap: 8, padding: "6px 12px", cursor: "pointer", background: "rgba(37,99,235,.08)", border: "none", font: "inherit", textAlign: "left", width: "100%" },
276
+ title: { fontWeight: 600, fontSize: 13, color: "#1d4ed8" },
277
+ state: { fontSize: 12, opacity: 0.75 },
278
+ badge: { fontSize: 11, padding: "1px 8px", borderRadius: 8, flex: "none" },
279
+ badgeRun: { background: "rgba(37,99,235,.15)", color: "#1d4ed8" },
280
+ badgeOk: { background: "rgba(22,163,74,.15)", color: "#15803d" },
281
+ badgeErr: { background: "rgba(185,28,28,.12)", color: "#b91c1c" },
282
+ body: { padding: "8px 12px", fontSize: 13, lineHeight: 1.65, whiteSpace: "pre-wrap", borderTop: "1px solid rgba(37,99,235,.2)", maxHeight: 320, overflowY: "auto" }
283
+ };
284
+ function blockOutput(block) {
285
+ if (block === void 0) return { text: "", isError: false };
286
+ const parts = [];
287
+ for (const b of block.content ?? []) {
288
+ if (b?.type === "text" && typeof b.text === "string") parts.push(b.text);
289
+ else if (b !== void 0 && b !== null) parts.push(JSON.stringify(b, null, 2));
290
+ }
291
+ if (parts.length === 0 && block.error !== void 0) {
292
+ const e = block.error;
293
+ parts.push(`${typeof e.name === "string" ? e.name : "Error"}: ${typeof e.code === "string" ? e.code : ""} ${typeof e.message === "string" ? e.message : ""}`.trim());
294
+ }
295
+ return { text: parts.join("\n").trim(), isError: block.isError === true };
296
+ }
297
+ function AdvisorToolRow(props) {
298
+ const t = props.t ?? ((key) => key);
299
+ const [expanded, setExpanded] = (0, import_react.useState)(false);
300
+ const block = props.block ?? {};
301
+ const done = block.kind !== void 0;
302
+ const { text, isError } = done ? blockOutput(block) : { text: "", isError: false };
303
+ const badge = !done ? { label: t("toolRunning"), css: style2.badgeRun } : isError ? { label: t("toolFailed"), css: style2.badgeErr } : { label: t("toolDone"), css: style2.badgeOk };
304
+ const canExpand = done && text !== "";
305
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: style2.row, children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", style: style2.head, onClick: () => {
307
+ if (canExpand) setExpanded((v) => !v);
308
+ }, children: [
309
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: style2.title, children: [
310
+ "\u{1F9ED} Advisor ",
311
+ t("toolTitle")
312
+ ] }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { ...style2.badge, ...badge.css }, children: badge.label }),
314
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: style2.state, children: !done ? t("toolConsulting") : canExpand ? expanded ? t("toolCollapse") : t("toolExpand") : "" })
315
+ ] }),
316
+ expanded && canExpand ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: style2.body, children: text }) : null
317
+ ] });
318
+ }
319
+
320
+ // src/client/PatrolNodeView.tsx
321
+ var import_react2 = require("react");
322
+ var import_jsx_runtime3 = require("react/jsx-runtime");
323
+ var ACCENT_ON_TRACK = {
324
+ border: "rgba(107,114,128,.35)",
325
+ head: "rgba(107,114,128,.06)",
326
+ title: "#4b5563",
327
+ badge: { background: "rgba(107,114,128,.14)", color: "#4b5563" }
328
+ };
329
+ var ACCENT_CORRECTION = {
330
+ border: "rgba(217,119,6,.45)",
331
+ head: "rgba(217,119,6,.09)",
332
+ title: "#b45309",
333
+ badge: { background: "rgba(217,119,6,.16)", color: "#b45309" }
334
+ };
335
+ var ACCENT_STOP = {
336
+ border: "rgba(185,28,28,.4)",
337
+ head: "rgba(185,28,28,.07)",
338
+ title: "#b91c1c",
339
+ badge: { background: "rgba(185,28,28,.12)", color: "#b91c1c" }
340
+ };
341
+ var style3 = {
342
+ row: (accent) => ({ display: "flex", flexDirection: "column", gap: 4, margin: "6px 0", border: `1px solid ${accent.border}`, borderRadius: 8, overflow: "hidden" }),
343
+ head: (accent) => ({ display: "flex", alignItems: "center", gap: 8, padding: "6px 12px", cursor: "pointer", background: accent.head, border: "none", font: "inherit", textAlign: "left", width: "100%" }),
344
+ title: (accent) => ({ fontWeight: 600, fontSize: 13, color: accent.title }),
345
+ badge: { fontSize: 11, padding: "1px 8px", borderRadius: 8, flex: "none" },
346
+ meta: { fontSize: 12, opacity: 0.7 },
347
+ body: { padding: "8px 12px", fontSize: 13, lineHeight: 1.65, whiteSpace: "pre-wrap", borderTop: "1px solid rgba(120,113,108,.25)", maxHeight: 320, overflowY: "auto" }
348
+ };
349
+ function PatrolNodeView(props) {
350
+ const t = props.t ?? ((key) => key);
351
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
352
+ const data = props.node?.data;
353
+ if (data === void 0) return null;
354
+ const onTrack = data.verdict === "on-track";
355
+ const accent = onTrack ? ACCENT_ON_TRACK : data.verdict === "stop" ? ACCENT_STOP : ACCENT_CORRECTION;
356
+ const badge = onTrack ? t("patrolVerdictOnTrack") : data.verdict === "stop" ? t("patrolVerdictStop") : t("patrolVerdictCorrection");
357
+ const title = onTrack ? t("patrolCheckTitle") : t("patrolCardTitle");
358
+ const canExpand = data.detail.trim() !== "";
359
+ const fmtTok = (n) => n >= 1e3 ? `${Math.round(n / 100) / 10}k` : String(n);
360
+ const usageText = data.usage === void 0 ? "" : ` \xB7 \u2193${fmtTok(data.usage.input)}${data.usage.cacheRead !== void 0 && data.usage.cacheRead > 0 ? `(\u7F13\u5B58${fmtTok(data.usage.cacheRead)})` : ""} \u2191${fmtTok(data.usage.output)} tok`;
361
+ const downgradedTag = data.downgraded === true ? ` \xB7 ${t("patrolDowngradedTag")}` : "";
362
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: style3.row(accent), children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", style: style3.head(accent), onClick: () => {
364
+ if (canExpand) setExpanded((v) => !v);
365
+ }, children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: style3.title(accent), children: `\u{1F9ED} Advisor ${title}` }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { ...style3.badge, ...accent.badge }, children: badge }),
368
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: style3.meta, children: `${data.reviewer} \xB7 ${t("patrolCardStepPrefix")}${data.step}${t("patrolCardStepSuffix")} \xB7 ${Math.round(data.patrolMs / 100) / 10}s${usageText}${downgradedTag}` }),
369
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: style3.meta, children: canExpand ? expanded ? t("patrolCollapse") : t("patrolExpand") : "" })
370
+ ] }),
371
+ expanded && canExpand ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: style3.body, children: data.detail }) : null
372
+ ] });
373
+ }
374
+
375
+ // src/client/store.ts
376
+ function createSnapshotStore(initial) {
377
+ let current = initial;
378
+ const listeners = /* @__PURE__ */ new Set();
379
+ return {
380
+ getSnapshot: () => current,
381
+ subscribe: (fn) => {
382
+ listeners.add(fn);
383
+ return () => {
384
+ listeners.delete(fn);
385
+ };
386
+ },
387
+ set: (value) => {
388
+ current = value;
389
+ for (const fn of listeners) fn();
390
+ }
391
+ };
392
+ }
393
+
394
+ // src/client/controller.ts
395
+ function routeKey(provider, model) {
396
+ return `${provider}\0${model}`;
397
+ }
398
+ function buildCandidates(groups, effective) {
399
+ const selectedKey = effective.provider === "" || effective.model === "" ? void 0 : routeKey(effective.provider, effective.model);
400
+ const seen = /* @__PURE__ */ new Set();
401
+ const out = groups.map((group) => ({
402
+ id: group.id,
403
+ name: group.name,
404
+ candidates: group.models.map((model) => {
405
+ const key = routeKey(group.id, model.id);
406
+ seen.add(key);
407
+ const declared = model.reasoning?.efforts;
408
+ return {
409
+ key,
410
+ provider: group.id,
411
+ model: model.id,
412
+ providerName: group.name,
413
+ modelName: model.name,
414
+ available: true,
415
+ selected: key === selectedKey,
416
+ ...declared === void 0 || declared === null ? {} : { effortLevels: declared.map((e) => e.id) }
417
+ };
418
+ })
419
+ }));
420
+ let stale = false;
421
+ if (selectedKey !== void 0 && !seen.has(selectedKey)) {
422
+ stale = true;
423
+ out.push({
424
+ id: effective.provider,
425
+ name: effective.provider,
426
+ candidates: [{
427
+ key: selectedKey,
428
+ provider: effective.provider,
429
+ model: effective.model,
430
+ providerName: effective.provider,
431
+ modelName: effective.model,
432
+ available: false,
433
+ selected: true
434
+ }]
435
+ });
436
+ }
437
+ return { groups: out, stale };
438
+ }
439
+ function currentEffortSupport(groups, route) {
440
+ if (route.provider === "" || route.model === "") return { known: false, levels: [] };
441
+ const declared = groups.find((g) => g.id === route.provider)?.models.find((m) => m.id === route.model)?.reasoning?.efforts;
442
+ if (declared === void 0 || declared === null) return { known: false, levels: [] };
443
+ return { known: true, levels: declared.map((e) => e.id) };
444
+ }
445
+ var AdvisorCardController = class {
446
+ constructor(scope, ctx) {
447
+ this.scope = scope;
448
+ this.ctx = ctx;
449
+ this.store = createSnapshotStore(this.projection());
450
+ this.unsubscribe = scope.subscribe(() => {
451
+ if (!this.saving) this.publish();
452
+ });
453
+ if (this.catalogStatus === "idle") void this.loadCatalog();
454
+ }
455
+ scope;
456
+ ctx;
457
+ catalogGroups = [];
458
+ catalogStatus = "idle";
459
+ catalogPartial = false;
460
+ draft;
461
+ saving = false;
462
+ failed = false;
463
+ disposed = false;
464
+ catalogGeneration = 0;
465
+ store;
466
+ unsubscribe;
467
+ dispose() {
468
+ this.disposed = true;
469
+ this.catalogGeneration += 1;
470
+ this.unsubscribe();
471
+ }
472
+ inject() {
473
+ return {
474
+ hooks: { advisorCard: this.store },
475
+ pickRoute: (key) => this.pickRoute(key),
476
+ setEffort: (effort) => this.setEffort(effort),
477
+ togglePatrol: () => this.togglePatrol(),
478
+ setPatrolEverySteps: (steps) => this.setPatrolEverySteps(steps),
479
+ setPatrolImmuneTurns: (n) => this.setPatrolImmuneTurns(n),
480
+ toggleInvestigate: () => this.toggleInvestigate(),
481
+ save: () => {
482
+ void this.save();
483
+ },
484
+ discard: () => this.discard(),
485
+ retryCatalog: () => {
486
+ void this.loadCatalog();
487
+ }
488
+ };
489
+ }
490
+ saved() {
491
+ const value = this.scope.getSnapshot().value;
492
+ return {
493
+ provider: value?.provider ?? "",
494
+ model: value?.model ?? "",
495
+ effort: value?.effort ?? "",
496
+ patrolEnabled: value?.patrolEnabled ?? true,
497
+ patrolEverySteps: value?.patrolEverySteps ?? 6,
498
+ patrolImmuneTurns: value?.patrolImmuneTurns ?? 3,
499
+ investigate: value?.investigate ?? true
500
+ };
501
+ }
502
+ savedPatrol() {
503
+ const saved = this.saved();
504
+ return { enabled: saved.patrolEnabled !== false, everySteps: saved.patrolEverySteps ?? 6 };
505
+ }
506
+ effective() {
507
+ return this.draft ?? this.saved();
508
+ }
509
+ pickRoute(key) {
510
+ if (!this.scope.getSnapshot().writable || this.saving) return;
511
+ const all = this.catalogGroups.flatMap((g) => g.models.map((m) => ({ provider: g.id, model: m.id })));
512
+ const hit = all.find((r) => routeKey(r.provider, r.model) === key);
513
+ const base = this.effective();
514
+ const currentKey = base.provider === "" || base.model === "" ? void 0 : routeKey(base.provider, base.model);
515
+ if (currentKey === key) {
516
+ this.draft = { ...base, provider: "", model: "" };
517
+ } else if (hit !== void 0) {
518
+ let draft = { ...base, provider: hit.provider, model: hit.model };
519
+ const support = currentEffortSupport(this.catalogGroups, draft);
520
+ if (support.known && draft.effort !== "" && !support.levels.includes(draft.effort)) {
521
+ console.log(`[dsh-advisor] \u6863\u4F4D ${draft.effort} \u4E0D\u88AB ${draft.provider}/${draft.model} \u652F\u6301\uFF0C\u5DF2\u81EA\u52A8\u6E05\u7A7A`);
522
+ draft = { ...draft, effort: "" };
523
+ }
524
+ this.draft = draft;
525
+ }
526
+ this.failed = false;
527
+ this.publish();
528
+ }
529
+ setEffort(effort) {
530
+ if (!this.scope.getSnapshot().writable || this.saving) return;
531
+ const support = currentEffortSupport(this.catalogGroups, this.effective());
532
+ if (support.known && effort !== "" && !support.levels.includes(effort)) return;
533
+ this.draft = { ...this.effective(), effort };
534
+ this.failed = false;
535
+ this.publish();
536
+ }
537
+ togglePatrol() {
538
+ if (!this.scope.getSnapshot().writable || this.saving) return;
539
+ const current = this.effective();
540
+ this.draft = { ...current, patrolEnabled: !(current.patrolEnabled ?? true) };
541
+ this.failed = false;
542
+ this.publish();
543
+ }
544
+ setPatrolEverySteps(steps) {
545
+ if (!this.scope.getSnapshot().writable || this.saving) return;
546
+ const bounded = Number.isFinite(steps) ? Math.min(500, Math.max(2, Math.round(steps))) : 6;
547
+ this.draft = { ...this.effective(), patrolEverySteps: bounded };
548
+ this.failed = false;
549
+ this.publish();
550
+ }
551
+ setPatrolImmuneTurns(n) {
552
+ if (!this.scope.getSnapshot().writable || this.saving) return;
553
+ const bounded = Number.isFinite(n) ? Math.min(20, Math.max(0, Math.round(n))) : 3;
554
+ this.draft = { ...this.effective(), patrolImmuneTurns: bounded };
555
+ this.failed = false;
556
+ this.publish();
557
+ }
558
+ toggleInvestigate() {
559
+ if (!this.scope.getSnapshot().writable || this.saving) return;
560
+ const current = this.effective();
561
+ this.draft = { ...current, investigate: !(current.investigate ?? true) };
562
+ this.failed = false;
563
+ this.publish();
564
+ }
565
+ discard() {
566
+ if (this.saving) return;
567
+ this.draft = void 0;
568
+ this.failed = false;
569
+ this.publish();
570
+ }
571
+ async save() {
572
+ const desired = this.draft;
573
+ if (this.disposed || desired === void 0 || this.saving || !this.scope.getSnapshot().writable) return;
574
+ this.saving = true;
575
+ this.failed = false;
576
+ this.publish();
577
+ try {
578
+ await this.scope.set("provider", desired.provider);
579
+ await this.scope.set("model", desired.model);
580
+ await this.scope.set("effort", desired.effort);
581
+ await this.scope.set("patrolEnabled", desired.patrolEnabled ?? true);
582
+ await this.scope.set("patrolEverySteps", desired.patrolEverySteps ?? 6);
583
+ await this.scope.set("patrolImmuneTurns", desired.patrolImmuneTurns ?? 3);
584
+ await this.scope.set("investigate", desired.investigate ?? true);
585
+ this.draft = void 0;
586
+ } catch {
587
+ this.failed = true;
588
+ }
589
+ this.saving = false;
590
+ this.publish();
591
+ }
592
+ async loadCatalog() {
593
+ if (this.disposed || this.catalogStatus === "loading") return;
594
+ const generation = this.catalogGeneration;
595
+ this.catalogStatus = "loading";
596
+ this.catalogPartial = false;
597
+ this.publish();
598
+ try {
599
+ const connection = this.ctx.get("connection");
600
+ const response = await connection.api.llm.models({});
601
+ if (generation !== this.catalogGeneration) return;
602
+ if (response.result.ok) {
603
+ this.catalogGroups = response.result.value.groups;
604
+ this.catalogPartial = response.result.value.failures.length > 0;
605
+ this.catalogStatus = "ready";
606
+ } else {
607
+ this.catalogStatus = "error";
608
+ }
609
+ } catch {
610
+ if (generation !== this.catalogGeneration) return;
611
+ this.catalogStatus = "error";
612
+ }
613
+ this.publish();
614
+ }
615
+ projection() {
616
+ const snapshot = this.scope.getSnapshot();
617
+ const effective = this.effective();
618
+ const built = buildCandidates(this.catalogGroups, effective);
619
+ return {
620
+ available: snapshot.status === "ready",
621
+ writable: snapshot.writable,
622
+ saving: this.saving,
623
+ failed: this.failed,
624
+ dirty: this.draft !== void 0,
625
+ catalogStatus: this.catalogStatus,
626
+ catalogPartial: this.catalogPartial,
627
+ effective,
628
+ patrol: {
629
+ enabled: effective.patrolEnabled ?? true,
630
+ everySteps: effective.patrolEverySteps ?? 6,
631
+ immuneTurns: effective.patrolImmuneTurns ?? 3,
632
+ investigate: effective.investigate ?? true
633
+ },
634
+ efforts: currentEffortSupport(this.catalogGroups, effective),
635
+ groups: built.groups
636
+ };
637
+ }
638
+ publish() {
639
+ this.store.set(this.projection());
640
+ }
641
+ };
642
+
643
+ // src/patrol-event.ts
644
+ var PATROL_EVENT_TYPE = "hook/invoked";
645
+ var PATROL_EVENT_PLUGIN = "dsh-advisor";
646
+ var PATROL_EVENT_POINT = "advisor-patrol";
647
+ function isPatrolVerdictEvent(event) {
648
+ if (event.type !== PATROL_EVENT_TYPE) return false;
649
+ const data = event.data;
650
+ if (data === null || typeof data !== "object") return false;
651
+ const record = data;
652
+ return record.plugin === PATROL_EVENT_PLUGIN && record.point === PATROL_EVENT_POINT;
653
+ }
654
+
655
+ // src/client/patrol-chat.ts
656
+ function patrolLocation(context) {
657
+ return context.start?.location ?? context.matches?.[0]?.location ?? { kind: "unresolved" };
658
+ }
659
+ function patrolChatDefinition() {
660
+ return {
661
+ kind: "advisor-patrol",
662
+ target: "chat",
663
+ match: (event) => {
664
+ if (!isPatrolVerdictEvent(event)) return null;
665
+ return { id: `advisor-patrol-${event.seq}`, role: "start" };
666
+ },
667
+ start: (_context, match) => {
668
+ const data = match.event.data;
669
+ return { ...data, seq: match.event.seq, time: match.event.time };
670
+ },
671
+ update: (context) => context.state,
672
+ buildViewNode: (context) => {
673
+ if (context.state === void 0) return null;
674
+ return {
675
+ key: context.key,
676
+ kind: "advisor-patrol",
677
+ id: context.id,
678
+ target: "chat",
679
+ anchorSeq: context.state.seq,
680
+ location: patrolLocation(context),
681
+ visibility: "visible",
682
+ data: context.state
683
+ };
684
+ }
685
+ };
686
+ }
687
+
688
+ // src/client/locales.ts
689
+ var zh = {
690
+ title: "Advisor \u5BA1\u67E5\u6A21\u578B",
691
+ description: "\u6267\u884C\u6A21\u578B\u5361\u4F4F/\u505A\u91CD\u5927\u51B3\u7B56/\u5BA3\u544A\u5B8C\u6210\u524D\uFF0C\u53EF\u8C03\u7528\u96F6\u53C2\u6570\u7684 advisor() \u5DE5\u5177\u5347\u7EA7\u54A8\u8BE2\u8BE5\u5BA1\u67E5\u6A21\u578B\uFF0C\u62FF\u56DE\u8BA1\u5212\u3001\u7EA0\u6B63\u6216\u505C\u6B62\u4FE1\u53F7\u3002\u6574\u6BB5\u4F1A\u8BDD\u6BCF\u6B21\u8C03\u7528\u90FD\u4F1A\u8F6C\u53D1\u7ED9\u5B83\u2014\u2014\u5347\u7EA7\u4E0D\u514D\u8D39\u3002",
692
+ armed: "\u5F53\u524D\u5BA1\u67E5\u6A21\u578B",
693
+ off: "\u672A\u9009\u62E9\u2014\u2014advisor \u5DE5\u5177\u5BF9\u6A21\u578B\u9690\u85CF\uFF08\u96F6\u63D0\u793A\u8BCD\u6210\u672C\uFF09",
694
+ offCurrent: "\u672A\u9009\u62E9",
695
+ choose: "\u9009\u62E9\u4E00\u4E2A DSH \u5DF2\u914D\u7F6E\u7684\u6A21\u578B\uFF1A",
696
+ loading: "\u6B63\u5728\u52A0\u8F7D\u6A21\u578B\u76EE\u5F55\u2026",
697
+ loadFailed: "\u6A21\u578B\u76EE\u5F55\u52A0\u8F7D\u5931\u8D25",
698
+ retry: "\u91CD\u8BD5",
699
+ partial: "\u90E8\u5206 provider \u7684\u6A21\u578B\u5217\u8868\u83B7\u53D6\u5931\u8D25\uFF0C\u5217\u8868\u53EF\u80FD\u4E0D\u5B8C\u6574\u3002",
700
+ empty: "\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u6A21\u578B\u2014\u2014\u8BF7\u5148\u5728 \u8BBE\u7F6E \u2192 \u6A21\u578B \u91CC\u914D\u7F6E provider\u3002",
701
+ effort: "\u63A8\u7406\u6863\u4F4D",
702
+ effortDefault: "\uFF08\u6A21\u578B\u9ED8\u8BA4\uFF09",
703
+ effortHint: "\u7559\u7A7A\u8D70\u6A21\u578B\u9ED8\u8BA4\uFF1B\u8BE5\u6A21\u578B\u672A\u58F0\u660E\u652F\u6301\u6863\u4F4D\u2014\u2014\u82E5\u914D\u7F6E\u7684\u6863\u4F4D\u4E0D\u88AB\u652F\u6301\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u964D\u7EA7\u4E3A\u9ED8\u8BA4\u6863\uFF08\u65E5\u5FD7\u53EF\u89C1\uFF09\u3002",
704
+ effortHintKnown: "\u53EA\u5217\u51FA\u8BE5\u6A21\u578B\u58F0\u660E\u652F\u6301\u7684\u6863\u4F4D\uFF1B\u7559\u7A7A\u8D70\u6A21\u578B\u9ED8\u8BA4\u3002",
705
+ unavailable: "\uFF08\u5DF2\u914D\u7F6E\u7684\u8DEF\u7531\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF09",
706
+ save: "\u4FDD\u5B58",
707
+ saving: "\u4FDD\u5B58\u4E2D\u2026",
708
+ discard: "\u653E\u5F03\u66F4\u6539",
709
+ saved: "\u5DF2\u4FDD\u5B58",
710
+ saveFailed: "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5",
711
+ readonly: "\u8BBE\u7F6E\u6587\u6863\u53EA\u8BFB\uFF0C\u65E0\u6CD5\u5728\u6B64\u4FEE\u6539\u3002",
712
+ patrolTitle: "\u5DE1\u903B\u6A21\u5F0F\uFF08\u81EA\u52A8\u68C0\u6D4B\u8DD1\u504F\uFF09",
713
+ patrolDescription: "\u6267\u884C\u8FC7\u7A0B\u4E2D\u6BCF\u9694\u82E5\u5E72\u6B65\u81EA\u52A8\u628A\u4F1A\u8BDD\u5FEB\u7167\u53D1\u7ED9\u5BA1\u67E5\u6A21\u578B\u68C0\u67E5\u65B9\u5411\uFF1B\u53D1\u73B0\u8DD1\u504F\u4F1A\u5728\u4E0B\u4E00\u6B65\u6CE8\u5165\u7EA0\u504F\uFF0CSTOP \u65F6\u8981\u6C42\u6267\u884C\u6A21\u578B\u505C\u4E0B\u5411\u7528\u6237\u62A5\u544A\u3002\u6BCF\u6B21\u5DE1\u903B\u6574\u6BB5\u4F1A\u8BDD\u8BA1\u8D39\u4E00\u6B21\u5BA1\u67E5\u6A21\u578B\u3002",
714
+ patrolToggle: "\u542F\u7528\u5DE1\u903B",
715
+ patrolEverySteps: "\u5DE1\u903B\u95F4\u9694\uFF08\u6B65\u6570\uFF09",
716
+ patrolEveryStepsHint: "\u6BCF N \u4E2A\u6A21\u578B\u8BF7\u6C42\u505A\u4E00\u6B21\u5DE1\u903B\u68C0\u67E5\uFF1B\u4E24\u6B21\u5DE1\u903B\u4E4B\u95F4\u53E6\u6709\u81F3\u5C11 90 \u79D2\u7684\u8282\u6D41\u3002",
717
+ patrolImmuneTurns: "\u7EA0\u504F\u51B7\u5374\uFF08\u8BF7\u6C42\u6570\uFF09",
718
+ investigateToggle: "\u5BA1\u67E5\u8005\u8C03\u67E5\u5DE5\u5177",
719
+ investigateHint: "\u5141\u8BB8\u5BA1\u67E5\u6A21\u578B\u5728\u88C1\u51B3\u524D\u7528\u53EA\u8BFB\u5DE5\u5177\uFF08\u5DE5\u4F5C\u533A\u5185\u641C\u7D22/\u8BFB\u6587\u4EF6\uFF09\u4EB2\u81EA\u6838\u5B9E\u4E8B\u5B9E\uFF1B\u91CD\u590D\u5EFA\u8BAE\u4E0E\u7A7A\u8BDD\u5EFA\u8BAE\u4F1A\u88AB\u81EA\u52A8\u53BB\u91CD/\u4E22\u5F03\u3002",
720
+ toolTitle: "\u54A8\u8BE2",
721
+ toolRunning: "\u8FDB\u884C\u4E2D",
722
+ toolDone: "\u5B8C\u6210",
723
+ toolFailed: "\u5931\u8D25",
724
+ toolConsulting: "\u6B63\u5728\u5411\u5BA1\u67E5\u6A21\u578B\u8F6C\u53D1\u6574\u6BB5\u4F1A\u8BDD\u2026",
725
+ toolExpand: "\u5C55\u5F00\u5EFA\u8BAE\u5168\u6587",
726
+ toolCollapse: "\u6536\u8D77",
727
+ patrolCardTitle: "\u5DE1\u903B\u88C1\u51B3",
728
+ patrolCheckTitle: "\u5DE1\u903B\u68C0\u67E5",
729
+ patrolVerdictOnTrack: "\u6B63\u5E38",
730
+ patrolVerdictCorrection: "\u7EA0\u504F",
731
+ patrolVerdictStop: "\u53EB\u505C",
732
+ patrolCardStepPrefix: "\u7B2C ",
733
+ patrolCardStepSuffix: " \u6B65",
734
+ patrolExpand: "\u5C55\u5F00\u88C1\u51B3\u5168\u6587",
735
+ patrolCollapse: "\u6536\u8D77",
736
+ patrolDowngradedTag: "\u51B7\u5374\u964D\u7EA7\xB7\u672A\u6CE8\u5165"
737
+ };
738
+ var en = {
739
+ title: "Advisor reviewer model",
740
+ description: "The executor model may call the zero-parameter advisor() tool when stuck, before major decisions, or before declaring done. The whole conversation is forwarded to this reviewer each call \u2014 escalation is not free.",
741
+ armed: "Current reviewer",
742
+ off: "None selected \u2014 the advisor tool is hidden from the model (zero prompt cost)",
743
+ offCurrent: "None selected",
744
+ choose: "Pick one of the models DSH already has configured:",
745
+ loading: "Loading model catalog\u2026",
746
+ loadFailed: "Failed to load the model catalog",
747
+ retry: "Retry",
748
+ partial: "Some providers failed to list models; the catalog may be incomplete.",
749
+ empty: "No configurable models \u2014 configure a provider under Settings \u2192 Models first.",
750
+ effort: "Reasoning effort",
751
+ effortDefault: "(model default)",
752
+ effortHint: "Leave empty for the model default; this model declares no effort support \u2014 an unsupported configured effort falls back to the model default automatically (see logs).",
753
+ effortHintKnown: "Only levels this model declares as supported are listed; empty means the model default.",
754
+ unavailable: "(configured route is not in the current catalog)",
755
+ save: "Save",
756
+ saving: "Saving\u2026",
757
+ discard: "Discard changes",
758
+ saved: "Saved",
759
+ saveFailed: "Save failed, please retry",
760
+ readonly: "The settings document is read-only here.",
761
+ patrolTitle: "Patrol mode (automatic drift detection)",
762
+ patrolDescription: "Every few steps the conversation snapshot is sent to the reviewer to check direction; on drift a correction is injected into the next request, and STOP asks the executor to halt and report. Each patrol bills the full conversation to the reviewer once.",
763
+ patrolToggle: "Enable patrol",
764
+ patrolEverySteps: "Patrol interval (steps)",
765
+ patrolEveryStepsHint: "One patrol check per N model requests; throttled to at most one patrol per 90 seconds.",
766
+ patrolImmuneTurns: "Correction cooldown (requests)",
767
+ investigateToggle: "Reviewer investigation tools",
768
+ investigateHint: "Let the reviewer verify facts with read-only tools (workspace search / file read) before ruling; duplicate and content-free advice is auto-deduped/dropped.",
769
+ toolTitle: "consultation",
770
+ toolRunning: "running",
771
+ toolDone: "done",
772
+ toolFailed: "failed",
773
+ toolConsulting: "forwarding the whole conversation to the reviewer\u2026",
774
+ toolExpand: "show guidance",
775
+ toolCollapse: "collapse",
776
+ patrolCardTitle: "patrol verdict",
777
+ patrolCheckTitle: "patrol check",
778
+ patrolVerdictOnTrack: "on track",
779
+ patrolVerdictCorrection: "correction",
780
+ patrolVerdictStop: "stop",
781
+ patrolCardStepPrefix: "step ",
782
+ patrolCardStepSuffix: "",
783
+ patrolExpand: "show verdict",
784
+ patrolCollapse: "collapse",
785
+ patrolDowngradedTag: "cooldown\xB7not injected"
786
+ };
787
+
788
+ // src/client/index.ts
789
+ var inject = ["slots", "locale", "connection", "settingsScope", "conversationEvents"];
790
+ var NS = "dsh-advisor.card";
791
+ function apply(ctx) {
792
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-advisor: card dictionaries");
793
+ const scope = ctx.settingsScope.bind({ namespace: "advisor" });
794
+ const controller = new AdvisorCardController(scope, ctx);
795
+ ctx.effect(() => () => {
796
+ controller.dispose();
797
+ }, "dsh-advisor: settings card");
798
+ ctx.slots.inject("settings.plugin.item", function* () {
799
+ yield ctx.slots.register({
800
+ name: "settings.plugin.item",
801
+ key: "advisor",
802
+ id: "advisor",
803
+ order: 30,
804
+ locale: NS,
805
+ inject: () => {
806
+ const t = ctx.locale.bind(NS);
807
+ return { ...controller.inject(), t };
808
+ }
809
+ }, AdvisorCard);
810
+ });
811
+ ctx.slots.inject("tool.call.toolview", function* () {
812
+ yield ctx.slots.register({
813
+ name: "tool.call.toolview",
814
+ key: "advisor",
815
+ locale: NS,
816
+ inject: () => ({ t: ctx.locale.bind(NS) })
817
+ }, AdvisorToolRow);
818
+ });
819
+ try {
820
+ const events = ctx.conversationEvents;
821
+ if (events !== void 0) {
822
+ ctx.effect(() => events.register(patrolChatDefinition()), "dsh-advisor: patrol chat definition");
823
+ ctx.slots.inject("conversation.chat.node", function* () {
824
+ yield ctx.slots.register({
825
+ name: "conversation.chat.node",
826
+ key: "advisor-patrol",
827
+ locale: NS,
828
+ inject: () => ({ t: ctx.locale.bind(NS) })
829
+ }, PatrolNodeView);
830
+ });
831
+ } else {
832
+ console.warn("[dsh-advisor] \u5F53\u524D UI \u672A\u63D0\u4F9B conversationEvents \u670D\u52A1\uFF0C\u5DE1\u903B\u5361\u7247\u4E0D\u53EF\u7528\uFF08\u88C1\u51B3\u4ECD\u4F1A\u5199\u65E5\u5FD7\u4E0E\u6CE8\u5165\uFF09");
833
+ }
834
+ } catch (error) {
835
+ console.warn("[dsh-advisor] \u5DE1\u903B\u5361\u7247\u6CE8\u518C\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5176\u4F59\u529F\u80FD\uFF09\uFF1A", error);
836
+ }
837
+ }
838
+
839
+ return module.exports;
840
+ }
841
+ });