weir-harness 1.2.3 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/cordis.patch.yml +7 -18
  2. package/lib/client.capability-badge.js +2 -0
  3. package/lib/client.capability-manager.js +46 -179
  4. package/lib/client.capability-model.js +428 -3
  5. package/lib/client.file-lease-badge.js +307 -0
  6. package/lib/client.file-lease-tool-model.js +189 -0
  7. package/lib/client.file-lease-tool-view.js +434 -0
  8. package/lib/client.js +124 -36
  9. package/lib/client.mcp-manager.js +563 -0
  10. package/lib/client.settings-page.js +10 -34
  11. package/package.json +2 -2
  12. package/skills/mcp-server-setup/SKILL.md +124 -0
  13. package/src/capabilities/apply-engine.js +18 -4
  14. package/src/capabilities/capability-remote.js +296 -45
  15. package/src/capabilities/initial-selection.js +3 -2
  16. package/src/capabilities/lifecycle-preload.js +69 -11
  17. package/src/capabilities/lifecycle-snapshot.js +127 -39
  18. package/src/capabilities/mcp-admission.js +2 -2
  19. package/src/capabilities/mcp-config.js +344 -0
  20. package/src/capabilities/mcp-credentials.js +180 -0
  21. package/src/capabilities/mcp-diagnostics.js +169 -0
  22. package/src/capabilities/mcp-drain.js +18 -4
  23. package/src/capabilities/mcp-facade.js +3 -3
  24. package/src/capabilities/mcp-gate.js +51 -52
  25. package/src/capabilities/mcp-install-tool.js +276 -0
  26. package/src/capabilities/mcp-manager.js +368 -142
  27. package/src/capabilities/mcp-mount.js +219 -79
  28. package/src/capabilities/mcp-operations.js +359 -0
  29. package/src/capabilities/mcp-process.js +43 -0
  30. package/src/capabilities/mcp-registry.js +74 -60
  31. package/src/capabilities/mcp-runtime.js +103 -0
  32. package/src/capabilities/read-payloads.js +8 -3
  33. package/src/capabilities/skill-applicability.js +185 -0
  34. package/src/capabilities/skill-builtin-migration.js +2 -1
  35. package/src/capabilities/skill-selection-plugin.js +161 -56
  36. package/src/capabilities/store/checked-contract.d.ts +2 -2
  37. package/src/capabilities/store/fs-adapter.js +2 -3
  38. package/src/context-guard/index.js +2 -2
  39. package/src/delegate/supervision-mount.js +5 -5
  40. package/src/delegate/tool.js +2 -10
  41. package/src/file-lease/client.js +1299 -0
  42. package/src/file-lease/index.js +34 -0
  43. package/src/file-lease/lineage.js +207 -0
  44. package/src/file-lease/node-contracts.d.ts +42 -0
  45. package/src/file-lease/plugin.js +1013 -0
  46. package/src/file-lease/singleton.js +155 -0
  47. package/src/file-lease/spawn.js +62 -0
  48. package/src/file-lease/tools.js +847 -0
  49. package/src/file-lease/view.js +144 -0
  50. package/src/file-lease/watch.js +473 -0
  51. package/src/hashline-edit/index.js +76 -42
  52. package/src/hashline-edit/write-tool.js +90 -0
  53. package/src/intent-gate/index.js +29 -12
  54. package/src/intent-gate/matcher.js +36 -2
  55. package/src/lease-broker/entry.js +98 -0
  56. package/src/lease-broker/node-contracts.d.ts +63 -0
  57. package/src/lease-broker/protocol.js +237 -0
  58. package/src/lease-broker/server.js +402 -0
  59. package/src/lease-broker/state.js +482 -0
  60. package/src/lsp/tools.js +137 -27
  61. package/src/process/index.js +4 -2
  62. package/src/settings/index.js +2 -44
  63. package/src/settings/sections.js +2 -24
  64. package/src/shared/audit.js +9 -1
  65. package/src/shared/child-scope.js +2 -0
  66. package/src/todo-driver/index.js +1 -6
  67. package/src/worktree/cards.js +6 -21
  68. package/src/worktree/lane-core.js +1 -19
  69. package/src/worktree/lane-workflows.js +11 -32
  70. package/src/worktree/rules.js +1 -1
  71. package/lib/client.edit-lock-maintenance.js +0 -592
  72. package/lib/client.edit-lock-panel.js +0 -405
  73. package/src/edit-lock/adapter.js +0 -57
  74. package/src/edit-lock/admin-ledger.js +0 -149
  75. package/src/edit-lock/admin-recovery.js +0 -151
  76. package/src/edit-lock/admission.js +0 -82
  77. package/src/edit-lock/binding-attestation.js +0 -29
  78. package/src/edit-lock/binding-guidance.js +0 -72
  79. package/src/edit-lock/binding-machine.js +0 -312
  80. package/src/edit-lock/call-context.js +0 -65
  81. package/src/edit-lock/derived-facts.js +0 -67
  82. package/src/edit-lock/domain.js +0 -165
  83. package/src/edit-lock/domains.js +0 -170
  84. package/src/edit-lock/host.js +0 -149
  85. package/src/edit-lock/incarnations.js +0 -150
  86. package/src/edit-lock/index.js +0 -1303
  87. package/src/edit-lock/inspect.js +0 -219
  88. package/src/edit-lock/lifecycle.js +0 -579
  89. package/src/edit-lock/limits.js +0 -61
  90. package/src/edit-lock/maintenance.js +0 -310
  91. package/src/edit-lock/manager.js +0 -875
  92. package/src/edit-lock/negotiation.js +0 -132
  93. package/src/edit-lock/operation-history.js +0 -214
  94. package/src/edit-lock/peer-client.js +0 -86
  95. package/src/edit-lock/peer-transport.js +0 -76
  96. package/src/edit-lock/peer.js +0 -87
  97. package/src/edit-lock/publisher.js +0 -191
  98. package/src/edit-lock/read-authority.js +0 -101
  99. package/src/edit-lock/recovery.js +0 -139
  100. package/src/edit-lock/remote.js +0 -362
  101. package/src/edit-lock/request-binding.js +0 -42
  102. package/src/edit-lock/request-data.js +0 -40
  103. package/src/edit-lock/reservation.js +0 -301
  104. package/src/edit-lock/reserved-runtime.js +0 -45
  105. package/src/edit-lock/resource-identity.js +0 -130
  106. package/src/edit-lock/runtime.js +0 -145
  107. package/src/edit-lock/settle.js +0 -144
  108. package/src/edit-lock/snapshot.js +0 -175
  109. package/src/edit-lock/stale-sweep.js +0 -120
  110. package/src/edit-lock/state.js +0 -462
  111. package/src/edit-lock/store.js +0 -327
  112. package/src/edit-lock/tool-scope.js +0 -29
  113. package/src/edit-lock/view.js +0 -170
  114. package/src/edit-lock/write-scopes.js +0 -48
  115. package/src/edit-lock/write-tool.js +0 -45
  116. package/src/shared/delegate-edit-lifecycle.js +0 -36
  117. package/src/worktree/authority.js +0 -56
package/lib/client.js CHANGED
@@ -1,4 +1,4 @@
1
- // Weir client chunks: {"client.blackboard-model.js":"2e5abdd44edb599d10d9d547fa07133e84250d6007161c2c8704b58151896f93","client.blackboard-panel.js":"c9d6a0f02623075fc884f3519f982574ad33ad871f7bcd7e8c36f12512f0de39","client.capability-badge.js":"6ffd703f498e0aa5cc8ac78f244402ec4ceee56d22d2a0e74c246799565776cc","client.capability-manager.js":"d22e8116922f98e61bbbdf36fc78bd81e8b47b0ee5e75ddb06bea4e204749b89","client.capability-model.js":"d2aef1aa2daaecad6c9a1b9d9a0ecf6fb37dd7ec80efff5d532cf3b8a6efa712","client.capability-presets.js":"12ac8da3568d013abd76b67cfe473a329f4af2feaac9cd55a82d8881e5bc1017","client.chain-editor.js":"1e9a33bc68cabc43bc27b91ef83eab5028a33256596c147e15722d684687765b","client.chain-model.js":"2e885922cf8c2972725c6eef8bf20c7d1202d961edfb8ad41574dac062d74371","client.disabled-categories-editor.js":"f9103fd2e2691eda9afd199e1a5462189097a9f3dee0518be5fa3587d896e033","client.edit-lock-maintenance.js":"5e6933f3ad222ea99e890a06b94a223de1e864de058fca2a53954d62eaad9ab8","client.edit-lock-panel.js":"fa78a0762d2787e6322308dfff103aab3cdbd078d1ec22000d6deefe258153b1","client.hash-edit-model.js":"71727d47a6ef32ebd2abfb063fa93499a5e607d6716e3ec8983ae056c4fb317f","client.hash-edit-view.js":"fc0f4dabddab9a48527553ad4232b3715e46e6863aab75e02ae2dcd6687265c2","client.lsp-model.js":"30943ed1ed11c1f2770a724da855ea2417c11566c2778c79e17cfc2de499cd80","client.lsp-panel.js":"61629a4d317dae9ed01c1cc0355632fb752788dc23f7606257188050734cd030","client.lsp-toggle.js":"d6af81be5e5b854e98563bbb9349a6db89834a9d7dee9cf0e97759ecb32090e8","client.model-picker.js":"57f96826e00b37ae6d6474a6750f31b4e128e5fdb37e302875a41cdb2e18f14a","client.notify-permissions.js":"2be9bdbeffef96baa3cbe4ce430ab30a1d1bf797782453d287d54602ea47db8e","client.notify-web.js":"0943f67d5526ff0fb7cb4f69580e0a9ea54ed06465c513afc51a5a7fb1c55ccf","client.robash-editor.js":"cd6a283b72cb02598e32ca4ce03a1b8de5f328ecdd1f5f920377524f3bdcbe1c","client.robash-model.js":"f4395338a773e8bb48e4f96aeb737cd634edcca0b90af992a7dbbe47be42e451","client.settings-page.js":"b6524f04641793602e00bc42f1c86f7e8981b903e16a8493379126c359dbab96","client.worktree-model.js":"8bfbaaa080f20e90a0128afccb9b4e232514615a8f2fee3a32a42dde78b34a8f","client.worktree-view.js":"356c3f5f683b8509ae8679243a6460ca02957fd15e057a629b2ac4fdd12ed057"}
1
+ // Weir client chunks: {"client.blackboard-model.js":"2e5abdd44edb599d10d9d547fa07133e84250d6007161c2c8704b58151896f93","client.blackboard-panel.js":"c9d6a0f02623075fc884f3519f982574ad33ad871f7bcd7e8c36f12512f0de39","client.capability-badge.js":"f5c5d21293360d00815446beb8455794a1778ae033e270af37e436329ac09185","client.capability-manager.js":"f02e08174487f2f70e90a5c50a4a44cbeb4d462f115a2d2ab8a52ed8b4e70aee","client.capability-model.js":"57127dc5ea7ca6aa70c619480684f4de84fcbff4922f70df9886f55fad66abbf","client.capability-presets.js":"12ac8da3568d013abd76b67cfe473a329f4af2feaac9cd55a82d8881e5bc1017","client.chain-editor.js":"1e9a33bc68cabc43bc27b91ef83eab5028a33256596c147e15722d684687765b","client.chain-model.js":"2e885922cf8c2972725c6eef8bf20c7d1202d961edfb8ad41574dac062d74371","client.disabled-categories-editor.js":"f9103fd2e2691eda9afd199e1a5462189097a9f3dee0518be5fa3587d896e033","client.file-lease-badge.js":"cd30546307c91c94fe05484557e46f58c53fe55657f62b0a0415a072dfb3b582","client.file-lease-tool-model.js":"f72d87b049454ec489b4dffac1f32dabc273947b8a4c55fe07c91ddf320bd899","client.file-lease-tool-view.js":"7004caa06b36d8497a1d2e448b6ce5e4c8efd22ad903d8b39e26220de64bb1fe","client.hash-edit-model.js":"71727d47a6ef32ebd2abfb063fa93499a5e607d6716e3ec8983ae056c4fb317f","client.hash-edit-view.js":"fc0f4dabddab9a48527553ad4232b3715e46e6863aab75e02ae2dcd6687265c2","client.lsp-model.js":"30943ed1ed11c1f2770a724da855ea2417c11566c2778c79e17cfc2de499cd80","client.lsp-panel.js":"61629a4d317dae9ed01c1cc0355632fb752788dc23f7606257188050734cd030","client.lsp-toggle.js":"d6af81be5e5b854e98563bbb9349a6db89834a9d7dee9cf0e97759ecb32090e8","client.mcp-manager.js":"cfcd4792251fcca8bfa6a2c35b80881ab6f6480cf2ebce24a8ce304cddf940a7","client.model-picker.js":"57f96826e00b37ae6d6474a6750f31b4e128e5fdb37e302875a41cdb2e18f14a","client.notify-permissions.js":"2be9bdbeffef96baa3cbe4ce430ab30a1d1bf797782453d287d54602ea47db8e","client.notify-web.js":"0943f67d5526ff0fb7cb4f69580e0a9ea54ed06465c513afc51a5a7fb1c55ccf","client.robash-editor.js":"cd6a283b72cb02598e32ca4ce03a1b8de5f328ecdd1f5f920377524f3bdcbe1c","client.robash-model.js":"f4395338a773e8bb48e4f96aeb737cd634edcca0b90af992a7dbbe47be42e451","client.settings-page.js":"f3c04a5b33cdb963357bc653f7050e598f19efc4cc833694379a4ba055e949d3","client.worktree-model.js":"8bfbaaa080f20e90a0128afccb9b4e232514615a8f2fee3a32a42dde78b34a8f","client.worktree-view.js":"356c3f5f683b8509ae8679243a6460ca02957fd15e057a629b2ac4fdd12ed057"}
2
2
  window.__ModuleLoader__.load({
3
3
  id: "weir-harness",
4
4
  factory: (require) => {
@@ -49,6 +49,17 @@ window.__ModuleLoader__.load({
49
49
  worktreeLoading: "Loading lanes…",
50
50
  hashEditInput: "Input",
51
51
  hashEditOutput: "Output",
52
+ // Edit-lock conversation cards: the seven tool titles the pre-arrival
53
+ // placeholder reads synchronously. Byte-identical to the view chunk's
54
+ // data face ("settings.weir.file-lease-tools") — a documented
55
+ // cross-namespace duplicate pinned by test/client.test.js.
56
+ leaseToolTitle_edit_lock_acquire: "Claim edit lock",
57
+ leaseToolTitle_edit_lock_release: "Release edit lock",
58
+ leaseToolTitle_edit_lock_hold: "Keep edit lock",
59
+ leaseToolTitle_edit_lock_status: "Edit lock status",
60
+ leaseToolTitle_edit_lock_try_steal: "Request edit lock transfer",
61
+ leaseToolTitle_edit_lock_reply: "Answer transfer request",
62
+ leaseToolTitle_edit_lock_watch: "Watch for release",
52
63
  // Session capability Badge + manager (12.x, D3/D4): the chunks carry
53
64
  // the same strings as in-code fallbacks; these rows translate them.
54
65
  capabilityTabLabel: "Capabilities",
@@ -84,6 +95,13 @@ window.__ModuleLoader__.load({
84
95
  worktreeLoading: "正在加载车道…",
85
96
  hashEditInput: "输入",
86
97
  hashEditOutput: "输出",
98
+ leaseToolTitle_edit_lock_acquire: "申请编辑锁",
99
+ leaseToolTitle_edit_lock_release: "释放编辑锁",
100
+ leaseToolTitle_edit_lock_hold: "保留编辑锁",
101
+ leaseToolTitle_edit_lock_status: "编辑锁状态",
102
+ leaseToolTitle_edit_lock_try_steal: "请求转交编辑锁",
103
+ leaseToolTitle_edit_lock_reply: "答复转交请求",
104
+ leaseToolTitle_edit_lock_watch: "订阅释放",
87
105
  capabilityTabLabel: "能力",
88
106
  capabilityGuideTitle: "能力",
89
107
  capabilityGuideDescription: "此会话的技能、MCP server 与预设。",
@@ -122,7 +140,8 @@ window.__ModuleLoader__.load({
122
140
  register: null,
123
141
  lspToggle: null,
124
142
  hashEdit: null,
125
- editLock: null,
143
+ fileLease: null,
144
+ fileLeaseTools: null,
126
145
  worktree: null,
127
146
  };
128
147
  // ---- Package-local chunk wiring (composition root) ----
@@ -218,16 +237,69 @@ window.__ModuleLoader__.load({
218
237
  if (!arrival?.chunks) return null;
219
238
  return react_jsx_runtime.jsx(arrival.chunks.LspToggle, { ...props, settingsBus, t: chunkDictionaries.lspToggle ?? props.t });
220
239
  }
221
- const loadEditLockChunk = lazyChunks(() => require.async("./client.edit-lock-panel.js").then((chunk) => {
222
- chunkDictionaries.register?.("settings.weir.edit-lock", chunk.dictionaries);
240
+ const loadFileLeaseChunk = lazyChunks(() => require.async("./client.file-lease-badge.js").then((chunk) => {
241
+ chunkDictionaries.register?.("settings.weir.file-lease", chunk.dictionaries);
223
242
  return chunk;
224
243
  }));
225
- /** Composer-bar Edit Lock entry: nothing until its chunk arrives. */
226
- function EditLockWrapper(props) {
227
- const arrival = useChunkArrival(loadEditLockChunk, typeof props.sessionId === "string" && props.sessionId !== "");
244
+ /** Composer-bar file-lease entry: nothing until its chunk arrives. */
245
+ function FileLeaseBadgeWrapper(props) {
246
+ const arrival = useChunkArrival(loadFileLeaseChunk, typeof props.sessionId === "string" && props.sessionId !== "");
228
247
  if (!arrival?.chunks) return null;
229
- return react_jsx_runtime.jsx(arrival.chunks.EditLockPanel, { ...props, t: chunkDictionaries.editLock ?? props.t });
248
+ return react_jsx_runtime.jsx(arrival.chunks.FileLeaseBadge, { ...props, t: chunkDictionaries.fileLease ?? props.t });
230
249
  }
250
+ // ---- Edit-lock conversation cards (edit-lock-tool-cards D2) ----
251
+ // One view chunk plus one model chunk carry the seven keyed tool cards;
252
+ // the dictionaries ride the view chunk and register under their own
253
+ // namespace (the composer badge already owns "settings.weir.file-lease",
254
+ // and a namespace registers once per apply).
255
+ const FILE_LEASE_TOOL_NAMES = ["edit_lock_acquire", "edit_lock_release", "edit_lock_hold", "edit_lock_status", "edit_lock_try_steal", "edit_lock_reply", "edit_lock_watch"];
256
+ const loadFileLeaseToolChunks = lazyChunks(() => Promise.all([
257
+ require.async("./client.file-lease-tool-view.js"),
258
+ require.async("./client.file-lease-tool-model.js")
259
+ ]).then((chunks) => {
260
+ chunkDictionaries.register?.("settings.weir.file-lease-tools", chunks[0].dictionaries);
261
+ return chunks;
262
+ }));
263
+ // Pre-arrival (or failed-load) placeholder: the localized title and the
264
+ // requested paths from the call arguments, so a card is never blank. The
265
+ // argument read mirrors the model chunk's argsPaths (same-package sync
266
+ // require is impossible, so the tiny parse is mirrored here, not shared).
267
+ const fileLeasePlaceholderStyle = { display: "flex", alignItems: "baseline", gap: "8px", minWidth: 0, padding: "8px 10px", border: "1px solid var(--dsw-alias-border-l2)", borderRadius: "var(--dsw-radius-md, 8px)", background: "var(--dsw-alias-bg-layer-1)" };
268
+ const fileLeasePlaceholderTitleStyle = { fontSize: "13px", lineHeight: "18px", fontWeight: 600, color: "var(--dsw-alias-label-primary)", whiteSpace: "nowrap" };
269
+ const fileLeasePlaceholderPathStyle = { fontSize: "12px", lineHeight: "16px", color: "var(--dsw-alias-label-secondary)", fontFamily: "var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", minWidth: 0 };
270
+ const fileLeaseArgsPaths = (block) => {
271
+ const raw = typeof block?.argsRaw === "string" ? block.argsRaw : typeof block?.call?.argsRaw === "string" ? block.call.argsRaw : null;
272
+ if (raw === null) return [];
273
+ let args;
274
+ try {
275
+ args = JSON.parse(raw);
276
+ } catch {
277
+ return [];
278
+ }
279
+ if (Array.isArray(args?.paths)) return args.paths.filter((path) => typeof path === "string" && path !== "");
280
+ return typeof args?.file_path === "string" && args.file_path !== "" ? [args.file_path] : [];
281
+ };
282
+ function FileLeaseToolPlaceholder(props) {
283
+ const paths = fileLeaseArgsPaths(props.block);
284
+ const title = typeof props.t === "function" ? props.t(`leaseToolTitle_${props.toolName}`) : props.toolName;
285
+ return react_jsx_runtime.jsxs("div", { "data-tool": props.toolName, "data-weir-lease-placeholder": "", style: fileLeasePlaceholderStyle, children: [
286
+ react_jsx_runtime.jsx("span", { style: fileLeasePlaceholderTitleStyle, children: title }, "title"),
287
+ paths.length > 0 ? react_jsx_runtime.jsx("span", { style: fileLeasePlaceholderPathStyle, children: paths.length > 1 ? `${paths[0]} +${paths.length - 1}` : paths[0] }, "path") : null
288
+ ] });
289
+ }
290
+ /** Keyed toolview wrapper for one edit_lock_* tool: the placeholder until
291
+ * the view+model chunks arrive, then the read-only card. */
292
+ const fileLeaseToolView = (toolName) => {
293
+ function FileLeaseToolViewWrapper(props) {
294
+ const arrival = useChunkArrival(loadFileLeaseToolChunks);
295
+ if (arrival?.chunks) {
296
+ const [view, model] = arrival.chunks;
297
+ return react_jsx_runtime.jsx(view.FileLeaseToolRow, { ...props, toolName, t: chunkDictionaries.fileLeaseTools ?? props.t, model });
298
+ }
299
+ return react_jsx_runtime.jsx(FileLeaseToolPlaceholder, { ...props, toolName });
300
+ }
301
+ return FileLeaseToolViewWrapper;
302
+ };
231
303
  // ---- Tab glyphs (ui-entries-and-icons) ----
232
304
  // Three 16x16 stroke glyphs drawn in currentColor, matching the host
233
305
  // CompassGlyph/CubeGlyph style (no strokeWidth, rounded caps/joins).
@@ -427,7 +499,8 @@ window.__ModuleLoader__.load({
427
499
  chunkDictionaries.register = registerChunkDictionaries;
428
500
  chunkDictionaries.lspToggle = ctx.locale.bind("settings.weir.lsp-toggle");
429
501
  chunkDictionaries.hashEdit = ctx.locale.bind("settings.weir.hash-edit");
430
- chunkDictionaries.editLock = ctx.locale.bind("settings.weir.edit-lock");
502
+ chunkDictionaries.fileLease = ctx.locale.bind("settings.weir.file-lease");
503
+ chunkDictionaries.fileLeaseTools = ctx.locale.bind("settings.weir.file-lease-tools");
431
504
  chunkDictionaries.worktree = ctx.locale.bind("settings.weir.worktree");
432
505
  const pageT = ctx.locale.bind("settings.weir.page");
433
506
  const capabilityT = ctx.locale.bind("settings.weir.capability");
@@ -619,28 +692,20 @@ window.__ModuleLoader__.load({
619
692
  key: "hash_edit",
620
693
  locale: NS
621
694
  }, HashEditToolView)), "ui-weir-settings: hash_edit toolview");
622
- // Edit Lock entry: renders only while the `edit-lock` command exists
623
- // (feature enabled). Each action is an explicit human /edit-lock run.
695
+ // File-lease ownership entry (edit-lock rebuild D13): the read-only
696
+ // badge of the file-lease feature. It carries NO action verb and no
697
+ // command bridge — every ownership state resolves by itself (turn end,
698
+ // expiry, negotiation) — and its single read is the read-only view
699
+ // endpoint, so nothing in this surface can write to the conversation.
624
700
  ctx.effect(() => ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
625
701
  name: "conversation.input.right",
626
- id: "weir-edit-lock",
702
+ id: "weir-file-lease",
627
703
  order: 101,
628
704
  locale: NS,
629
705
  inject: (sessionId) => {
630
706
  if (!sessionId) return {};
631
707
  return {
632
708
  sessionId,
633
- runEditLock: async (verb) => {
634
- if (!ctx.remote.commands?.execute) return { kind: "error", text: "unknown command: /edit-lock" };
635
- const result = await ctx.remote.commands.execute(sessionId, `/edit-lock ${verb}`, []);
636
- if (!result.ok) return { kind: "error", text: `${result.error.message} (${result.error.code})` };
637
- if (result.value === undefined) return { kind: "error", text: "unknown command: /edit-lock" };
638
- return result.value.result;
639
- },
640
- commandsList: (sid) => {
641
- if (!ctx.remote.commands?.list) return Promise.resolve([]);
642
- return ctx.remote.commands.list(sid).then((result) => (result.ok ? result.value : []));
643
- },
644
709
  // Structured view: read-only, never written to the conversation.
645
710
  fetchView: async () => {
646
711
  const result = await httpChannel("api/weir-edit-lock/view", { sessionId });
@@ -649,7 +714,7 @@ window.__ModuleLoader__.load({
649
714
  }
650
715
  };
651
716
  }
652
- }, EditLockWrapper)), "ui-weir-settings: edit lock entry");
717
+ }, FileLeaseBadgeWrapper)), "ui-weir-settings: file lease badge");
653
718
  // ---- Worktree lane surfaces ----
654
719
  // Each surface registers in its own effect so one failure cannot take
655
720
  // down the others (same discipline as the hash_edit view). Reads go to
@@ -810,6 +875,11 @@ window.__ModuleLoader__.load({
810
875
  const result = await capabilityChannel(`weirCapabilities/${method}`, { args: { sessionId: sid } });
811
876
  return result.ok === true ? result.value : fallback;
812
877
  };
878
+ const capabilityMutation = async (method, sid, request) => {
879
+ const result = await capabilityChannel(`weirCapabilityMutations/${method}`, { args: { sessionId: sid, request } });
880
+ if (result.ok === true) return result.value;
881
+ return { status: result.error?.code ?? "call-failed", saved: false, error: result.error ?? { code: "call-failed", message: "MCP mutation failed" } };
882
+ };
813
883
  // The presets & workspace-default view is its own chunk, pulled only
814
884
  // when the Presets view is first selected (D3) — the composition root
815
885
  // owns every require.async specifier (no chunk-to-chunk waterfall).
@@ -837,6 +907,9 @@ window.__ModuleLoader__.load({
837
907
  // (explicit error surface) — a null payload maps to the error state.
838
908
  applySelection: (sid, draft) => capabilityPayload(sid, `/capabilities apply ${JSON.stringify(draft)}`, true),
839
909
  mcpAdd: (sid, spec) => capabilityPayload(sid, `/capabilities mcp-add ${JSON.stringify(spec)}`),
910
+ mcpPreview: (sid, request) => capabilityMutation("mutate", sid, request),
911
+ mcpMutate: (sid, request) => capabilityMutation("mutate", sid, request),
912
+ mcpBatch: (sid, request) => capabilityMutation("batch", sid, request),
840
913
  fetchListing: (sid) => capabilityRead("list", sid, { error: true }),
841
914
  fetchConditions: (sid) => capabilityRead("conditions", sid, { error: true }),
842
915
  // The manager forwards only its established props. Adapt the lazy view
@@ -867,14 +940,19 @@ window.__ModuleLoader__.load({
867
940
  const [impl, setImpl] = react.useState(null);
868
941
  react.useEffect(() => {
869
942
  let alive = true;
870
- Promise.all([require.async("./client.capability-badge.js"), require.async("./client.capability-model.js"), require.async("./client.capability-manager.js")]).then(([badge, model, panel]) => {
943
+ Promise.all([
944
+ require.async("./client.capability-badge.js"),
945
+ require.async("./client.capability-model.js"),
946
+ require.async("./client.capability-manager.js"),
947
+ require.async("./client.mcp-manager.js").catch(() => null),
948
+ ]).then(([badge, model, panel, mcp]) => {
871
949
  registerChunkDictionaries("settings.weir.capability", model.dictionaries);
872
- if (alive) setImpl(() => ({ Badge: badge.CapabilityBadge, model, Panel: panel.CapabilityManagerPanel }));
950
+ if (alive) setImpl(() => ({ Badge: badge.CapabilityBadge, model, Panel: panel.CapabilityManagerPanel, McpManagerView: mcp?.McpManagerView, McpManagerBoundary: mcp?.McpManagerBoundary }));
873
951
  });
874
952
  return () => { alive = false; };
875
953
  }, [props.sessionId]);
876
954
  if (!impl) return null;
877
- return react_jsx_runtime.jsx(impl.Badge, { ...props, t: capabilityT, model: impl.model, ManagerPanel: impl.Panel });
955
+ return react_jsx_runtime.jsx(impl.Badge, { ...props, t: capabilityT, model: impl.model, ManagerPanel: impl.Panel, McpManagerView: impl.McpManagerView, McpManagerBoundary: impl.McpManagerBoundary });
878
956
  };
879
957
  ctx.effect(() => ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
880
958
  name: "conversation.input.right",
@@ -903,7 +981,8 @@ window.__ModuleLoader__.load({
903
981
  // view tree.
904
982
  const loadCapabilityPanelChunks = lazyChunks(() => Promise.all([
905
983
  require.async("./client.capability-model.js"),
906
- require.async("./client.capability-manager.js")
984
+ require.async("./client.capability-manager.js"),
985
+ require.async("./client.mcp-manager.js").catch(() => null),
907
986
  ]).then((chunks) => {
908
987
  registerChunkDictionaries("settings.weir.capability", chunks[0].dictionaries);
909
988
  return chunks;
@@ -911,10 +990,10 @@ window.__ModuleLoader__.load({
911
990
  function CapabilityPanelWrapper(props) {
912
991
  const arrival = useChunkArrival(loadCapabilityPanelChunks, typeof props.sessionId === "string" && props.sessionId !== "");
913
992
  if (!arrival?.chunks) return react_jsx_runtime.jsx("div", { style: settingsLoadingStyle, children: typeof props.t === "function" ? props.t("capability.loading") : "" });
914
- const [model, panel] = arrival.chunks;
993
+ const [model, panel, mcp] = arrival.chunks;
915
994
  // The same view tree the composer popover renders — the sidebar is
916
995
  // just the outer frame (D1). No onClose: the pane chrome owns closing.
917
- return react_jsx_runtime.jsx(panel.CapabilityManagerPanel, { ...props, t: capabilityT, model, shell: "sidebar" });
996
+ return react_jsx_runtime.jsx(panel.CapabilityManagerPanel, { ...props, t: capabilityT, model, shell: "sidebar", McpManagerView: mcp?.McpManagerView, McpManagerBoundary: mcp?.McpManagerBoundary });
918
997
  }
919
998
  ctx.inject?.(["sidebarRightTabs"], (sidebarScope) => {
920
999
  capabilitySidebarTab = true;
@@ -1013,6 +1092,17 @@ window.__ModuleLoader__.load({
1013
1092
  }
1014
1093
  } : {})
1015
1094
  }, BlackboardPill)), "ui-weir-settings: blackboard header pill");
1095
+ // Edit-lock conversation cards (edit-lock-tool-cards D2): one keyed,
1096
+ // read-only tool view per edit_lock_* tool, each in its own effect so
1097
+ // one registration failure cannot take down the others. Registration
1098
+ // pulls no chunk; the wrapper renders a title+paths placeholder first.
1099
+ for (const toolName of FILE_LEASE_TOOL_NAMES) {
1100
+ ctx.effect(() => ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
1101
+ name: "tool.call.toolview",
1102
+ key: toolName,
1103
+ locale: NS
1104
+ }, fileLeaseToolView(toolName))), `ui-weir-settings: ${toolName} toolview`);
1105
+ }
1016
1106
  // Top-level Settings section (same place as dsh-web-kimi and the
1017
1107
  // built-in General/Models sections), with a nested item slot
1018
1108
  // hosting the form — the single Weir settings entry.
@@ -1064,15 +1154,14 @@ window.__ModuleLoader__.load({
1064
1154
  require.async("./client.chain-model.js"),
1065
1155
  require.async("./client.robash-model.js"),
1066
1156
  require.async("./client.lsp-model.js"),
1067
- require.async("./client.notify-permissions.js"),
1068
- require.async("./client.edit-lock-maintenance.js")
1069
- ]).then(([settingsPage, chainEditor, robashEditor, disabledCategoriesEditor, lspPanel, chainModel, robashModel, lspModel, notifyPermissions, editLockMaintenance]) => {
1157
+ require.async("./client.notify-permissions.js")
1158
+ ]).then(([settingsPage, chainEditor, robashEditor, disabledCategoriesEditor, lspPanel, chainModel, robashModel, lspModel, notifyPermissions]) => {
1070
1159
  // The whole settings card tree shares one t (the page
1071
1160
  // namespace): every batch chunk's data face merges into ONE
1072
1161
  // registration — a duplicate (namespace, locale) pair would
1073
1162
  // throw, so per-feature namespaces are not an option here.
1074
1163
  const pageDictionaries = { en: {}, zh: {} };
1075
- for (const chunk of [settingsPage, chainModel, robashModel, lspModel, disabledCategoriesEditor, notifyPermissions, editLockMaintenance]) {
1164
+ for (const chunk of [settingsPage, chainModel, robashModel, lspModel, disabledCategoriesEditor, notifyPermissions]) {
1076
1165
  Object.assign(pageDictionaries.en, chunk.dictionaries?.en);
1077
1166
  Object.assign(pageDictionaries.zh, chunk.dictionaries?.zh);
1078
1167
  }
@@ -1094,8 +1183,7 @@ window.__ModuleLoader__.load({
1094
1183
  // The HTTP channel adapter reaches every fetch-talking editor
1095
1184
  // through the same prop (composition-root injection: a chunk
1096
1185
  // never builds its own carrier).
1097
- NotifyPermissionsField: (editorProps) => react_jsx_runtime.jsx(notifyPermissions.NotifyPermissionsField, { ...editorProps, channel: httpChannel }),
1098
- EditLockMaintenanceField: (editorProps) => react_jsx_runtime.jsx(editLockMaintenance.EditLockMaintenanceField, { ...editorProps, channel: httpChannel })
1186
+ NotifyPermissionsField: (editorProps) => react_jsx_runtime.jsx(notifyPermissions.NotifyPermissionsField, { ...editorProps, channel: httpChannel })
1099
1187
  };
1100
1188
  return { settingsPage, editors };
1101
1189
  }, (error) => {