@dsh-electron/dsh-plugin-multi-root-workspace 0.1.9 → 0.1.10

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 (50) hide show
  1. package/CHANGELOG.en.md +14 -1
  2. package/CHANGELOG.md +14 -1
  3. package/README.en.md +23 -8
  4. package/README.md +23 -8
  5. package/cordis.patch.yml +4 -0
  6. package/lib/auto-review.js +1 -1
  7. package/lib/client.js +573 -131
  8. package/lib/client.js.map +1 -1
  9. package/lib/{command-Bwd6EGNV.js → command-BGdz7VBH.js} +65 -5
  10. package/lib/command.js +1 -1
  11. package/lib/{compat-CagUNzRB.js → compat-D-MljsPV.js} +12 -1
  12. package/lib/compat.js +1 -1
  13. package/lib/{contract-DZ4zczDT.js → contract-CStSEOcK.js} +44 -1
  14. package/lib/index.js +6 -5
  15. package/lib/{instructions-CGNS3H0H.js → instructions-MePoRigy.js} +2 -206
  16. package/lib/instructions.js +1 -1
  17. package/lib/llm-message-fYORacvh.js +206 -0
  18. package/lib/metadata-DgqSdVX_.js +35 -0
  19. package/lib/{registry-BClm6wfh.js → registry-KSsmU393.js} +60 -7
  20. package/lib/registry.js +1 -1
  21. package/lib/{review-context-D-ohSVmA.js → review-context-AH-SXHmj.js} +1 -1
  22. package/lib/review-context.js +1 -1
  23. package/lib/scope.js +1 -1
  24. package/lib/skills.js +790 -0
  25. package/lib/types/client/WorkspaceFoldersAction.d.ts +1 -1
  26. package/lib/types/client/locales.d.ts +26 -16
  27. package/lib/types/client/skills-client.d.ts +23 -0
  28. package/lib/types/client/skills-settings.d.ts +10 -0
  29. package/lib/types/client/skills.d.ts +36 -0
  30. package/lib/types/client/styles.d.ts +1 -1
  31. package/lib/types/command.d.ts +1 -1
  32. package/lib/types/compat/client-session.d.ts +2 -0
  33. package/lib/types/compat/dsh-version.d.ts +5 -5
  34. package/lib/types/compat/skill-content.d.ts +3 -0
  35. package/lib/types/compat/skills-client.d.ts +33 -0
  36. package/lib/types/compat/skills-settings.d.ts +8 -0
  37. package/lib/types/compat/skills.d.ts +13 -0
  38. package/lib/types/compat/storage-zod.d.ts +1 -0
  39. package/lib/types/contract.d.ts +12 -1
  40. package/lib/types/registry.d.ts +7 -0
  41. package/lib/types/roots.d.ts +4 -1
  42. package/lib/types/skills/catalog.d.ts +34 -0
  43. package/lib/types/skills/invocation.d.ts +7 -0
  44. package/lib/types/skills/metadata.d.ts +13 -0
  45. package/lib/types/skills/pool.d.ts +19 -0
  46. package/lib/types/skills/reader.d.ts +3 -0
  47. package/lib/types/skills/routing.d.ts +2 -0
  48. package/lib/types/skills-contract.d.ts +39 -0
  49. package/lib/types/skills.d.ts +43 -0
  50. package/package.json +96 -83
package/lib/client.js CHANGED
@@ -36,12 +36,12 @@ window.__ModuleLoader__.load({
36
36
  }) : target, mod));
37
37
  //#endregion
38
38
  let react = require("react");
39
- let react_dom = require("react-dom");
40
39
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
41
40
  _deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
41
+ let react_jsx_runtime = require("react/jsx-runtime");
42
+ let react_dom = require("react-dom");
42
43
  require("@deepseek-ai/cordis");
43
44
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
44
- let react_jsx_runtime = require("react/jsx-runtime");
45
45
  //#region src/compat/client-session.ts
46
46
  /**
47
47
  * The session the composed client is showing, when it can name one.
@@ -62,6 +62,17 @@ window.__ModuleLoader__.load({
62
62
  const list = sessions.list;
63
63
  return typeof list?.subscribe === "function" ? list.subscribe(listener) : () => {};
64
64
  }
65
+ /** Selection changes only: activity/title updates must not close a Skill preview. */
66
+ function subscribeCurrentSession(sessions, listener) {
67
+ let previous = currentSessionIdOf(sessions);
68
+ return subscribeSessions(sessions, () => {
69
+ const current = currentSessionIdOf(sessions);
70
+ if (current !== previous) {
71
+ previous = current;
72
+ listener();
73
+ }
74
+ });
75
+ }
65
76
  /**
66
77
  * The list snapshot, when the service actually exposes one.
67
78
  *
@@ -5248,6 +5259,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5248
5259
  return /* @__PURE__ */ _superRefine(fn, params);
5249
5260
  }
5250
5261
  //#endregion
5262
+ //#region src/skills-contract.ts
5263
+ /** Browser-safe Skills wire shapes. Session IDs select authority; clients never send paths. */
5264
+ const nameSchema = string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$/);
5265
+ object({
5266
+ sessionId: string().min(1),
5267
+ name: nameSchema.optional(),
5268
+ generation: number().int().nonnegative().optional()
5269
+ }).strict();
5270
+ const status = {
5271
+ sessionId: string(),
5272
+ generation: number().int().nonnegative(),
5273
+ enabled: boolean()
5274
+ };
5275
+ const skillsStatusSchema = object(status);
5276
+ const skillsCatalogSchema = object({
5277
+ ...status,
5278
+ skills: array(object({
5279
+ name: nameSchema,
5280
+ description: string(),
5281
+ modelInvocable: boolean()
5282
+ }))
5283
+ });
5284
+ const skillsPreviewSchema = object({
5285
+ ...status,
5286
+ name: nameSchema,
5287
+ content: string()
5288
+ });
5289
+ //#endregion
5251
5290
  //#region src/contract.ts
5252
5291
  /**
5253
5292
  * The wire contract between the browser panel and this plugin's host half.
@@ -5291,6 +5330,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5291
5330
  }).strict().optional(),
5292
5331
  id: string().optional(),
5293
5332
  path: string().optional(),
5333
+ aliasId: string().optional(),
5334
+ skillsEnabled: boolean().optional(),
5294
5335
  alias: string().optional(),
5295
5336
  access: _enum([
5296
5337
  "inherit",
@@ -5314,6 +5355,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5314
5355
  ordinal: number().int().positive(),
5315
5356
  id: string(),
5316
5357
  path: string(),
5358
+ aliasId: string().optional(),
5359
+ skillsEnabled: boolean().optional(),
5317
5360
  alias: string().optional(),
5318
5361
  addedAt: string(),
5319
5362
  state: _enum([
@@ -5424,6 +5467,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5424
5467
  state: value.state,
5425
5468
  access: value.access ?? "inherit",
5426
5469
  ...value.effectiveAccess === void 0 ? {} : { effectiveAccess: value.effectiveAccess },
5470
+ ...value.aliasId === void 0 ? {} : { aliasId: value.aliasId },
5471
+ ...value.skillsEnabled === void 0 ? {} : { skillsEnabled: value.skillsEnabled },
5427
5472
  ...value.alias === void 0 ? {} : { alias: value.alias },
5428
5473
  ...value.detail === void 0 ? {} : { detail: value.detail }
5429
5474
  };
@@ -5450,13 +5495,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5450
5495
  const NS$2 = "multiRootWorkspace";
5451
5496
  /** Chinese copy (the canonical shape). */
5452
5497
  const zh = {
5453
- "panel.mode": "当前会话权限:",
5454
- "panel.effectiveAccess": "当前有效:",
5498
+ "skills.globalEnable": "启用附属根 Skills",
5499
+ "skills.globalHint": "控制所有附属目录的 Skill 发现与新调用;保留每个目录的独立选择。",
5500
+ "skills.settingsFailed": "设置保存失败,请重试。",
5501
+ "skills.userOnly": "仅用户调用",
5502
+ "panel.skills": "目录技能",
5503
+ "panel.skillsEnabled": "此目录技能已启用,点击停用",
5504
+ "panel.skillsDisabled": "此目录技能已停用,点击启用",
5505
+ "panel.aliasIdEdit": "编辑 Skill 命名空间",
5506
+ "panel.aliasIdPlaceholder": "唯一的 kebab-case 标识",
5507
+ "panel.aliasIdInvalid": "命名空间需要修复",
5508
+ "error.invalid-alias-id": "Skill 命名空间须为唯一的 kebab-case,最长 48 字符。",
5509
+ "panel.accessEffective": "{access}({effective})",
5455
5510
  "panel.toolCaps": "工具 {tools} 对附属根始终只读。",
5456
5511
  "panel.invalidCap": "工具权限配置无效,附属根写入全部被拒绝。",
5457
- "mode.read-only": "只读",
5458
- "mode.workspace-write": "读写",
5459
- "mode.danger-full-access": "完全权限(Full-access)",
5460
5512
  "effective.read-only": "只读",
5461
5513
  "effective.read-write": "读写",
5462
5514
  "effective.full-access": "完全权限(Full-access)",
@@ -5465,11 +5517,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5465
5517
  "action.title": "管理本会话的附属工作目录",
5466
5518
  "switcher.manage": "管理工作区目录…",
5467
5519
  "panel.title": "工作区目录",
5468
- "panel.subtitle": "主目录与「跟随会话」的附加目录遵循当前沙箱模式;目录也可固定为只读或读写。",
5469
- "panel.primary": "主根",
5520
+ "panel.primary": "主要工作目录",
5470
5521
  "panel.primaryNote": "会话原始项目,不可移除",
5471
- "panel.cwd": "当前执行目录",
5472
- "panel.cwdNote": "切换目录不会增加授权",
5522
+ "panel.currentAgentCwd": "当前Agent工作目录",
5473
5523
  "panel.additional": "附属工作目录",
5474
5524
  "panel.limitReached": "已达到附加目录数量上限,请先移除一个再添加。",
5475
5525
  "panel.empty": "还没有附属工作目录。",
@@ -5495,12 +5545,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5495
5545
  "panel.moveUp": "上移",
5496
5546
  "panel.moveDown": "下移",
5497
5547
  "access.inherit": "跟随会话",
5498
- "access.readWrite": "固定读写",
5499
- "access.readOnly": "只读",
5548
+ "access.readWrite": "工作区内修改",
5549
+ "access.readOnly": "仅可查看",
5500
5550
  "panel.accessLabel": "访问权限",
5501
5551
  "panel.accessInherit": "设为跟随会话",
5502
- "panel.accessReadOnly": "设为只读",
5503
- "panel.accessReadWrite": "设为固定读写",
5552
+ "panel.accessReadOnly": "仅可查看",
5553
+ "panel.accessReadWrite": "工作区内修改",
5504
5554
  "panel.accessUpgradeConfirm": "将该目录的权限改为「{access}」?在某些沙箱模式下,这将允许 Agent 修改其中的文件。",
5505
5555
  "panel.accessUpgradeTitle": "扩大访问权限",
5506
5556
  "panel.accessUpgradeDo": "修改",
@@ -5532,13 +5582,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5532
5582
  };
5533
5583
  /** English copy, pinned to the Chinese key set. */
5534
5584
  const en = {
5535
- "panel.mode": "Current session access:",
5536
- "panel.effectiveAccess": "Effective:",
5585
+ "skills.globalEnable": "Enable additional-root Skills",
5586
+ "skills.globalHint": "Controls discovery and new invocations for all additional folders, preserving each folder’s choice.",
5587
+ "skills.settingsFailed": "Could not save settings. Please try again.",
5588
+ "skills.userOnly": "User invocation only",
5589
+ "panel.skills": "Folder skills",
5590
+ "panel.skillsEnabled": "Skills enabled for this folder. Click to disable.",
5591
+ "panel.skillsDisabled": "Skills disabled for this folder. Click to enable.",
5592
+ "panel.aliasIdEdit": "Edit Skill namespace",
5593
+ "panel.aliasIdPlaceholder": "Unique kebab-case identifier",
5594
+ "panel.aliasIdInvalid": "Namespace needs repair",
5595
+ "error.invalid-alias-id": "Skill namespace must be unique kebab-case, up to 48 characters.",
5596
+ "panel.accessEffective": "{access} ({effective})",
5537
5597
  "panel.toolCaps": "The tools {tools} always have read-only access to additional roots.",
5538
5598
  "panel.invalidCap": "Invalid tool access configuration refuses all additional-root writes.",
5539
- "mode.read-only": "Read-only",
5540
- "mode.workspace-write": "Read-write",
5541
- "mode.danger-full-access": "Full-access",
5542
5599
  "effective.read-only": "Read-only",
5543
5600
  "effective.read-write": "Read-write",
5544
5601
  "effective.full-access": "Full-access",
@@ -5547,11 +5604,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5547
5604
  "action.title": "Manage this session's secondary working directories",
5548
5605
  "switcher.manage": "Manage workspace folders…",
5549
5606
  "panel.title": "Workspace folders",
5550
- "panel.subtitle": "The primary root and inherit folders follow the current sandbox mode; a folder can also be pinned read-only or read-write.",
5551
- "panel.primary": "Primary root",
5607
+ "panel.primary": "Primary working directory",
5552
5608
  "panel.primaryNote": "The session's original project; it cannot be removed",
5553
- "panel.cwd": "Current working directory",
5554
- "panel.cwdNote": "Changing directory does not grant access",
5609
+ "panel.currentAgentCwd": "Current Agent working directory",
5555
5610
  "panel.additional": "Secondary working directories",
5556
5611
  "panel.limitReached": "The additional-folder limit is reached; remove one before adding another.",
5557
5612
  "panel.empty": "No secondary working directories yet.",
@@ -5577,12 +5632,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5577
5632
  "panel.moveUp": "Move up",
5578
5633
  "panel.moveDown": "Move down",
5579
5634
  "access.inherit": "Inherit",
5580
- "access.readWrite": "Read-write",
5581
- "access.readOnly": "Read-only",
5635
+ "access.readWrite": "Workspace Write",
5636
+ "access.readOnly": "Read Only",
5582
5637
  "panel.accessLabel": "Access",
5583
5638
  "panel.accessInherit": "Make inherit",
5584
- "panel.accessReadOnly": "Make read-only",
5585
- "panel.accessReadWrite": "Make read-write",
5639
+ "panel.accessReadOnly": "Read Only",
5640
+ "panel.accessReadWrite": "Workspace Write",
5586
5641
  "panel.accessUpgradeConfirm": "Change this folder's access to \"{access}\"? Under some sandbox modes this lets the agent modify files in it.",
5587
5642
  "panel.accessUpgradeTitle": "Widen access",
5588
5643
  "panel.accessUpgradeDo": "Change",
@@ -6193,13 +6248,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6193
6248
  padding: 0 24px 24px;
6194
6249
  }
6195
6250
 
6196
- .mrfw-description {
6197
- margin: 0 0 8px;
6198
- font-size: 14px;
6199
- line-height: 22px;
6200
- color: var(--dsw-alias-label-tertiary);
6201
- }
6202
-
6203
6251
  .mrfw-alert {
6204
6252
  margin: 0 0 8px;
6205
6253
  font-size: 14px;
@@ -6251,6 +6299,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6251
6299
 
6252
6300
  /* A root entry's two lines: the display name on top, the absolute path under
6253
6301
  it in the tertiary voice (the workspace rows recipe of the host sidebar). */
6302
+ /* Match upstream General Settings' DeveloperToolsRow layout. */
6303
+ .mrfw-skillsSettings {
6304
+ display: flex;
6305
+ align-items: center;
6306
+ justify-content: space-between;
6307
+ gap: 24px;
6308
+ padding: 16px 0;
6309
+ border-bottom: 0.5px solid var(--dsw-alias-border-l2);
6310
+ }
6311
+ .mrfw-skillsSettingsText { min-width: 0; }
6312
+ .mrfw-skillsSettingsTitle { font-size: 14px; line-height: 20px; }
6313
+ .mrfw-skillsSettingsDescription {
6314
+ margin-top: 4px;
6315
+ color: var(--dsw-alias-label-secondary);
6316
+ font-size: 12px;
6317
+ line-height: 18px;
6318
+ }
6254
6319
  .mrfw-rootText {
6255
6320
  display: flex;
6256
6321
  flex: 1 1 240px;
@@ -6259,15 +6324,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6259
6324
  min-width: 0;
6260
6325
  }
6261
6326
 
6262
- /* The name line is a flex row: the truncating name text first, the access
6263
- badge beside it never shrinking — baseline-aligned so the badge's smaller
6264
- voice sits on the name's baseline rather than its middle. */
6327
+ /* Keep status tags and permission glyphs visible beside the alias, wrapping
6328
+ onto another line when the dialog is narrow. */
6265
6329
  .mrfw-rootName {
6266
6330
  display: flex;
6267
- align-items: baseline;
6331
+ align-items: center;
6332
+ flex-wrap: wrap;
6268
6333
  gap: 6px;
6269
6334
  min-width: 0;
6270
- overflow: hidden;
6271
6335
  font-size: 14px;
6272
6336
  line-height: 20px;
6273
6337
  font-weight: 500;
@@ -6349,6 +6413,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6349
6413
  transition: background-color var(--ds-transition-duration-fast) var(--ds-ease-in-out);
6350
6414
  }
6351
6415
 
6416
+ .mrfw-btn[aria-busy='true'] {
6417
+ cursor: wait;
6418
+ }
6419
+
6352
6420
  .mrfw-btn:disabled {
6353
6421
  cursor: not-allowed;
6354
6422
  opacity: 0.4;
@@ -6442,8 +6510,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6442
6510
  padding: 0 12px;
6443
6511
  }
6444
6512
 
6445
- .mrfw-accessBadge,
6446
- .mrfw-effectiveAccess {
6513
+ .mrfw-cwdTag {
6514
+ flex: none;
6515
+ padding: 0 6px;
6516
+ border-radius: 4px;
6517
+ background: var(--dsw-alias-interactive-bg-hover);
6518
+ color: var(--dsw-alias-label-secondary);
6519
+ font-size: 11px;
6520
+ line-height: 18px;
6521
+ }
6522
+
6523
+ .mrfw-accessBadge {
6524
+ display: inline-flex;
6525
+ align-items: center;
6526
+ gap: 4px;
6527
+ }
6528
+
6529
+ .mrfw-accessBadge {
6447
6530
  flex: none;
6448
6531
  font-size: 12px;
6449
6532
  line-height: 18px;
@@ -6477,6 +6560,336 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6477
6560
  width: 32px;
6478
6561
  }
6479
6562
  `;
6563
+ //#endregion
6564
+ //#region src/compat/skills-client.ts
6565
+ function bindSkillsForm(ctx) {
6566
+ const modern = ctx.get("configForms");
6567
+ if (modern !== void 0) return modern.get("multi-root-skills");
6568
+ return ctx.get("settingsScope")?.bind({ namespace: "multi-root-skills" });
6569
+ }
6570
+ function clientInject(ctx, services, callback) {
6571
+ if (typeof ctx.inject === "function") ctx.inject(services, callback);
6572
+ }
6573
+ function registerNamespaceRule(ctx, rule) {
6574
+ const conversation = ctx.get("uiConversation");
6575
+ return typeof conversation?.userText?.register === "function" ? conversation.userText.register(rule) : void 0;
6576
+ }
6577
+ //#endregion
6578
+ //#region src/client/skills-client.ts
6579
+ /** Session-addressed Connection cache. Every refresh and change notification fences old replies. */
6580
+ var SkillsClient = class {
6581
+ connection;
6582
+ states = /* @__PURE__ */ new Map();
6583
+ closed = false;
6584
+ constructor(connection) {
6585
+ this.connection = connection;
6586
+ }
6587
+ state(id) {
6588
+ let state = this.states.get(id);
6589
+ if (state === void 0) {
6590
+ state = {
6591
+ epoch: 0,
6592
+ listeners: /* @__PURE__ */ new Set()
6593
+ };
6594
+ this.states.set(id, state);
6595
+ }
6596
+ return state;
6597
+ }
6598
+ async call(endpoint, payload, signal) {
6599
+ signal?.throwIfAborted();
6600
+ const result = await this.connection.rpc.call(PANEL_CHANNEL, endpoint, payload, signal);
6601
+ signal?.throwIfAborted();
6602
+ if (!result.ok) throw new Error(result.error?.message ?? "Skills request failed");
6603
+ return result.value;
6604
+ }
6605
+ async list(sessionId, signal) {
6606
+ if (this.closed) throw new Error("Skills client is closed");
6607
+ const state = this.state(sessionId);
6608
+ if (state.pending === void 0) {
6609
+ const epoch = ++state.epoch;
6610
+ state.request?.abort();
6611
+ const request = new AbortController();
6612
+ state.request = request;
6613
+ const pending = (async () => {
6614
+ const value = skillsCatalogSchema.parse(await this.call("skillsList", { sessionId }, request.signal));
6615
+ if (this.closed || value.sessionId !== sessionId || epoch !== state.epoch) throw new Error("Stale Skills response");
6616
+ const changed = JSON.stringify(state.value) !== JSON.stringify(value);
6617
+ state.value = value;
6618
+ if (changed) for (const listener of state.listeners) listener();
6619
+ return value;
6620
+ })();
6621
+ state.pending = pending;
6622
+ pending.then(() => {
6623
+ if (state.pending === pending) delete state.pending;
6624
+ }, () => {
6625
+ if (state.pending === pending) delete state.pending;
6626
+ });
6627
+ }
6628
+ return await withAbort(state.pending, signal);
6629
+ }
6630
+ snapshot(sessionId) {
6631
+ return this.states.get(sessionId)?.value;
6632
+ }
6633
+ async preview(sessionId, name, signal) {
6634
+ const state = this.state(sessionId), epoch = state.epoch;
6635
+ const value = skillsPreviewSchema.parse(await this.call("skillsPreview", {
6636
+ sessionId,
6637
+ name
6638
+ }, signal));
6639
+ if (this.closed || epoch !== state.epoch || value.sessionId !== sessionId || value.name !== name) throw new Error("Stale Skills preview");
6640
+ return value;
6641
+ }
6642
+ subscribe(sessionId, listener) {
6643
+ const state = this.state(sessionId);
6644
+ state.listeners.add(listener);
6645
+ if (state.watching === void 0) {
6646
+ const lifetime = new AbortController();
6647
+ state.watching = lifetime;
6648
+ this.watch(sessionId, state, lifetime.signal);
6649
+ }
6650
+ return () => {
6651
+ state.listeners.delete(listener);
6652
+ if (state.listeners.size === 0) {
6653
+ state.watching?.abort();
6654
+ delete state.watching;
6655
+ state.request?.abort();
6656
+ this.states.delete(sessionId);
6657
+ }
6658
+ };
6659
+ }
6660
+ invalidate(state) {
6661
+ state.epoch++;
6662
+ state.request?.abort();
6663
+ delete state.pending;
6664
+ delete state.value;
6665
+ for (const listener of state.listeners) listener();
6666
+ }
6667
+ reset() {
6668
+ for (const state of this.states.values()) this.invalidate(state);
6669
+ }
6670
+ dispose() {
6671
+ this.closed = true;
6672
+ for (const state of this.states.values()) {
6673
+ this.invalidate(state);
6674
+ state.watching?.abort();
6675
+ }
6676
+ this.states.clear();
6677
+ }
6678
+ async watch(id, state, signal) {
6679
+ while (!signal.aborted && !this.closed) try {
6680
+ const current = state.value ?? await this.list(id, signal);
6681
+ const next = skillsStatusSchema.parse(await this.call("skillsChanges", {
6682
+ sessionId: id,
6683
+ generation: current.generation
6684
+ }, signal));
6685
+ if (next.sessionId !== id) throw new Error("Wrong Skills Session");
6686
+ if (next.generation !== current.generation) this.invalidate(state);
6687
+ } catch {
6688
+ if (signal.aborted || this.closed) return;
6689
+ this.invalidate(state);
6690
+ await new Promise((resolve) => {
6691
+ const finish = () => {
6692
+ clearTimeout(timer);
6693
+ signal.removeEventListener("abort", finish);
6694
+ resolve();
6695
+ };
6696
+ const timer = setTimeout(finish, 1e3);
6697
+ signal.addEventListener("abort", finish, { once: true });
6698
+ });
6699
+ }
6700
+ }
6701
+ };
6702
+ async function withAbort(promise, signal) {
6703
+ signal?.throwIfAborted();
6704
+ if (signal === void 0) return await promise;
6705
+ return await new Promise((resolve, reject) => {
6706
+ const abort = () => reject(signal.reason);
6707
+ signal.addEventListener("abort", abort, { once: true });
6708
+ promise.then((value) => {
6709
+ signal.removeEventListener("abort", abort);
6710
+ if (!signal.aborted) resolve(value);
6711
+ }, (error) => {
6712
+ signal.removeEventListener("abort", abort);
6713
+ reject(error);
6714
+ });
6715
+ });
6716
+ }
6717
+ //#endregion
6718
+ //#region src/client/skills.tsx
6719
+ const namespaceSkillRule = {
6720
+ id: "multi-root-skill",
6721
+ order: 19,
6722
+ match(text, from) {
6723
+ const regex = /(?<!\S)\/([a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*)(?!\S)/g;
6724
+ regex.lastIndex = from;
6725
+ const match = regex.exec(text);
6726
+ return match === null ? void 0 : {
6727
+ id: "multi-root-skill",
6728
+ start: match.index,
6729
+ end: match.index + match[0].length,
6730
+ data: { name: match[1] }
6731
+ };
6732
+ }
6733
+ };
6734
+ function namespaceSource(client, t) {
6735
+ return {
6736
+ trigger: "/",
6737
+ name: "multi-root-skills",
6738
+ order: 3,
6739
+ async candidates({ sessionId }, { query, signal }) {
6740
+ try {
6741
+ const view = client.snapshot(sessionId) ?? await client.list(sessionId, signal);
6742
+ signal.throwIfAborted();
6743
+ return view.skills.filter((skill) => skill.name.toLowerCase().includes(query.toLowerCase())).map((skill) => ({
6744
+ name: skill.name,
6745
+ description: skill.modelInvocable ? skill.description : `${t("skills.userOnly")} · ${skill.description}`
6746
+ }));
6747
+ } catch (error) {
6748
+ if (signal.aborted) return [];
6749
+ throw error;
6750
+ }
6751
+ },
6752
+ onPick({ candidate }) {
6753
+ return { text: `/${candidate.name} ` };
6754
+ },
6755
+ lexicon({ sessionId }) {
6756
+ return client.snapshot(sessionId)?.skills.map((skill) => skill.name);
6757
+ },
6758
+ subscribeLexicon({ sessionId }, listener) {
6759
+ return client.subscribe(sessionId, listener);
6760
+ }
6761
+ };
6762
+ }
6763
+ function installSkillsClient(ctx, t, subscribeSession) {
6764
+ const connection = ctx.get("connection");
6765
+ if (connection === void 0) return;
6766
+ const client = new SkillsClient(connection);
6767
+ ctx.effect(() => () => client.dispose());
6768
+ ctx.effect(() => subscribeSession(() => {
6769
+ client.reset();
6770
+ }));
6771
+ const source = namespaceSource(client, t);
6772
+ clientInject(ctx, ["inputTriggers"], (scope) => {
6773
+ return scope.get("inputTriggers").registerSource(source);
6774
+ });
6775
+ clientInject(ctx, ["uiConversation"], (scope) => {
6776
+ const stop = registerNamespaceRule(scope, namespaceSkillRule);
6777
+ if (stop === void 0) return;
6778
+ const slots = scope.slots;
6779
+ const stopRenderer = slots.inject("conversation.user.text.node", () => slots.register({
6780
+ name: "conversation.user.text.node",
6781
+ key: namespaceSkillRule.id,
6782
+ priority: 100
6783
+ }, ({ node }) => `/${node.data.name}`));
6784
+ return () => {
6785
+ stopRenderer();
6786
+ stop();
6787
+ };
6788
+ });
6789
+ if (typeof ctx.on === "function") ctx.on("connection/reset", () => {
6790
+ client.reset();
6791
+ });
6792
+ }
6793
+ //#endregion
6794
+ //#region src/client/skills-settings.tsx
6795
+ const BUNDLE_KEY = "@dsh-electron/dsh-plugin-multi-root-workspace";
6796
+ function SkillsSettingsCard({ form, t }) {
6797
+ const snapshot = (0, react.useSyncExternalStore)((listener) => form.subscribe(listener), () => form.getSnapshot());
6798
+ const [busy, setBusy] = (0, react.useState)(false), [error, setError] = (0, react.useState)();
6799
+ const mounted = (0, react.useRef)(true);
6800
+ (0, react.useEffect)(() => {
6801
+ mounted.current = true;
6802
+ return () => {
6803
+ mounted.current = false;
6804
+ };
6805
+ }, []);
6806
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6807
+ className: "mrfw-skillsSettings",
6808
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6809
+ className: "mrfw-skillsSettingsText",
6810
+ children: [
6811
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6812
+ className: "mrfw-skillsSettingsTitle",
6813
+ children: t("skills.globalEnable")
6814
+ }),
6815
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6816
+ className: "mrfw-skillsSettingsDescription",
6817
+ children: t("skills.globalHint")
6818
+ }),
6819
+ error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6820
+ role: "alert",
6821
+ children: error
6822
+ }) : null
6823
+ ]
6824
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
6825
+ label: t("skills.globalEnable"),
6826
+ checked: snapshot.value?.enabled ?? true,
6827
+ disabled: busy || snapshot.status !== "ready" || !snapshot.writable,
6828
+ onChange: (enabled) => {
6829
+ setBusy(true);
6830
+ setError(void 0);
6831
+ form.mutate([{
6832
+ op: "set",
6833
+ path: ["enabled"],
6834
+ value: enabled
6835
+ }], snapshot.revision).then((ok) => {
6836
+ if (mounted.current && !ok) setError(t("skills.settingsFailed"));
6837
+ }).catch(() => {
6838
+ if (mounted.current) setError(t("skills.settingsFailed"));
6839
+ }).finally(() => {
6840
+ if (mounted.current) setBusy(false);
6841
+ });
6842
+ }
6843
+ })]
6844
+ });
6845
+ }
6846
+ function installSkillsSettingsCard(ctx, t) {
6847
+ let current;
6848
+ let stop;
6849
+ const candidates = /* @__PURE__ */ new Map();
6850
+ const update = () => {
6851
+ const chosen = [...candidates.entries()].sort(([a], [b]) => Number(b.get("configForms") !== void 0) - Number(a.get("configForms") !== void 0))[0];
6852
+ if (chosen?.[1] === current) return;
6853
+ stop?.();
6854
+ stop = void 0;
6855
+ current = chosen?.[1];
6856
+ if (!chosen) return;
6857
+ const [scope, form] = chosen;
6858
+ const slots = scope.slots;
6859
+ const disposers = [slots.inject("settings.plugin.item", () => slots.register({
6860
+ name: "settings.plugin.item",
6861
+ key: "multi-root-skills",
6862
+ inject: () => ({
6863
+ form,
6864
+ t
6865
+ })
6866
+ }, SkillsSettingsCard)), slots.inject("plugins.bundle.config", () => slots.register({
6867
+ name: "plugins.bundle.config",
6868
+ key: BUNDLE_KEY,
6869
+ inject: () => ({
6870
+ form,
6871
+ t
6872
+ })
6873
+ }, SkillsSettingsCard))];
6874
+ stop = () => {
6875
+ for (const dispose of disposers) dispose();
6876
+ };
6877
+ };
6878
+ for (const service of ["settingsScope", "configForms"]) clientInject(ctx, [service], (scope) => {
6879
+ const form = bindSkillsForm(scope);
6880
+ if (form === void 0) return;
6881
+ candidates.set(scope, form);
6882
+ update();
6883
+ return () => {
6884
+ candidates.delete(scope);
6885
+ update();
6886
+ };
6887
+ });
6888
+ ctx.effect(() => () => {
6889
+ stop?.();
6890
+ candidates.clear();
6891
+ });
6892
+ }
6480
6893
  //#endregion
6481
6894
  //#region src/compat/client-icons.ts
6482
6895
  /**
@@ -6536,11 +6949,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6536
6949
  const IconRefresh = icon("IconRefreshOutline16", "IconRefreshOutlineRegular");
6537
6950
  const IconSettings = icon("IconSettingsOutline16", "IconSettingsOutlineRegular");
6538
6951
  const IconTrash = icon("IconTrashOutline16", "IconTrashOutlineRegular");
6539
- const IconAccessReadWrite = maybeIcon(["IconWorkspaceWriteRegular"]);
6540
- const IconAccessReadOnly = maybeIcon(["IconReadOnlyRegular"]);
6952
+ const IconAccessReadWrite = maybeIcon(["PermissionIconWorkspaceWriteRegular", "IconWorkspaceWriteRegular"]);
6953
+ const IconAccessReadOnly = maybeIcon(["PermissionIconReadOnlyRegular", "IconReadOnlyRegular"]);
6541
6954
  const IconAccessInherit = maybeIcon(["IconWorkspaceTreeOutline16", "IconWorkspaceTreeOutlineRegular"]);
6542
6955
  //#endregion
6543
- //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
6956
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-typert-protocol@0.2.1-rc.3_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-typert-protocol/lib/index.js
6544
6957
  /** The one Remote failure class shared by owners, the Gateway, and consumers. */
6545
6958
  /**
6546
6959
  * One Remote call failure: a real Error carrying its stable code and typed
@@ -7050,7 +7463,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7050
7463
  return n;
7051
7464
  }
7052
7465
  //#endregion
7053
- //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
7466
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.2.1-rc.3_@deepseek-ai+cordis@4.0.5/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
7054
7467
  /**
7055
7468
  * The `dsh-resource://file/…` address grammar: how a file is named across the
7056
7469
  * Sidebar and the resource model, built and parsed without touching a
@@ -7472,7 +7885,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7472
7885
  };
7473
7886
  }
7474
7887
  //#endregion
7475
- //#region node_modules/.pnpm/@deepseek-ai+dsh-host-open-in-app@0.2.1-rc.2_@deepseek-ai+cordis@4.0.5_@deepseek-ai+dsh_90dfa79a7f5aae0c8b5a66b41d5c318b/node_modules/@deepseek-ai/dsh-host-open-in-app/lib/types/shared.js
7888
+ //#region node_modules/.pnpm/@deepseek-ai+dsh-host-open-in-app@0.2.1-rc.3_@deepseek-ai+cordis@4.0.5_@deepseek-ai+dsh_5b371ee77d0b761177ea40ae8a5dc767/node_modules/@deepseek-ai/dsh-host-open-in-app/lib/types/shared.js
7476
7889
  var shared_exports = /* @__PURE__ */ __exportAll({
7477
7890
  OPEN_IN_APP_APPS_PATH: () => OPEN_IN_APP_APPS_PATH,
7478
7891
  OPEN_IN_APP_APPS_ROUTE: () => OPEN_IN_APP_APPS_ROUTE,
@@ -8039,7 +8452,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8039
8452
  * The Workspace Folders panel: one sidebar footer action that opens a dialog
8040
8453
  * listing this session's workspace root and the additional roots registered for
8041
8454
  * it, with the actions the milestone requires — Add (through the composed
8042
- * directory picker), Remove, Alias, Copy Path, Reveal, and reordering.
8455
+ * directory picker), Remove, Alias, Copy Path, and Reveal.
8043
8456
  *
8044
8457
  * Why a footer action and its own dialog, instead of a first-class panel: the
8045
8458
  * slots that can host a full panel (`sidebar.panellist` + the keyed `main`
@@ -8065,14 +8478,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8065
8478
  primary: "mrfw-btnPrimary",
8066
8479
  danger: "mrfw-btnDanger"
8067
8480
  };
8068
- /** One small capsule button matching the panel's own styling. */
8481
+ /** One small capsule button matching the panel's own styling.
8482
+ * Pending requests block activation through aria-disabled without changing
8483
+ * opacity, hover or keyboard focus. Native disabled is for unavailable actions.
8484
+ */
8069
8485
  function Action(props) {
8070
8486
  const variant = props.variant ?? "ghost";
8071
8487
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8072
8488
  type: "button",
8073
8489
  className: `mrfw-btn ${VARIANT_CLASS[variant]}`,
8074
- onClick: props.onClick,
8490
+ onClick: props.pending === true ? void 0 : props.onClick,
8075
8491
  disabled: props.disabled === true,
8492
+ "aria-disabled": props.disabled === true || props.pending === true || void 0,
8493
+ "aria-busy": props.pending === true || void 0,
8076
8494
  children: props.children
8077
8495
  });
8078
8496
  }
@@ -8085,13 +8503,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8085
8503
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
8086
8504
  type: "button",
8087
8505
  className: `mrfw-btn mrfw-iconBtn ${VARIANT_CLASS[variant]}`,
8088
- title: props.label,
8506
+ title: props.hint ?? props.label,
8089
8507
  "aria-label": props.label,
8090
- onClick: props.onClick,
8508
+ "aria-pressed": props.pressed,
8509
+ onClick: props.pending === true ? void 0 : props.onClick,
8091
8510
  disabled: props.disabled === true,
8511
+ "aria-disabled": props.disabled === true || props.pending === true || void 0,
8512
+ "aria-busy": props.pending === true || void 0,
8092
8513
  children: props.icon
8093
8514
  });
8094
8515
  }
8516
+ /** A skill is a reusable instruction document; the star denotes its capability. */
8517
+ function SkillsIcon({ enabled }) {
8518
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
8519
+ width: "16",
8520
+ height: "16",
8521
+ viewBox: "0 0 16 16",
8522
+ fill: "none",
8523
+ stroke: "currentColor",
8524
+ strokeWidth: "1",
8525
+ strokeLinecap: "round",
8526
+ strokeLinejoin: "round",
8527
+ "aria-hidden": "true",
8528
+ children: [
8529
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 13.5H3a1 1 0 0 1-1-1v-10a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1V7M4.5 4.5h5M4.5 7h3" }),
8530
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m11.5 8 .85 2.65L15 11.5l-2.65.85-.85 2.65-.85-2.65L8 11.5l2.65-.85Z" }),
8531
+ enabled ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m2 14 12-12" })
8532
+ ]
8533
+ });
8534
+ }
8095
8535
  /** The 16px close glyph of the dialog's header button. */
8096
8536
  function CloseIcon() {
8097
8537
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -8111,7 +8551,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8111
8551
  * and reveals a trailing icon on hover — clicking the text or the icon
8112
8552
  * copies the same canonical path. The accessible name swaps to "copied"
8113
8553
  * for the feedback window, the icon button's old recipe. Shared by the
8114
- * primary row, the current-directory line, and every additional root.
8554
+ * primary row and every additional root.
8115
8555
  */
8116
8556
  function PathCopy(props) {
8117
8557
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -8167,6 +8607,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8167
8607
  function displayNameOf(root) {
8168
8608
  return root.alias ?? basename(root.path);
8169
8609
  }
8610
+ /** Display-only path matching; the host remains the sole authority for grants. */
8611
+ function workingRootOf(view) {
8612
+ if (view?.effectiveCwd === void 0) return void 0;
8613
+ const normalize = (path) => {
8614
+ return (/^[a-z]:[\\/]/i.test(path) || path.startsWith("\\") ? path.replace(/\\/g, "/").toLowerCase() : path).replace(/\/+$/, "") || "/";
8615
+ };
8616
+ const cwd = normalize(view.effectiveCwd);
8617
+ return [view.primaryRoot, ...view.roots.map((root) => root.path)].filter((path) => {
8618
+ const root = normalize(path);
8619
+ return cwd === root || cwd.startsWith(root.endsWith("/") ? root : `${root}/`);
8620
+ }).sort((a, b) => normalize(b).length - normalize(a).length)[0];
8621
+ }
8170
8622
  /** The sidebar footer action plus the dialog it opens. */
8171
8623
  function WorkspaceFoldersAction(props) {
8172
8624
  const [open, setOpen] = (0, react.useState)(false);
@@ -8243,6 +8695,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8243
8695
  const [upgradeFor, setUpgradeFor] = (0, react.useState)(void 0);
8244
8696
  const [aliasFor, setAliasFor] = (0, react.useState)(void 0);
8245
8697
  const [aliasDraft, setAliasDraft] = (0, react.useState)("");
8698
+ const [aliasField, setAliasField] = (0, react.useState)("alias");
8246
8699
  const [copiedId, setCopiedId] = (0, react.useState)(void 0);
8247
8700
  const [menuFor, setMenuFor] = (0, react.useState)(void 0);
8248
8701
  const dialogRef = (0, react.useRef)(null);
@@ -8521,7 +8974,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8521
8974
  }));
8522
8975
  }
8523
8976
  }, []);
8524
- const move = (0, react.useCallback)(async (root, before) => {
8977
+ (0, react.useCallback)(async (root, before) => {
8525
8978
  await mutate("move", before === void 0 ? { entry: entryOf(root) } : {
8526
8979
  entry: entryOf(root),
8527
8980
  beforeEntry: entryOf(before)
@@ -8531,7 +8984,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8531
8984
  const atLimit = state.view !== void 0 && state.view.count >= state.view.limit;
8532
8985
  const IconAccessTrigger = addAccess === "read-only" ? IconAccessReadOnly : addAccess === "read-write" ? IconAccessReadWrite : IconAccessInherit;
8533
8986
  const primaryPath = state.view?.primaryRoot;
8534
- const cwdPath = state.view?.effectiveCwd;
8987
+ const workingRoot = workingRootOf(state.view);
8535
8988
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8536
8989
  className: "mrfw-overlay",
8537
8990
  role: "presentation",
@@ -8568,18 +9021,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8568
9021
  className: "mrfw-note",
8569
9022
  children: t("panel.noSession")
8570
9023
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
8571
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8572
- className: "mrfw-description",
8573
- children: t("panel.subtitle")
8574
- }),
8575
- state.view?.mode === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
8576
- className: "mrfw-note",
8577
- children: [
8578
- t("panel.mode"),
8579
- " ",
8580
- t(`mode.${state.view.mode}`)
8581
- ]
8582
- }),
8583
9024
  state.view?.toolAccessInvalid ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
8584
9025
  className: "mrfw-note",
8585
9026
  children: t("panel.invalidCap")
@@ -8612,12 +9053,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8612
9053
  className: "mrfw-row mrfw-rowBare",
8613
9054
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8614
9055
  className: "mrfw-rootText",
8615
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9056
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8616
9057
  className: "mrfw-rootName",
8617
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9058
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8618
9059
  className: "mrfw-rootNameText",
8619
9060
  children: state.view === void 0 ? "…" : state.view.primaryName ?? basename(state.view.primaryRoot)
8620
- })
9061
+ }), primaryPath !== void 0 && workingRoot === primaryPath ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9062
+ className: "mrfw-cwdTag",
9063
+ children: t("panel.currentAgentCwd")
9064
+ }) : null]
8621
9065
  }), primaryPath === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8622
9066
  className: "mrfw-rootPath",
8623
9067
  children: "…"
@@ -8632,26 +9076,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8632
9076
  })
8633
9077
  })]
8634
9078
  }),
8635
- cwdPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8636
- className: "mrfw-section",
8637
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8638
- className: "mrfw-sectionTitleRow",
8639
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8640
- className: "mrfw-sectionTitle",
8641
- children: t("panel.cwd")
8642
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
8643
- className: "mrfw-sectionNote",
8644
- children: t("panel.cwdNote")
8645
- })]
8646
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathCopy, {
8647
- path: cwdPath,
8648
- copied: copiedId === "cwd",
8649
- onCopy: () => {
8650
- copyPath("cwd", cwdPath);
8651
- },
8652
- t
8653
- })]
8654
- }),
8655
9079
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
8656
9080
  className: "mrfw-section",
8657
9081
  children: [
@@ -8674,8 +9098,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8674
9098
  t("panel.emptyHint")
8675
9099
  ]
8676
9100
  }) : null,
8677
- roots.map((root, index) => {
9101
+ roots.map((root) => {
8678
9102
  const key = rowKey(root);
9103
+ const IconAccess = root.access === "read-only" ? IconAccessReadOnly : root.access === "read-write" ? IconAccessReadWrite : IconAccessInherit;
8679
9104
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8680
9105
  className: "mrfw-row",
8681
9106
  children: [
@@ -8688,17 +9113,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8688
9113
  className: "mrfw-rootNameText",
8689
9114
  children: displayNameOf(root)
8690
9115
  }),
8691
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9116
+ workingRoot === root.path ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9117
+ className: "mrfw-cwdTag",
9118
+ children: t("panel.currentAgentCwd")
9119
+ }) : null,
9120
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8692
9121
  className: "mrfw-accessBadge",
8693
- children: t(accessKey(root.access))
8694
- }),
8695
- root.effectiveAccess === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
8696
- className: "mrfw-effectiveAccess",
8697
- children: [
8698
- t("panel.effectiveAccess"),
8699
- " ",
8700
- t(`effective.${root.effectiveAccess}`)
8701
- ]
9122
+ children: [IconAccess === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccess, { size: 14 }), root.effectiveAccess === void 0 ? t(accessKey(root.access)) : t("panel.accessEffective", {
9123
+ access: t(accessKey(root.access)),
9124
+ effective: t(`effective.${root.effectiveAccess}`)
9125
+ })]
8702
9126
  })
8703
9127
  ]
8704
9128
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PathCopy, {
@@ -8716,36 +9140,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8716
9140
  children: t(stateKey(root.state))
8717
9141
  }),
8718
9142
  root.state === "available" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8719
- disabled: state.busy,
9143
+ pending: state.busy,
8720
9144
  onClick: () => {
8721
9145
  reRegister(root);
8722
9146
  },
8723
9147
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconRefresh, {}),
8724
9148
  label: t("panel.reRegister")
8725
9149
  }),
8726
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8727
- disabled: state.busy,
8728
- onClick: () => {
8729
- reveal(root);
8730
- },
8731
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpen, {}),
8732
- label: t("panel.reveal")
8733
- }),
8734
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8735
- disabled: state.busy || index === 0,
9150
+ root.skillsEnabled === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
9151
+ pending: state.busy,
9152
+ pressed: root.skillsEnabled,
8736
9153
  onClick: () => {
8737
- move(root, roots[index - 1]);
9154
+ mutate("setSkillsEnabled", {
9155
+ entry: entryOf(root),
9156
+ skillsEnabled: !root.skillsEnabled
9157
+ });
8738
9158
  },
8739
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronUp, {}),
8740
- label: t("panel.moveUp")
9159
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillsIcon, { enabled: root.skillsEnabled }),
9160
+ label: t("panel.skills"),
9161
+ hint: t(root.skillsEnabled ? "panel.skillsEnabled" : "panel.skillsDisabled")
8741
9162
  }),
8742
9163
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8743
- disabled: state.busy || index === roots.length - 1,
9164
+ pending: state.busy,
8744
9165
  onClick: () => {
8745
- move(root, roots[index + 2]);
9166
+ reveal(root);
8746
9167
  },
8747
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDown, {}),
8748
- label: t("panel.moveDown")
9168
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconFolderOpen, {}),
9169
+ label: t("panel.reveal")
8749
9170
  }),
8750
9171
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Menu, {
8751
9172
  open: menuFor === key,
@@ -8758,19 +9179,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8758
9179
  label: t("panel.rename"),
8759
9180
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconEdit, {})
8760
9181
  },
9182
+ {
9183
+ id: "alias-id",
9184
+ label: t("panel.aliasIdEdit"),
9185
+ disabled: state.busy
9186
+ },
8761
9187
  {
8762
9188
  id: "access:inherit",
8763
9189
  label: t("panel.accessInherit"),
9190
+ icon: IconAccessInherit === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessInherit, {}),
8764
9191
  disabled: state.busy || root.access === "inherit"
8765
9192
  },
8766
9193
  {
8767
9194
  id: "access:read-only",
8768
9195
  label: t("panel.accessReadOnly"),
9196
+ icon: IconAccessReadOnly === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessReadOnly, {}),
8769
9197
  disabled: state.busy || root.access === "read-only"
8770
9198
  },
8771
9199
  {
8772
9200
  id: "access:read-write",
8773
9201
  label: t("panel.accessReadWrite"),
9202
+ icon: IconAccessReadWrite === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessReadWrite, {}),
8774
9203
  disabled: state.busy || root.access === "read-write"
8775
9204
  },
8776
9205
  {
@@ -8783,7 +9212,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8783
9212
  ],
8784
9213
  onSelect: (id) => {
8785
9214
  setMenuFor(void 0);
9215
+ if (id === "alias-id") {
9216
+ setAliasField("aliasId");
9217
+ setAliasFor(key);
9218
+ setAliasDraft(root.aliasId ?? "");
9219
+ }
8786
9220
  if (id === "rename") {
9221
+ setAliasField("alias");
8787
9222
  setAliasFor(key);
8788
9223
  setAliasDraft(root.alias ?? "");
8789
9224
  }
@@ -8807,7 +9242,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8807
9242
  align: "end",
8808
9243
  closeOnPointerLeave: true,
8809
9244
  anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8810
- disabled: state.busy,
9245
+ pending: state.busy,
8811
9246
  onClick: () => {
8812
9247
  setMenuFor((current) => current === key ? void 0 : key);
8813
9248
  },
@@ -8822,19 +9257,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8822
9257
  autoFocus: true,
8823
9258
  className: "mrfw-input mrfw-inputInline",
8824
9259
  value: aliasDraft,
8825
- placeholder: t("panel.aliasPlaceholder"),
9260
+ placeholder: t(aliasField === "aliasId" ? "panel.aliasIdPlaceholder" : "panel.aliasPlaceholder"),
8826
9261
  onChange: (event) => {
8827
9262
  setAliasDraft(event.target.value);
8828
9263
  }
8829
9264
  }),
8830
9265
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
8831
- disabled: state.busy,
9266
+ pending: state.busy,
8832
9267
  onClick: () => {
8833
- mutate("alias", {
9268
+ mutate(aliasField === "aliasId" ? "setAliasId" : "alias", {
8834
9269
  entry: entryOf(root),
8835
- alias: aliasDraft
9270
+ [aliasField]: aliasDraft
9271
+ }).then((ok) => {
9272
+ if (ok) setAliasFor(void 0);
8836
9273
  });
8837
- setAliasFor(void 0);
8838
9274
  },
8839
9275
  children: t("panel.aliasSave")
8840
9276
  }),
@@ -8856,7 +9292,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8856
9292
  children: [
8857
9293
  state.noSession || props.pickDirectory === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconButton, {
8858
9294
  variant: "outline",
8859
- disabled: state.busy || atLimit,
9295
+ pending: state.busy,
9296
+ disabled: atLimit,
8860
9297
  onClick: () => {
8861
9298
  addViaPicker();
8862
9299
  },
@@ -8918,9 +9355,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8918
9355
  "aria-label": t("panel.accessLabel"),
8919
9356
  "aria-haspopup": "menu",
8920
9357
  "aria-expanded": accessMenuOpen,
8921
- disabled: state.busy || atLimit,
9358
+ disabled: atLimit,
9359
+ "aria-disabled": state.busy || atLimit || void 0,
9360
+ "aria-busy": state.busy || void 0,
8922
9361
  onClick: () => {
8923
- setAccessMenuOpen((open) => !open);
9362
+ if (!state.busy) setAccessMenuOpen((open) => !open);
8924
9363
  },
8925
9364
  children: [
8926
9365
  IconAccessTrigger === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconAccessTrigger, {}),
@@ -8933,7 +9372,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8933
9372
  onClick: () => {
8934
9373
  addManualPath();
8935
9374
  },
8936
- disabled: state.busy || atLimit || manualPath.trim() === "",
9375
+ pending: state.busy,
9376
+ disabled: atLimit || manualPath.trim() === "",
8937
9377
  variant: "primary",
8938
9378
  children: t("panel.addConfirm")
8939
9379
  })
@@ -8946,7 +9386,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8946
9386
  onClick: () => {
8947
9387
  refresh();
8948
9388
  },
8949
- disabled: state.busy,
9389
+ pending: state.busy,
8950
9390
  variant: "outline",
8951
9391
  children: t("panel.retry")
8952
9392
  })
@@ -8969,7 +9409,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8969
9409
  children: t("panel.aliasCancel")
8970
9410
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Action, {
8971
9411
  variant: "danger",
8972
- disabled: state.busy,
9412
+ pending: state.busy,
8973
9413
  onClick: () => {
8974
9414
  const pending = upgradeFor;
8975
9415
  setUpgradeFor(void 0);
@@ -9301,6 +9741,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9301
9741
  const sessionId = () => currentSessionIdOf(readService(ctx, "sessions"));
9302
9742
  const subscribeSession = (listener) => subscribeSessions(readService(ctx, "sessions"), listener);
9303
9743
  const t = ctx.locale.bind(NS$2);
9744
+ installSkillsClient(ctx, t, (listener) => subscribeCurrentSession(readService(ctx, "sessions"), listener));
9745
+ installSkillsSettingsCard(ctx, t);
9304
9746
  const openInApp = createOpenInAppAdapter({
9305
9747
  t: ctx.locale.bind(NS),
9306
9748
  shortcuts: readService(ctx, "shortcuts")?.catalog