@dsh-electron/dsh-plugin-multi-root-workspace 0.1.14 → 0.1.15

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 (61) hide show
  1. package/CHANGELOG.en.md +20 -1
  2. package/CHANGELOG.md +20 -1
  3. package/README.en.md +24 -7
  4. package/README.md +24 -7
  5. package/cordis.patch.yml +5 -1
  6. package/lib/{llm-message-fYORacvh.js → agent-instructions-BXuKhOzr.js} +1 -84
  7. package/lib/auto-review.js +2 -2
  8. package/lib/client.js +208 -26
  9. package/lib/client.js.map +1 -1
  10. package/lib/{command-Dcz6nULo.js → command-y5tfcAeC.js} +21 -10
  11. package/lib/command.js +1 -1
  12. package/lib/compat-CTJbpCxu.js +267 -0
  13. package/lib/compat.js +1 -1
  14. package/lib/{dialects-Dr4rDR-H.js → dialects-CdlG3RF9.js} +1 -1
  15. package/lib/{compat-veDqCOoa.js → dsh-version-DCwF7REk.js} +2 -264
  16. package/lib/index.js +10 -8
  17. package/lib/{instructions-KsUwUFE0.js → instructions-Ck9wp4J4.js} +695 -26
  18. package/lib/instructions.js +1 -1
  19. package/lib/llm-message-CepyzEBQ.js +84 -0
  20. package/lib/lsp.js +3 -3
  21. package/lib/{metadata-DgqSdVX_.js → metadata-99jgViHQ.js} +1 -1
  22. package/lib/{method-wrapper-CFY3p3j2.js → method-wrapper-D4oUCsEF.js} +22 -2
  23. package/lib/{native-win32-CRNXr11U.js → native-win32-C5oMtt7f.js} +1 -1
  24. package/lib/ops-compatibility.js +58 -0
  25. package/lib/ops-settings-UN2oOKEp.js +40 -0
  26. package/lib/ops-tools-k4JYaOrM.js +152 -0
  27. package/lib/process-fence.js +100 -24
  28. package/lib/{protocol-CnR1KTXy.js → protocol-CwDnFEyK.js} +8 -4
  29. package/lib/{registry-DR_sMuvS.js → registry-DyDNDSJZ.js} +235 -79
  30. package/lib/{registry-lease-win32-DDO-c7ig.js → registry-lease-win32-r4puac9m.js} +1 -1
  31. package/lib/registry.js +2 -2
  32. package/lib/{review-context-NDpKeMCc.js → review-context-B-oHkU6a.js} +24 -7
  33. package/lib/review-context.js +1 -1
  34. package/lib/{roots-Ca_W1S9n.js → roots-CmaWxRVy.js} +112 -22
  35. package/lib/{sandbox-C-vCJCp3.js → sandbox-CZElTNPz.js} +1 -1
  36. package/lib/sandbox.js +1 -1
  37. package/lib/scope.js +72 -9
  38. package/lib/skills-utyuvmwr.js +104 -0
  39. package/lib/skills.js +4 -105
  40. package/lib/{tool-context-CpgIFcCB.js → tool-context-CCeY3mrK.js} +1 -1
  41. package/lib/types/authority/protocol.d.ts +5 -1
  42. package/lib/types/client/locales.d.ts +28 -0
  43. package/lib/types/client/ops-settings.d.ts +6 -0
  44. package/lib/types/client/skills-settings.d.ts +3 -2
  45. package/lib/types/command.d.ts +3 -3
  46. package/lib/types/compat/instruction-activity.d.ts +32 -1
  47. package/lib/types/compat/ops-confirmation.d.ts +17 -0
  48. package/lib/types/compat/ops-settings.d.ts +11 -0
  49. package/lib/types/compat/ops-tools.d.ts +25 -0
  50. package/lib/types/compat/skills-client.d.ts +1 -0
  51. package/lib/types/compat/tool-context.d.ts +4 -0
  52. package/lib/types/compat.d.ts +2 -2
  53. package/lib/types/instructions.d.ts +6 -1
  54. package/lib/types/ops-compatibility.d.ts +34 -0
  55. package/lib/types/registry.d.ts +67 -8
  56. package/lib/types/review-context.d.ts +2 -0
  57. package/lib/types/roots.d.ts +114 -20
  58. package/lib/types/scope.d.ts +28 -2
  59. package/lib/workspace-files.js +3 -3
  60. package/lib/{workspace-routing-YPxaQ4V3.js → workspace-routing-D_qwMUh-.js} +4 -4
  61. package/package.json +5 -1
package/lib/client.js CHANGED
@@ -5593,6 +5593,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5593
5593
  "autoReview.loadFailed": "无法读取插件状态。",
5594
5594
  "autoReview.restartRequired": "更改将在重启后生效。",
5595
5595
  "autoReview.settingsFailed": "设置保存失败,请重试。",
5596
+ "ops.compatibilityEnable": "OPS 兼容模式",
5597
+ "ops.experimental": "实验性",
5598
+ "ops.compatibilityHint": "仅限 DSH 0.2.*。启用 OPS 写保护、实时活动与 Review 增强。受限模式可能拒绝附加根操作(包括读取),后台缓存可能残留;关闭时显式只读附加根可能被 OPS 写入。",
5599
+ "ops.settingsFailed": "设置保存失败,请重试。",
5600
+ "ops.confirmTitle": "开启 OPS 实验性兼容?",
5601
+ "ops.confirmDescription": "这仍是有限的实验性适配。开启后,附加根的合法读写及部分 PTC 操作可能被拒绝;Windows 存在只读边界时,OPS 读取和执行也可能被拒绝。Linux 批处理或结果读取、后台任务及清理可能不可用,并留下缓存。活动记录尚不持久化,恢复会话或审查动态操作时,Review 可能因证据不足而失败。目前尚未实现 OPS 全功能兼容,请仅在能接受这些限制时开启。",
5602
+ "ops.confirmAcknowledge": "我已了解上述限制,仍要开启实验性兼容",
5603
+ "ops.confirmCancel": "暂不开启",
5604
+ "ops.confirmClose": "关闭并保持未开启",
5605
+ "ops.confirmEnable": "确认开启",
5596
5606
  "skills.userOnly": "仅用户调用",
5597
5607
  "panel.skills": "目录技能",
5598
5608
  "panel.skillsEnabled": "此目录技能已启用,点击停用",
@@ -5637,6 +5647,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5637
5647
  "panel.copied": "已复制",
5638
5648
  "panel.reveal": "在文件管理器中显示",
5639
5649
  "panel.reRegister": "重新登记(保留原访问级别)",
5650
+ "panel.redirectedHint": "目标已改变:请移除该登记后再显式添加新目录。",
5640
5651
  "panel.moveUp": "上移",
5641
5652
  "panel.moveDown": "下移",
5642
5653
  "access.inherit": "跟随会话",
@@ -5673,6 +5684,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5673
5684
  "error.copy-failed": "无法写入剪贴板,请检查浏览器的剪贴板权限。",
5674
5685
  "error.session-not-found": "找不到该会话;请先打开一个工作区会话。",
5675
5686
  "error.unsupported-path": "该路径是网络或设备路径,Windows ACL 隔离无法覆盖它。",
5687
+ "error.protected-path": "该目录与 DSH 主目录或根登记存储目录重叠,不能登记。",
5688
+ "error.protected-boundary-unknown": "暂时无法确定 DSH 主目录等受保护目录的位置;恢复之前不能新增登记。",
5689
+ "error.revive-target-changed": "该登记现在指向另一个目录;请先移除它,再显式添加新目录。",
5676
5690
  "error.fallback": "操作失败。"
5677
5691
  };
5678
5692
  /** English copy, pinned to the Chinese key set. */
@@ -5686,6 +5700,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5686
5700
  "autoReview.loadFailed": "Could not read plugin state.",
5687
5701
  "autoReview.restartRequired": "The change takes effect after restart.",
5688
5702
  "autoReview.settingsFailed": "Could not save settings. Please try again.",
5703
+ "ops.compatibilityEnable": "OPS compatibility mode",
5704
+ "ops.experimental": "Experimental",
5705
+ "ops.compatibilityHint": "Requires DSH 0.2.*. Enables OPS write protection, live activity, and Review enhancements. Restricted modes may reject additional-root operations, including reads, and background caches may remain. When off, OPS may write to an explicitly read-only additional root.",
5706
+ "ops.settingsFailed": "Could not save settings. Please try again.",
5707
+ "ops.confirmTitle": "Enable experimental OPS compatibility?",
5708
+ "ops.confirmDescription": "This is a limited experimental adaptation. Valid additional-root reads/writes and some PTC calls may be rejected; Windows may also block OPS reads and execution when read-only boundaries apply. Linux batch/result operations, background tasks, and cleanup may fail or leave caches behind. Activity is not persisted: after resume, or when reviewing dynamic operations, Review may fail for lack of evidence. Full OPS compatibility is not available. Enable only if you accept these limitations.",
5709
+ "ops.confirmAcknowledge": "I understand these limitations and want to enable experimental compatibility",
5710
+ "ops.confirmCancel": "Keep disabled",
5711
+ "ops.confirmClose": "Close and keep disabled",
5712
+ "ops.confirmEnable": "Confirm and enable",
5689
5713
  "skills.userOnly": "User invocation only",
5690
5714
  "panel.skills": "Folder skills",
5691
5715
  "panel.skillsEnabled": "Skills enabled for this folder. Click to disable.",
@@ -5730,6 +5754,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5730
5754
  "panel.copied": "Copied",
5731
5755
  "panel.reveal": "Reveal in file manager",
5732
5756
  "panel.reRegister": "Re-register (keeps its access level)",
5757
+ "panel.redirectedHint": "The target changed: remove this registration, then add the new folder explicitly.",
5733
5758
  "panel.moveUp": "Move up",
5734
5759
  "panel.moveDown": "Move down",
5735
5760
  "access.inherit": "Inherit",
@@ -5766,6 +5791,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5766
5791
  "error.copy-failed": "Could not write to the clipboard; check the browser's clipboard permission.",
5767
5792
  "error.session-not-found": "That session is not active; open a workspace session first.",
5768
5793
  "error.unsupported-path": "That is a network or device path, which Windows ACL confinement cannot cover.",
5794
+ "error.protected-path": "That directory overlaps the DSH home or the registry storage directories; it cannot be registered.",
5795
+ "error.protected-boundary-unknown": "The protected DSH directories cannot be resolved right now; no folder can be registered until they can.",
5796
+ "error.revive-target-changed": "That registration now resolves to a different directory; remove it, then add the new folder explicitly.",
5769
5797
  "error.fallback": "The operation failed."
5770
5798
  };
5771
5799
  //#endregion
@@ -6682,10 +6710,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6682
6710
  `;
6683
6711
  //#endregion
6684
6712
  //#region src/compat/skills-client.ts
6685
- function bindSkillsForm(ctx) {
6713
+ function bindSettingsForm(ctx, entryId) {
6686
6714
  const modern = ctx.get("configForms");
6687
- if (modern !== void 0) return modern.get("multi-root-skills");
6688
- return ctx.get("settingsScope")?.bind({ namespace: "multi-root-skills" });
6715
+ if (modern !== void 0) return modern.get(entryId);
6716
+ return ctx.get("settingsScope")?.bind({ namespace: entryId });
6717
+ }
6718
+ function bindSkillsForm(ctx) {
6719
+ return bindSettingsForm(ctx, "multi-root-skills");
6689
6720
  }
6690
6721
  function clientInject(ctx, services, callback) {
6691
6722
  if (typeof ctx.inject === "function") ctx.inject(services, callback);
@@ -7122,6 +7153,123 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7122
7153
  });
7123
7154
  }
7124
7155
  //#endregion
7156
+ //#region src/compat/ops-confirmation.ts
7157
+ function opsRiskConfirmation() {
7158
+ return _deepseek_ai_dsh_client_ui_primitives.RiskConfirmation;
7159
+ }
7160
+ //#endregion
7161
+ //#region src/client/ops-settings.tsx
7162
+ function OpsCompatibilitySettingsCard({ form, t }) {
7163
+ const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
7164
+ const [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)();
7165
+ const [confirmation, setConfirmation] = (0, react.useState)();
7166
+ const [acknowledged, setAcknowledged] = (0, react.useState)(false);
7167
+ const saving = (0, react.useRef)(false);
7168
+ const mounted = (0, react.useRef)(true);
7169
+ const RiskConfirmation = opsRiskConfirmation();
7170
+ const canConfirm = confirmation !== void 0 && confirmation.form === form && confirmation.revision === snapshot.revision && snapshot.status === "ready" && snapshot.writable && snapshot.value?.enabled !== true && RiskConfirmation !== void 0;
7171
+ const dismiss = () => {
7172
+ setConfirmation(void 0);
7173
+ setAcknowledged(false);
7174
+ };
7175
+ (0, react.useEffect)(() => {
7176
+ mounted.current = true;
7177
+ return () => {
7178
+ mounted.current = false;
7179
+ };
7180
+ }, []);
7181
+ (0, react.useEffect)(() => {
7182
+ if (confirmation !== void 0 && !canConfirm) {
7183
+ setConfirmation(void 0);
7184
+ setAcknowledged(false);
7185
+ }
7186
+ }, [confirmation, canConfirm]);
7187
+ const save = async (enabled, revision) => {
7188
+ if (saving.current) return;
7189
+ saving.current = true;
7190
+ setBusy(true);
7191
+ setError(void 0);
7192
+ try {
7193
+ const ok = await form.mutate([{
7194
+ op: "set",
7195
+ path: ["enabled"],
7196
+ value: enabled
7197
+ }], revision);
7198
+ if (mounted.current && !ok) setError(t("ops.settingsFailed"));
7199
+ } catch {
7200
+ if (mounted.current) setError(t("ops.settingsFailed"));
7201
+ } finally {
7202
+ saving.current = false;
7203
+ if (mounted.current) setBusy(false);
7204
+ }
7205
+ };
7206
+ if (snapshot.status === "unavailable") return null;
7207
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7208
+ className: "mrfw-skillsSettings",
7209
+ children: [
7210
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7211
+ className: "mrfw-skillsSettingsText",
7212
+ children: [
7213
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7214
+ className: "mrfw-settingsTitleRow",
7215
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7216
+ className: "mrfw-skillsSettingsTitle",
7217
+ children: t("ops.compatibilityEnable")
7218
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
7219
+ tone: "info",
7220
+ children: t("ops.experimental")
7221
+ })]
7222
+ }),
7223
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7224
+ className: "mrfw-skillsSettingsDescription",
7225
+ children: t("ops.compatibilityHint")
7226
+ }),
7227
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
7228
+ role: "alert",
7229
+ children: error
7230
+ }) : null
7231
+ ]
7232
+ }),
7233
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
7234
+ label: t("ops.compatibilityEnable"),
7235
+ checked: snapshot.value?.enabled === true,
7236
+ disabled: busy || confirmation !== void 0 || snapshot.status !== "ready" || !snapshot.writable || snapshot.value?.enabled !== true && RiskConfirmation === void 0,
7237
+ onChange: (enabled) => {
7238
+ if (!enabled) {
7239
+ save(false, snapshot.revision);
7240
+ return;
7241
+ }
7242
+ setError(void 0);
7243
+ setAcknowledged(false);
7244
+ setConfirmation({
7245
+ form,
7246
+ revision: snapshot.revision
7247
+ });
7248
+ }
7249
+ }),
7250
+ RiskConfirmation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RiskConfirmation, {
7251
+ open: canConfirm,
7252
+ title: t("ops.confirmTitle"),
7253
+ description: t("ops.confirmDescription"),
7254
+ acknowledgeLabel: t("ops.confirmAcknowledge"),
7255
+ cancelLabel: t("ops.confirmCancel"),
7256
+ closeLabel: t("ops.confirmClose"),
7257
+ confirmLabel: t("ops.confirmEnable"),
7258
+ acknowledged,
7259
+ disabled: busy || !canConfirm,
7260
+ onAcknowledgedChange: setAcknowledged,
7261
+ onCancel: dismiss,
7262
+ onConfirm: () => {
7263
+ if (!canConfirm || !acknowledged || saving.current) return;
7264
+ const revision = confirmation.revision;
7265
+ dismiss();
7266
+ save(true, revision);
7267
+ }
7268
+ })
7269
+ ]
7270
+ });
7271
+ }
7272
+ //#endregion
7125
7273
  //#region src/client/skills-settings.tsx
7126
7274
  const BUNDLE_KEY = "@dsh-electron/dsh-plugin-multi-root-workspace";
7127
7275
  function SkillsSettingsCard({ form, t }) {
@@ -7189,42 +7337,55 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7189
7337
  /**
7190
7338
  * The bundle's one `plugins.bundle.config` cell: every plugin-level switch the
7191
7339
  * package offers, in display order. Each row owns its own form and write path —
7192
- * Skills through its settings namespace, Auto Review through the Plugin
7340
+ * Skills and OPS through their settings namespaces, Auto Review through the Plugin
7193
7341
  * Manager's Loader-row enablement — so one cell never serializes their state.
7194
7342
  */
7195
- function BundleSettingsCard({ form, autoReviewForm, t }) {
7196
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [form !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsSettingsCard, {
7197
- form,
7198
- t
7199
- }) : null, autoReviewForm !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoReviewSettingsCard, {
7200
- form: autoReviewForm,
7201
- t
7202
- }) : null] });
7343
+ function BundleSettingsCard({ form, autoReviewForm, opsForm, t }) {
7344
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
7345
+ form !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsSettingsCard, {
7346
+ form,
7347
+ t
7348
+ }) : null,
7349
+ autoReviewForm !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoReviewSettingsCard, {
7350
+ form: autoReviewForm,
7351
+ t
7352
+ }) : null,
7353
+ opsForm !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OpsCompatibilitySettingsCard, {
7354
+ form: opsForm,
7355
+ t
7356
+ }) : null
7357
+ ] });
7203
7358
  }
7204
7359
  function installSkillsSettingsCard(ctx, t) {
7205
7360
  let currentSkills;
7361
+ let currentOps;
7206
7362
  let currentAutoReview = false;
7207
7363
  let stop;
7208
7364
  const candidates = /* @__PURE__ */ new Map();
7209
7365
  const update = () => {
7210
- const skillsForm = [...candidates.entries()].sort(([a], [b]) => Number(b.get("configForms") !== void 0) - Number(a.get("configForms") !== void 0))[0]?.[1];
7366
+ const chosen = [...candidates.entries()].sort(([a], [b]) => Number(b.get("configForms") !== void 0) - Number(a.get("configForms") !== void 0))[0];
7367
+ const skillsForm = chosen?.[1].skills;
7368
+ const opsCandidate = chosen?.[1].ops;
7369
+ const opsForm = opsCandidate?.getSnapshot().status === "unavailable" ? void 0 : opsCandidate;
7211
7370
  const autoSnapshot = autoForm.getSnapshot();
7212
7371
  const autoAvailable = autoSnapshot.status === "loading" || autoSnapshot.status === "error" || autoSnapshot.status === "ready" && autoSnapshot.row !== void 0;
7213
- if (skillsForm === void 0 && !autoAvailable) {
7214
- if (currentSkills !== void 0 || currentAutoReview) {
7372
+ if (skillsForm === void 0 && opsForm === void 0 && !autoAvailable) {
7373
+ if (currentSkills !== void 0 || currentOps !== void 0 || currentAutoReview) {
7215
7374
  stop?.();
7216
7375
  stop = void 0;
7217
7376
  currentSkills = void 0;
7377
+ currentOps = void 0;
7218
7378
  currentAutoReview = false;
7219
7379
  }
7220
7380
  return;
7221
7381
  }
7222
- if (currentSkills === skillsForm && currentAutoReview === autoAvailable) return;
7382
+ if (currentSkills === skillsForm && currentOps === opsForm && currentAutoReview === autoAvailable) return;
7223
7383
  const slots = ctx.slots;
7224
7384
  if (slots === void 0) return;
7225
7385
  stop?.();
7226
7386
  stop = void 0;
7227
7387
  currentSkills = skillsForm;
7388
+ currentOps = opsForm;
7228
7389
  currentAutoReview = autoAvailable;
7229
7390
  const disposers = [slots.inject("plugins.bundle.config", () => slots.register({
7230
7391
  name: "plugins.bundle.config",
@@ -7232,6 +7393,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7232
7393
  inject: () => ({
7233
7394
  form: skillsForm,
7234
7395
  autoReviewForm: autoForm,
7396
+ opsForm,
7235
7397
  t
7236
7398
  })
7237
7399
  }, BundleSettingsCard))];
@@ -7243,6 +7405,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7243
7405
  t
7244
7406
  })
7245
7407
  }, SkillsSettingsCard)));
7408
+ if (opsForm !== void 0) disposers.push(slots.inject("settings.plugin.item", () => slots.register({
7409
+ name: "settings.plugin.item",
7410
+ key: "multi-root-ops-compatibility",
7411
+ inject: () => ({
7412
+ form: opsForm,
7413
+ t
7414
+ })
7415
+ }, OpsCompatibilitySettingsCard)));
7246
7416
  stop = () => {
7247
7417
  for (const dispose of disposers) dispose();
7248
7418
  };
@@ -7251,11 +7421,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7251
7421
  const stopAutoWatch = autoForm.subscribe(update);
7252
7422
  update();
7253
7423
  for (const service of ["settingsScope", "configForms"]) clientInject(ctx, [service], (scope) => {
7254
- const form = bindSkillsForm(scope);
7255
- if (form === void 0) return;
7256
- candidates.set(scope, form);
7424
+ const skills = bindSkillsForm(scope);
7425
+ const ops = scope.get("configForms")?.get("multi-root-ops-compatibility");
7426
+ if (skills === void 0 && ops === void 0) return;
7427
+ const stopOpsWatch = ops?.subscribe(update);
7428
+ candidates.set(scope, {
7429
+ skills,
7430
+ ops
7431
+ });
7257
7432
  update();
7258
7433
  return () => {
7434
+ stopOpsWatch?.();
7259
7435
  candidates.delete(scope);
7260
7436
  update();
7261
7437
  };
@@ -9394,12 +9570,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9394
9570
  accessChosen
9395
9571
  ]);
9396
9572
  /**
9397
- * Re-register one withheld or unusable root in place. The request carries
9398
- * the stored path and deliberately NO access: `registry.add` treats the call
9399
- * as a revive — it keeps the record's access, alias, and identity instead of
9573
+ * Re-register one MISSING root in place. The request carries the stored
9574
+ * path and deliberately NO access: `registry.add` treats the call as a
9575
+ * revive — it keeps the record's access, alias, and identity instead of
9400
9576
  * creating a new registration — which is also why the root limit must not
9401
- * gate this button: a revive consumes no slot, and it is the panel's only
9402
- * repair route for a redirected entry.
9577
+ * gate this button: a revive consumes no slot. Only `missing` rows get it:
9578
+ * a `redirected` registration now names a different directory, and revive
9579
+ * never changes the grant's target — replacing it is the explicit
9580
+ * remove-then-add flow the redirected hint describes (ADR-0027).
9403
9581
  */
9404
9582
  const reRegister = (0, react.useCallback)(async (root) => {
9405
9583
  await mutate("add", { path: root.path });
@@ -9627,14 +9805,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9627
9805
  className: "mrfw-stateWarn",
9628
9806
  children: t(stateKey(root.state))
9629
9807
  }),
9630
- root.state === "available" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9808
+ root.state === "missing" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9631
9809
  pending: state.busy,
9632
9810
  onClick: () => {
9633
9811
  reRegister(root);
9634
9812
  },
9635
9813
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRefresh, {}),
9636
9814
  label: t("panel.reRegister")
9637
- }),
9815
+ }) : null,
9816
+ root.state === "redirected" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9817
+ className: "mrfw-note",
9818
+ children: t("panel.redirectedHint")
9819
+ }) : null,
9638
9820
  root.skillsEnabled === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9639
9821
  pending: state.busy,
9640
9822
  pressed: root.skillsEnabled,