dsh-yolo-mode 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/)。
4
4
 
5
+ ## [0.6.2] - 2026-09-27
6
+
7
+ ### 修复:插件详情页的配置卡在引言、表单永不出现
8
+
9
+ 现象:0.6.1 起配置区块已出现在插件详情页,但只画出引言一句,表单始终不渲染。
10
+
11
+ 根因(浏览器控制台实锤):
12
+
13
+ ```
14
+ Uncaught (in promise) Error: cannot get property "remote.session" without inject
15
+ at YoloStore._fetchRemoteDirectory (store.js)
16
+ at async Promise.all (index 2)
17
+ at async YoloStore.load (store.js)
18
+ ```
19
+
20
+ - `_fetchRemoteDirectory()` 中的 `this.remote.session` 在 **try 块之外**,而客户端的 Remote
21
+ 代理是**按 inject 声明做门禁**的:读取未声明的命名空间会**抛错**,而不是返回 undefined。
22
+ 插件当时只声明了 `remote.llm`,**漏了 `remote.session`**。
23
+ - 而 `load()` 的 `Promise.all` **没有 try/catch**,这一抛就把 `status` 永久钉在 `loading`:
24
+ 界面既不显示表单、也不显示错误,只剩引言那一句(这个静默失败本身也是缺陷)。
25
+
26
+ 修复:
27
+
28
+ - `dsh.client.inject` 与源码的 `inject` 导出都补上 **`remote.session`**(`remote.llm` 同理,
29
+ 两个命名空间都必须声明,缺一即抛)。
30
+ - `store.js`:两处 Remote 命名空间属性读取移入 `try/catch`,缺失时**降级为「无目录」**,
31
+ 不再 reject `load()`。
32
+ - `store.js`:`load()` 整体包 `try/catch`,桥/传输 reject 时置 `status:'error'` 并保留原因,
33
+ 不再静默停在 `loading`。
34
+ - 测试 +3(**141/141**):inject 必须含两个 Remote 命名空间;`remote.session` /
35
+ `remote.llm` 属性读取抛错时目录降级且 `status` 仍 `ready`;桥 reject 时 `status='error'`
36
+ 且带原始原因。
37
+
38
+ ## [0.6.1] - 2026-09-27
39
+
40
+ ### 修复:配置位置改到插件详情页(0.6.0 放错了地方)
41
+
42
+ 0.6.0 把配置放进了输入框旁的 `YOLO <预设>` 胶囊浮层(`shell.overlay`)——那是**状态与日志**
43
+ 的位置,不是「插件的配置面板」。本次改用 DSH 插件管理页声明的专用槽位:
44
+
45
+ - **新增 `plugins.bundle.config` 注册**(key = npm 包名 `dsh-yolo-mode`):配置渲染在
46
+ 「插件列表 → dsh-yolo-mode」页面的**描述与组件行之间**。该槽位是 keyed slot,页面仅在
47
+ 有注册者(且 key 等于包名)时才渲染配置区块
48
+ (`dsh-client-ui-plugin-manager`:`configured = ledger.bundles.has(pkg.name)`)——
49
+ 这正是此前该页面完全没有配置区的原因。
50
+ - bundle 页可容纳多个条目,因此页面**不传宿主 `form`**,由本插件自持草稿、校验与保存
51
+ (仍走 `/yolo-mode` 设置桥 + 乐观修订锁)。`view === 'summary'` 时不渲染
52
+ (bundle 页只请求 `page`)。
53
+ - **胶囊浮层回归纯状态/日志**:撤销 0.6.0 给 `shell.overlay` 加的「配置 / 状态与日志」双标签;
54
+ 点胶囊仍是原来的统计卡 + 裁决表分页 + 打开日志 + 刷新。
55
+ - 文案 `tabConfig` / `tabStatus` 退役(不再有标签页);`settings.section` 保持退役。
56
+
5
57
  ## [0.6.0] - 2026-09-27
6
58
 
7
59
  ### 变更:全部配置移入插件自身 UI 面板(设置页退役)
@@ -15,8 +15,6 @@ window.__ModuleLoader__.load({
15
15
  const NS = "settings.yoloMode";
16
16
  /** English dictionary. */
17
17
  const en = {
18
- tabConfig: "Configuration",
19
- tabStatus: "Status & log",
20
18
  chip: "YOLO status",
21
19
  sectionIntro: "Configure how YOLO mode auto-judges sandbox escalation approval requests.",
22
20
  loadError: "Could not load YOLO mode settings.",
@@ -74,8 +72,6 @@ window.__ModuleLoader__.load({
74
72
  };
75
73
  /** Chinese dictionary. */
76
74
  const zh = {
77
- tabConfig: "配置",
78
- tabStatus: "状态与日志",
79
75
  chip: "YOLO 状态",
80
76
  sectionIntro: "配置 YOLO 模式如何自动裁决沙箱升权审批申请。",
81
77
  loadError: "无法加载 YOLO 模式设置。",
@@ -398,7 +394,12 @@ window.__ModuleLoader__.load({
398
394
  * no `remote.llm` namespace is present, so the caller can fall back.
399
395
  */
400
396
  async _fetchRemoteDirectory() {
401
- const llm = this.remote ? this.remote.llm : null;
397
+ let llm = null;
398
+ try {
399
+ llm = this.remote ? this.remote.llm : null;
400
+ } catch {
401
+ llm = null;
402
+ }
402
403
  if (llm === null || llm === void 0) return void 0;
403
404
  let providers = [];
404
405
  let models = [];
@@ -408,7 +409,12 @@ window.__ModuleLoader__.load({
408
409
  } catch {
409
410
  providers = [];
410
411
  }
411
- const session = this.remote ? this.remote.session : null;
412
+ let session = null;
413
+ try {
414
+ session = this.remote ? this.remote.session : null;
415
+ } catch {
416
+ session = null;
417
+ }
412
418
  if (session && typeof session.modelCatalog === "function") try {
413
419
  const response = await session.modelCatalog();
414
420
  const value = response && response.ok === true ? response.value : void 0;
@@ -460,11 +466,24 @@ window.__ModuleLoader__.load({
460
466
  status: "loading",
461
467
  error: void 0
462
468
  });
463
- const [viewResult, statusResult, directory] = await Promise.all([
464
- this._call(YOLO_RPC_VIEW, {}),
465
- this._call(YOLO_RPC_STATUS, {}),
466
- this._fetchLlmDirectory()
467
- ]);
469
+ let viewResult;
470
+ let statusResult;
471
+ let directory;
472
+ try {
473
+ [viewResult, statusResult, directory] = await Promise.all([
474
+ this._call(YOLO_RPC_VIEW, {}),
475
+ this._call(YOLO_RPC_STATUS, {}),
476
+ this._fetchLlmDirectory()
477
+ ]);
478
+ } catch (error) {
479
+ if (generation !== this._generation) return;
480
+ this.set({
481
+ status: "error",
482
+ error: error !== null && typeof error === "object" && typeof error.message === "string" ? error.message : true,
483
+ conflicted: false
484
+ });
485
+ return;
486
+ }
468
487
  if (generation !== this._generation) return;
469
488
  if (!viewResult.ok || !statusResult.ok) {
470
489
  const code = this._errorCode(!viewResult.ok ? viewResult.error : statusResult.error);
@@ -558,60 +577,6 @@ window.__ModuleLoader__.load({
558
577
  }
559
578
  };
560
579
  //#endregion
561
- //#region src/client/ui/Chip.js
562
- /**
563
- * YOLO mode status chip (slot `conversation.input.left`). Shows "YOLO <preset>"
564
- * and toggles the shared popup via store.togglePopup().
565
- */
566
- const chipStyle = {
567
- display: "inline-flex",
568
- alignItems: "center",
569
- gap: 6,
570
- padding: "4px 10px",
571
- borderRadius: 999,
572
- backgroundColor: "#ffffff",
573
- color: "#1d4ed8",
574
- border: "1px solid #93c5fd",
575
- fontSize: 12,
576
- cursor: "pointer",
577
- whiteSpace: "nowrap",
578
- userSelect: "none"
579
- };
580
- /** Status colour for the leading dot based on judge readiness. */
581
- function dotColor(statusInfo) {
582
- return statusInfo && statusInfo.judgeConfigured ? "#16a34a" : "#d97706";
583
- }
584
- /**
585
- * @param {object} props - slot-delivered { store, useSnapshot, t } (inject face).
586
- */
587
- function Chip(props) {
588
- const store = props.store;
589
- const useSnapshot = props.useSnapshot;
590
- if (store === void 0 || useSnapshot === void 0) return null;
591
- return ChipLoaded({
592
- store,
593
- useSnapshot
594
- });
595
- }
596
- function ChipLoaded({ store, useSnapshot }) {
597
- (0, react.useEffect)(() => {
598
- store.load();
599
- }, [store]);
600
- const statusInfo = useSnapshot((s) => s).statusInfo;
601
- const preset = statusInfo && typeof statusInfo.preset === "string" ? statusInfo.preset : "…";
602
- return (0, react.createElement)("button", {
603
- style: chipStyle,
604
- title: "YOLO",
605
- onClick: () => store.togglePopup()
606
- }, (0, react.createElement)("span", { style: {
607
- width: 8,
608
- height: 8,
609
- borderRadius: 4,
610
- backgroundColor: dotColor(statusInfo),
611
- display: "inline-block"
612
- } }), "YOLO " + preset);
613
- }
614
- //#endregion
615
580
  //#region src/client/presets.js
616
581
  /**
617
582
  * Static per-preset display data for the YOLO settings page: the level table
@@ -701,8 +666,10 @@ window.__ModuleLoader__.load({
701
666
  //#endregion
702
667
  //#region src/client/ui/ConfigForm.js
703
668
  /**
704
- * YOLO mode configuration form. Rendered inside the plugin's own UI panel
705
- * (`shell.overlay`, see ./Popup.js): the DSH `settings.section` page was retired
669
+ * YOLO mode configuration form. Rendered on this plugin's own page in the DSH
670
+ * plugin manager (插件列表 → dsh-yolo-mode), via the `plugins.bundle.config`
671
+ * slot keyed by the package name (see ../index.js) — between the package
672
+ * description and the component rows. The `settings.section` page was retired,
706
673
  * so this form is the single configuration surface for the plugin. It renders
707
674
  * the resolved configuration (view.value) and saves via path ops through
708
675
  * store.mutate with an optimistic-revision lock. Everything is React.createElement
@@ -889,14 +856,21 @@ window.__ModuleLoader__.load({
889
856
  }
890
857
  };
891
858
  /**
892
- * The YOLO configuration form (hosted by the plugin's own UI panel).
859
+ * The YOLO configuration form.
860
+ *
861
+ * Hosted by this plugin's own page in the plugin manager: registered into
862
+ * `plugins.bundle.config` under the package name (see ../index.js). The page
863
+ * dispatches `view`; a bundle page only ever asks for `page`, and a bundle may
864
+ * hold several configuration entries, so the page passes NO host `form` — this
865
+ * component owns its draft, validation and save path (the /yolo-mode bridge).
893
866
  *
894
- * @param {object} props - panel-delivered inject face: { store, useSnapshot, t }
895
- * plus optional close() to dismiss the hosting panel.
867
+ * @param {object} props - slot props `{ view? }` merged with the inject face
868
+ * `{ store, useSnapshot, t }`; an optional `close()` adds a dismiss control.
896
869
  */
897
870
  function ConfigForm(props) {
898
- const { store, useSnapshot, t, close } = props;
871
+ const { view, store, useSnapshot, t, close } = props;
899
872
  if (store === void 0 || useSnapshot === void 0 || t === void 0) return null;
873
+ if (view === "summary") return null;
900
874
  return ReactSection({
901
875
  store,
902
876
  useSnapshot,
@@ -1154,20 +1128,69 @@ window.__ModuleLoader__.load({
1154
1128
  return ops;
1155
1129
  }
1156
1130
  //#endregion
1131
+ //#region src/client/ui/Chip.js
1132
+ /**
1133
+ * YOLO mode status chip (slot `conversation.input.left`). Shows "YOLO <preset>"
1134
+ * and toggles the shared popup via store.togglePopup().
1135
+ */
1136
+ const chipStyle = {
1137
+ display: "inline-flex",
1138
+ alignItems: "center",
1139
+ gap: 6,
1140
+ padding: "4px 10px",
1141
+ borderRadius: 999,
1142
+ backgroundColor: "#ffffff",
1143
+ color: "#1d4ed8",
1144
+ border: "1px solid #93c5fd",
1145
+ fontSize: 12,
1146
+ cursor: "pointer",
1147
+ whiteSpace: "nowrap",
1148
+ userSelect: "none"
1149
+ };
1150
+ /** Status colour for the leading dot based on judge readiness. */
1151
+ function dotColor(statusInfo) {
1152
+ return statusInfo && statusInfo.judgeConfigured ? "#16a34a" : "#d97706";
1153
+ }
1154
+ /**
1155
+ * @param {object} props - slot-delivered { store, useSnapshot, t } (inject face).
1156
+ */
1157
+ function Chip(props) {
1158
+ const store = props.store;
1159
+ const useSnapshot = props.useSnapshot;
1160
+ if (store === void 0 || useSnapshot === void 0) return null;
1161
+ return ChipLoaded({
1162
+ store,
1163
+ useSnapshot
1164
+ });
1165
+ }
1166
+ function ChipLoaded({ store, useSnapshot }) {
1167
+ (0, react.useEffect)(() => {
1168
+ store.load();
1169
+ }, [store]);
1170
+ const statusInfo = useSnapshot((s) => s).statusInfo;
1171
+ const preset = statusInfo && typeof statusInfo.preset === "string" ? statusInfo.preset : "…";
1172
+ return (0, react.createElement)("button", {
1173
+ style: chipStyle,
1174
+ title: "YOLO",
1175
+ onClick: () => store.togglePopup()
1176
+ }, (0, react.createElement)("span", { style: {
1177
+ width: 8,
1178
+ height: 8,
1179
+ borderRadius: 4,
1180
+ backgroundColor: dotColor(statusInfo),
1181
+ display: "inline-block"
1182
+ } }), "YOLO " + preset);
1183
+ }
1184
+ //#endregion
1157
1185
  //#region src/client/ui/Popup.js
1158
1186
  /**
1159
- * YOLO mode panel (slot `shell.overlay`). Renders null while the store's open
1160
- * flag is false. When open it hosts TWO tabs and is the plugin's single
1161
- * configuration surface (the DSH `settings.section` page was retired):
1162
- *
1163
- * - `config` (default): the full configuration form (preset / modes / judge
1164
- * provider+model / timeout / maxTokens / concurrency / system prompt /
1165
- * levels), reusing ./ConfigForm.js.
1166
- * - `status`: the stats card, the recent decisions table (paged, PAGE_SIZE per
1167
- * page, newest first), the "open audit log" button and a refresh button.
1187
+ * YOLO mode status popup (slot `shell.overlay`). Renders null while the store's
1188
+ * open flag is false; otherwise a stats card plus the recent decisions table
1189
+ * (paged, PAGE_SIZE per page, newest first), an "open audit log" button, and a
1190
+ * refresh button.
1168
1191
  *
1169
- * Guard/hook split mirrors ConfigForm: the outer component validates the slot
1170
- * inject face, the inner component owns all hooks unconditionally.
1192
+ * Guard/hook split mirrors ConfigForm: the outer component validates the
1193
+ * slot inject face, the inner component owns all hooks unconditionally.
1171
1194
  */
1172
1195
  /** Rows per page of the recent-decisions table. */
1173
1196
  const PAGE_SIZE = 5;
@@ -1284,35 +1307,6 @@ window.__ModuleLoader__.load({
1284
1307
  };
1285
1308
  const logResultOk = { color: "#16a34a" };
1286
1309
  const logResultErr = { color: "#dc2626" };
1287
- /** Body wrapper for the status tab (the config tab renders ConfigForm directly). */
1288
- const statusBodyStyle = {
1289
- display: "flex",
1290
- flexDirection: "column",
1291
- gap: 12
1292
- };
1293
- const tabStyle = {
1294
- backgroundColor: "#ffffff",
1295
- color: "#374151",
1296
- border: "1px solid #cbd5e1",
1297
- borderRadius: 6,
1298
- padding: "5px 12px",
1299
- fontSize: 12,
1300
- cursor: "pointer"
1301
- };
1302
- const tabActiveStyle = {
1303
- backgroundColor: "#2563eb",
1304
- color: "#ffffff",
1305
- borderColor: "#2563eb"
1306
- };
1307
- /** One panel tab switch; the active tab is highlighted and non-submit. */
1308
- function tabButton(t, id, current, setTab) {
1309
- return (0, react.createElement)("button", {
1310
- key: id,
1311
- type: "button",
1312
- style: Object.assign({}, tabStyle, id === current ? tabActiveStyle : {}),
1313
- onClick: () => setTab(id)
1314
- }, t(id === "config" ? "tabConfig" : "tabStatus"));
1315
- }
1316
1310
  /** Truncate long reason text for display. */
1317
1311
  function truncate(text, max) {
1318
1312
  if (typeof text !== "string") return "";
@@ -1334,7 +1328,6 @@ window.__ModuleLoader__.load({
1334
1328
  }
1335
1329
  function ReactPopup({ store, useSnapshot, t }) {
1336
1330
  const state = useSnapshot((s) => s);
1337
- const [tab, setTab] = (0, react.useState)("config");
1338
1331
  const [page, setPage] = (0, react.useState)(0);
1339
1332
  const [logResult, setLogResult] = (0, react.useState)(void 0);
1340
1333
  const [logBusy, setLogBusy] = (0, react.useState)(false);
@@ -1372,55 +1365,43 @@ window.__ModuleLoader__.load({
1372
1365
  setLogBusy(false);
1373
1366
  }
1374
1367
  };
1375
- const statusBody = [
1376
- logResult !== void 0 ? (0, react.createElement)("p", { style: Object.assign({}, captionStyle, logResult.ok ? logResultOk : logResultErr) }, logResult.ok ? t("openLogOk") + (logResult.value && logResult.value.path ? ": " + logResult.value.path : "") : logMessage(t, logResult.error)) : auditFile !== void 0 ? (0, react.createElement)("p", { style: captionStyle }, t("logPath") + ": " + auditFile) : null,
1377
- (0, react.createElement)("div", { style: statGrid }, statCellOf(t, "statsTotal", stats.total), statCellOf(t, "statsAllowed", stats.allowed), statCellOf(t, "statsRejected", stats.rejected), statCellOf(t, "statsDelegated", stats.delegated)),
1378
- recent.length === 0 ? (0, react.createElement)("p", { style: {
1379
- color: "#6b7280",
1380
- fontSize: 12,
1381
- margin: 0
1382
- } }, t("recentEmpty")) : (0, react.createElement)("table", { style: tableStyle }, (0, react.createElement)("thead", {}, (0, react.createElement)("tr", {}, (0, react.createElement)("th", { style: thStyle }, t("tableTime")), (0, react.createElement)("th", { style: thStyle }, t("tableTool")), (0, react.createElement)("th", { style: thStyle }, t("tableTarget")), (0, react.createElement)("th", { style: thStyle }, t("tableDecision")), (0, react.createElement)("th", { style: thStyle }, t("tableOutcome")), (0, react.createElement)("th", { style: thStyle }, t("tableReason")))), (0, react.createElement)("tbody", {}, pageRows.map((row, idx) => tableRow(t, row, safePage * PAGE_SIZE + idx)))),
1383
- totalPages > 1 ? (0, react.createElement)("div", { style: {
1384
- display: "flex",
1385
- alignItems: "center",
1386
- justifyContent: "flex-end",
1387
- gap: 8
1388
- } }, (0, react.createElement)("button", {
1389
- style: Object.assign({}, pagerButton, safePage === 0 ? pagerButtonDisabled : {}),
1390
- disabled: safePage === 0,
1391
- onClick: () => setPage(safePage - 1)
1392
- }, t("pagePrev")), (0, react.createElement)("span", { style: {
1393
- fontSize: 12,
1394
- color: "#374151"
1395
- } }, t("pageOf", {
1396
- current: String(safePage + 1),
1397
- total: String(totalPages)
1398
- })), (0, react.createElement)("button", {
1399
- style: Object.assign({}, pagerButton, safePage >= totalPages - 1 ? pagerButtonDisabled : {}),
1400
- disabled: safePage >= totalPages - 1,
1401
- onClick: () => setPage(safePage + 1)
1402
- }, t("pageNext"))) : null,
1403
- (0, react.createElement)("div", { style: {
1404
- display: "flex",
1405
- justifyContent: "flex-end"
1406
- } }, (0, react.createElement)("button", {
1407
- style: primaryButton,
1408
- onClick: () => store.togglePopup()
1409
- }, t("close")))
1410
- ];
1411
- return (0, react.createElement)("div", { style: popupStyle }, (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("strong", {}, t("chip")), (0, react.createElement)("div", { style: headerButtons }, tabButton(t, "config", tab, setTab), tabButton(t, "status", tab, setTab), tab === "status" ? (0, react.createElement)("button", {
1368
+ return (0, react.createElement)("div", { style: popupStyle }, (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("strong", {}, t("chip")), (0, react.createElement)("div", { style: headerButtons }, (0, react.createElement)("button", {
1412
1369
  style: logBusy ? Object.assign({}, secondaryButton, { opacity: .6 }) : secondaryButton,
1413
1370
  onClick: () => void openLog(),
1414
1371
  title: auditFile !== void 0 ? auditFile : void 0
1415
- }, logBusy ? t("openLogBusy") : t("openLog")) : null, (0, react.createElement)("button", {
1372
+ }, logBusy ? t("openLogBusy") : t("openLog")), (0, react.createElement)("button", {
1416
1373
  style: primaryButton,
1417
1374
  onClick: () => void store.load()
1418
- }, t("refresh")))), tab === "config" ? (0, react.createElement)(ConfigForm, {
1419
- store,
1420
- useSnapshot,
1421
- t,
1422
- close: () => store.togglePopup()
1423
- }) : (0, react.createElement)("div", { style: statusBodyStyle }, ...statusBody));
1375
+ }, t("refresh")))), logResult !== void 0 ? (0, react.createElement)("p", { style: Object.assign({}, captionStyle, logResult.ok ? logResultOk : logResultErr) }, logResult.ok ? t("openLogOk") + (logResult.value && logResult.value.path ? ": " + logResult.value.path : "") : logMessage(t, logResult.error)) : auditFile !== void 0 ? (0, react.createElement)("p", { style: captionStyle }, t("logPath") + ": " + auditFile) : null, (0, react.createElement)("div", { style: statGrid }, statCellOf(t, "statsTotal", stats.total), statCellOf(t, "statsAllowed", stats.allowed), statCellOf(t, "statsRejected", stats.rejected), statCellOf(t, "statsDelegated", stats.delegated)), recent.length === 0 ? (0, react.createElement)("p", { style: {
1376
+ color: "#6b7280",
1377
+ fontSize: 12,
1378
+ margin: 0
1379
+ } }, t("recentEmpty")) : (0, react.createElement)("table", { style: tableStyle }, (0, react.createElement)("thead", {}, (0, react.createElement)("tr", {}, (0, react.createElement)("th", { style: thStyle }, t("tableTime")), (0, react.createElement)("th", { style: thStyle }, t("tableTool")), (0, react.createElement)("th", { style: thStyle }, t("tableTarget")), (0, react.createElement)("th", { style: thStyle }, t("tableDecision")), (0, react.createElement)("th", { style: thStyle }, t("tableOutcome")), (0, react.createElement)("th", { style: thStyle }, t("tableReason")))), (0, react.createElement)("tbody", {}, pageRows.map((row, idx) => tableRow(t, row, safePage * PAGE_SIZE + idx)))), totalPages > 1 ? (0, react.createElement)("div", { style: {
1380
+ display: "flex",
1381
+ alignItems: "center",
1382
+ justifyContent: "flex-end",
1383
+ gap: 8
1384
+ } }, (0, react.createElement)("button", {
1385
+ style: Object.assign({}, pagerButton, safePage === 0 ? pagerButtonDisabled : {}),
1386
+ disabled: safePage === 0,
1387
+ onClick: () => setPage(safePage - 1)
1388
+ }, t("pagePrev")), (0, react.createElement)("span", { style: {
1389
+ fontSize: 12,
1390
+ color: "#374151"
1391
+ } }, t("pageOf", {
1392
+ current: String(safePage + 1),
1393
+ total: String(totalPages)
1394
+ })), (0, react.createElement)("button", {
1395
+ style: Object.assign({}, pagerButton, safePage >= totalPages - 1 ? pagerButtonDisabled : {}),
1396
+ disabled: safePage >= totalPages - 1,
1397
+ onClick: () => setPage(safePage + 1)
1398
+ }, t("pageNext"))) : null, (0, react.createElement)("div", { style: {
1399
+ display: "flex",
1400
+ justifyContent: "flex-end"
1401
+ } }, (0, react.createElement)("button", {
1402
+ style: primaryButton,
1403
+ onClick: () => store.togglePopup()
1404
+ }, t("close"))));
1424
1405
  }
1425
1406
  /** User-facing message for a failed open-log attempt (by error code). */
1426
1407
  function logMessage(t, error) {
@@ -1476,24 +1457,34 @@ window.__ModuleLoader__.load({
1476
1457
  /**
1477
1458
  * YOLO mode — browser half (DSH client plugin).
1478
1459
  *
1479
- * Registers the `conversation.input.left` status chip and the `shell.overlay`
1480
- * panel. ALL configuration (preset/modes/judge/levels) lives in that panel: the
1481
- * `settings.section` page was retired, so nothing is configured from the DSH
1460
+ * Registers three slots:
1461
+ * - `plugins.bundle.config` (key `dsh-yolo-mode`): ALL configuration, rendered
1462
+ * on this plugin's own page in the plugin manager (插件列表 → dsh-yolo-mode),
1463
+ * between the package description and the component rows;
1464
+ * - `conversation.input.left`: the `YOLO <preset>` status chip;
1465
+ * - `shell.overlay`: the stats / recent-decisions panel that chip opens.
1466
+ *
1467
+ * The `settings.section` page stays retired: nothing is configured from the DSH
1482
1468
  * Settings surface any more. Data flows through the connection's generic RPC
1483
1469
  * channel (/yolo-mode) into a snapshot store; writes travel as path ops through
1484
1470
  * settingsMutate with an optimistic-revision lock.
1485
1471
  */
1486
1472
  /**
1487
1473
  * Services required by these slot registrations (dsh.client.inject is the same
1488
- * short-name set): slots, locale, connection, remote, remote.llm. The dotted
1489
- * `remote.llm` namespace carries the provider/model directory on 0.1.7+.
1474
+ * short-name set): slots, locale, connection, remote, remote.llm, remote.session.
1475
+ * Both dotted Remote namespaces are REQUIRED, not optional: the client Remote
1476
+ * proxy is inject-gated and throws `cannot get property "remote.X" without
1477
+ * inject` on an undeclared read — it does not return undefined. The store reads
1478
+ * `remote.llm` for the provider directory and `remote.session` for the model
1479
+ * catalog, so both must be declared here (and in package.json's dsh.client.inject).
1490
1480
  */
1491
1481
  const inject = [
1492
1482
  "slots",
1493
1483
  "locale",
1494
1484
  "connection",
1495
1485
  "remote",
1496
- "remote.llm"
1486
+ "remote.llm",
1487
+ "remote.session"
1497
1488
  ];
1498
1489
  /**
1499
1490
  * Register the YOLO chip/panel once the slot declarations are on the ledger,
@@ -1535,6 +1526,12 @@ window.__ModuleLoader__.load({
1535
1526
  useSnapshot,
1536
1527
  t
1537
1528
  });
1529
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
1530
+ name: "plugins.bundle.config",
1531
+ key: "dsh-yolo-mode",
1532
+ locale: NS,
1533
+ inject: injected
1534
+ }, ConfigForm));
1538
1535
  ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
1539
1536
  name: "conversation.input.left",
1540
1537
  id: "yolo-mode-chip",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-yolo-mode",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "dsh-yolo-mode —— DeepSeek Harness 双面包插件:当会话处于可写沙箱模式且审批策略为 ask 时,用大模型自动裁决沙箱升权申请,支持内置预设与自定义权限层级,并提供宿主 settings + 自发布设置桥(/yolo-mode)与 Web 客户端 UI。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -73,7 +73,8 @@
73
73
  "locale",
74
74
  "connection",
75
75
  "remote",
76
- "remote.llm"
76
+ "remote.llm",
77
+ "remote.session"
77
78
  ]
78
79
  },
79
80
  "compatibility": {