weir-harness 1.2.3 → 2.0.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.
- package/cordis.patch.yml +7 -18
- package/lib/client.file-lease-badge.js +307 -0
- package/lib/client.js +20 -30
- package/lib/client.settings-page.js +10 -34
- package/package.json +2 -2
- package/src/capabilities/store/checked-contract.d.ts +2 -2
- package/src/capabilities/store/fs-adapter.js +2 -3
- package/src/context-guard/index.js +2 -2
- package/src/delegate/supervision-mount.js +5 -5
- package/src/delegate/tool.js +2 -10
- package/src/file-lease/client.js +1066 -0
- package/src/file-lease/index.js +30 -0
- package/src/file-lease/lineage.js +207 -0
- package/src/file-lease/node-contracts.d.ts +42 -0
- package/src/file-lease/plugin.js +789 -0
- package/src/file-lease/singleton.js +155 -0
- package/src/file-lease/spawn.js +62 -0
- package/src/file-lease/tools.js +459 -0
- package/src/file-lease/view.js +144 -0
- package/src/hashline-edit/index.js +76 -42
- package/src/hashline-edit/write-tool.js +90 -0
- package/src/lease-broker/entry.js +98 -0
- package/src/lease-broker/node-contracts.d.ts +63 -0
- package/src/lease-broker/protocol.js +236 -0
- package/src/lease-broker/server.js +399 -0
- package/src/lease-broker/state.js +425 -0
- package/src/lsp/tools.js +137 -27
- package/src/settings/index.js +2 -44
- package/src/settings/sections.js +2 -24
- package/src/shared/audit.js +6 -1
- package/src/todo-driver/index.js +1 -6
- package/src/worktree/cards.js +6 -21
- package/src/worktree/lane-core.js +1 -19
- package/src/worktree/lane-workflows.js +11 -32
- package/lib/client.edit-lock-maintenance.js +0 -592
- package/lib/client.edit-lock-panel.js +0 -405
- package/src/edit-lock/adapter.js +0 -57
- package/src/edit-lock/admin-ledger.js +0 -149
- package/src/edit-lock/admin-recovery.js +0 -151
- package/src/edit-lock/admission.js +0 -82
- package/src/edit-lock/binding-attestation.js +0 -29
- package/src/edit-lock/binding-guidance.js +0 -72
- package/src/edit-lock/binding-machine.js +0 -312
- package/src/edit-lock/call-context.js +0 -65
- package/src/edit-lock/derived-facts.js +0 -67
- package/src/edit-lock/domain.js +0 -165
- package/src/edit-lock/domains.js +0 -170
- package/src/edit-lock/host.js +0 -149
- package/src/edit-lock/incarnations.js +0 -150
- package/src/edit-lock/index.js +0 -1303
- package/src/edit-lock/inspect.js +0 -219
- package/src/edit-lock/lifecycle.js +0 -579
- package/src/edit-lock/limits.js +0 -61
- package/src/edit-lock/maintenance.js +0 -310
- package/src/edit-lock/manager.js +0 -875
- package/src/edit-lock/negotiation.js +0 -132
- package/src/edit-lock/operation-history.js +0 -214
- package/src/edit-lock/peer-client.js +0 -86
- package/src/edit-lock/peer-transport.js +0 -76
- package/src/edit-lock/peer.js +0 -87
- package/src/edit-lock/publisher.js +0 -191
- package/src/edit-lock/read-authority.js +0 -101
- package/src/edit-lock/recovery.js +0 -139
- package/src/edit-lock/remote.js +0 -362
- package/src/edit-lock/request-binding.js +0 -42
- package/src/edit-lock/request-data.js +0 -40
- package/src/edit-lock/reservation.js +0 -301
- package/src/edit-lock/reserved-runtime.js +0 -45
- package/src/edit-lock/resource-identity.js +0 -130
- package/src/edit-lock/runtime.js +0 -145
- package/src/edit-lock/settle.js +0 -144
- package/src/edit-lock/snapshot.js +0 -175
- package/src/edit-lock/stale-sweep.js +0 -120
- package/src/edit-lock/state.js +0 -462
- package/src/edit-lock/store.js +0 -327
- package/src/edit-lock/tool-scope.js +0 -29
- package/src/edit-lock/view.js +0 -170
- package/src/edit-lock/write-scopes.js +0 -48
- package/src/edit-lock/write-tool.js +0 -45
- package/src/shared/delegate-edit-lifecycle.js +0 -36
- package/src/worktree/authority.js +0 -56
package/cordis.patch.yml
CHANGED
|
@@ -40,12 +40,6 @@
|
|
|
40
40
|
worktreeAutoSetup: true
|
|
41
41
|
worktreeWatchTimeoutMinutes: 360
|
|
42
42
|
worktreeAutoApprove: auto-clean
|
|
43
|
-
editLockEnabled: true
|
|
44
|
-
# Message-driven auto-resume (edit-lock-auto-resume-on-message): a genuine
|
|
45
|
-
# user message resumes a stopped session and confirms its retained files.
|
|
46
|
-
# Read live per message, so toggling applies without a restart.
|
|
47
|
-
editLockAutoResume: true
|
|
48
|
-
editLockStaleSweep: true
|
|
49
43
|
robashEnabled: true
|
|
50
44
|
# The five whitelist tables are NOT declared here on purpose. A patch
|
|
51
45
|
# row's `config` is replaced WHOLE rather than deep-merged, so a default
|
|
@@ -185,10 +179,6 @@
|
|
|
185
179
|
compaction: true
|
|
186
180
|
toolResultPruner: true
|
|
187
181
|
weirCompactionCoordinator: true
|
|
188
|
-
# 编辑锁服务:提供者 edit-lock 与消费者 todo-driver / hashline-edit /
|
|
189
|
-
# lsp 同在本组。预设服务必须 isolate,否则预设加载以
|
|
190
|
-
# "Preset services require isolate realms" 失败。
|
|
191
|
-
weirEditLock: true
|
|
192
182
|
# Worktree lanes service: provider (worktree) and consumer
|
|
193
183
|
# (delegate) share this realm (S24 / S26 S-A).
|
|
194
184
|
weirWorktreeLanes: true
|
|
@@ -241,10 +231,10 @@
|
|
|
241
231
|
# F4 todo 续推:空转续推 + 用户打断/供应商错误语义 + 逃生舱
|
|
242
232
|
- id: todo-driver
|
|
243
233
|
name: 'weir-harness/todo-driver'
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
- id:
|
|
247
|
-
name: 'weir-harness/
|
|
234
|
+
# 文件租约(编辑锁新实现):进程内共享一个租约 client,发布五个所有权
|
|
235
|
+
# 工具;不发布 cordis 服务,因此不需要 isolate 组,也不再约束行序。
|
|
236
|
+
- id: file-lease
|
|
237
|
+
name: 'weir-harness/file-lease'
|
|
248
238
|
# F7 锚点编辑:read 锚点增强 + fail-closed hash_edit 工具
|
|
249
239
|
- id: hashline-edit
|
|
250
240
|
name: 'weir-harness/hashline-edit'
|
|
@@ -378,7 +368,6 @@
|
|
|
378
368
|
compaction: true
|
|
379
369
|
toolResultPruner: true
|
|
380
370
|
weirCompactionCoordinator: true
|
|
381
|
-
weirEditLock: true
|
|
382
371
|
weirWorktreeLanes: true
|
|
383
372
|
weirBlackboard: true
|
|
384
373
|
config:
|
|
@@ -418,9 +407,9 @@
|
|
|
418
407
|
name: 'weir-harness/blackboard'
|
|
419
408
|
- id: todo-driver
|
|
420
409
|
name: 'weir-harness/todo-driver'
|
|
421
|
-
#
|
|
422
|
-
- id:
|
|
423
|
-
name: 'weir-harness/
|
|
410
|
+
# File leases (the edit-lock rebuild): no service, no ordering constraint.
|
|
411
|
+
- id: file-lease
|
|
412
|
+
name: 'weir-harness/file-lease'
|
|
424
413
|
- id: hashline-edit
|
|
425
414
|
name: 'weir-harness/hashline-edit'
|
|
426
415
|
config:
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "weir-harness",
|
|
3
|
+
chunk: "client.file-lease-badge.js",
|
|
4
|
+
factory: (require) => {
|
|
5
|
+
var module = { exports: {} };
|
|
6
|
+
var exports = module.exports;
|
|
7
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
// ---- Edit-lock Badge (conversation composer bar) ----
|
|
11
|
+
// The ONLY user-visible surface of the edit-lock mechanism (design D13).
|
|
12
|
+
// It is read-only by construction, not by convention:
|
|
13
|
+
//
|
|
14
|
+
// - the badge has exactly three states — idle, holding N, unprotected
|
|
15
|
+
// — and each one resolves by itself (turn end, expiry, negotiation).
|
|
16
|
+
// None of them asks the user for anything. The badge names the
|
|
17
|
+
// surface, and appends a fact only when there is one: how many files
|
|
18
|
+
// this session holds, or that protection is off. An idle badge is
|
|
19
|
+
// just its name — never a status word standing in for "nothing";
|
|
20
|
+
// - the panel speaks only when there is something to say. With no
|
|
21
|
+
// claim and no degraded protection it is ONE line; each group's
|
|
22
|
+
// heading appears only above a non-empty list, and the protection
|
|
23
|
+
// notice only when protection is degraded. There are no headings over
|
|
24
|
+
// empty lists and no disclaimer about what cannot be clicked: a
|
|
25
|
+
// surface with no controls needs no sentence saying so;
|
|
26
|
+
// - it contains NO button besides its own toggle, NO command input, NO
|
|
27
|
+
// confirmation step and no clickable row. Expand/collapse is the one
|
|
28
|
+
// interaction in this chunk; every ownership change belongs to the
|
|
29
|
+
// model's tools. A control here would reintroduce the "some state
|
|
30
|
+
// needs a human" that the rebuild exists to delete;
|
|
31
|
+
// - it never writes to the conversation and never notifies. The read
|
|
32
|
+
// goes to the one read-only endpoint through the composition root's
|
|
33
|
+
// `fetchView` prop; nothing else is called.
|
|
34
|
+
//
|
|
35
|
+
// Everything shown arrives pre-shaped by the server-side view
|
|
36
|
+
// (`src/file-lease/view.js`): short path names, holder labels, and how
|
|
37
|
+
// long a retention has left. This chunk renders that and derives only
|
|
38
|
+
// the presentation state — it never parses prose and never invents a
|
|
39
|
+
// fact.
|
|
40
|
+
const STATE_NONE = "none";
|
|
41
|
+
const STATE_HOLDING = "holding";
|
|
42
|
+
const STATE_DEGRADED = "degraded";
|
|
43
|
+
/** Status-dot colour per state; only theme-defined tokens. */
|
|
44
|
+
const dotColor = {
|
|
45
|
+
none: "var(--dsw-alias-label-tertiary)",
|
|
46
|
+
holding: "var(--dsw-alias-state-business-primary)",
|
|
47
|
+
degraded: "var(--dsw-alias-state-warn-primary)"
|
|
48
|
+
};
|
|
49
|
+
const rootStyle = { position: "relative", display: "inline-flex" };
|
|
50
|
+
const badgeStyle = {
|
|
51
|
+
display: "inline-flex",
|
|
52
|
+
alignItems: "center",
|
|
53
|
+
gap: "6px",
|
|
54
|
+
background: "none",
|
|
55
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
56
|
+
borderRadius: "var(--dsw-radius-sm)",
|
|
57
|
+
cursor: "pointer",
|
|
58
|
+
padding: "3px 8px",
|
|
59
|
+
fontSize: "12px",
|
|
60
|
+
lineHeight: "16px",
|
|
61
|
+
color: "var(--dsw-alias-label-secondary)"
|
|
62
|
+
};
|
|
63
|
+
// Popover surface: the theme's documented overlay token (light and dark).
|
|
64
|
+
const panelStyle = {
|
|
65
|
+
position: "absolute",
|
|
66
|
+
bottom: "calc(100% + 6px)",
|
|
67
|
+
right: 0,
|
|
68
|
+
zIndex: 20,
|
|
69
|
+
width: "min(420px, 80vw)",
|
|
70
|
+
maxHeight: "50vh",
|
|
71
|
+
overflow: "auto",
|
|
72
|
+
background: "var(--dsw-alias-bg-overlay)",
|
|
73
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
74
|
+
borderRadius: "var(--dsw-radius-md, 8px)",
|
|
75
|
+
boxShadow: "0 6px 24px var(--dsw-alias-border-l2)",
|
|
76
|
+
padding: "10px 12px",
|
|
77
|
+
color: "var(--dsw-alias-label-primary)",
|
|
78
|
+
fontSize: "12px",
|
|
79
|
+
lineHeight: "18px"
|
|
80
|
+
};
|
|
81
|
+
const mutedStyle = { color: "var(--dsw-alias-label-secondary)" };
|
|
82
|
+
/** A group heading: bold, and spaced only when something precedes it. */
|
|
83
|
+
const headStyle = { fontWeight: 600 };
|
|
84
|
+
// The degraded notice: amber (the same token the dot uses) at the panel's
|
|
85
|
+
// own weight — a status line, not an alarm.
|
|
86
|
+
const noticeStyle = { color: "var(--dsw-alias-state-warn-primary)" };
|
|
87
|
+
const rowStyle = { display: "flex", flexWrap: "wrap", alignItems: "baseline", gap: "8px", padding: "3px 0", borderTop: "1px solid var(--dsw-alias-border-l2)" };
|
|
88
|
+
const nameStyle = { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontFamily: "var(--dsw-font-markdown-code-block-font-family, ui-monospace, monospace)" };
|
|
89
|
+
const dot = (state) => react_jsx_runtime.jsx("span", { "aria-hidden": true, style: { display: "inline-block", width: "7px", height: "7px", borderRadius: "50%", background: dotColor[state], flex: "none" } });
|
|
90
|
+
/**
|
|
91
|
+
* The badge state of a view. Anything unreadable is `degraded`: an
|
|
92
|
+
* unreadable surface must never be presented as "nothing held", which
|
|
93
|
+
* reads like a fact the panel cannot actually vouch for.
|
|
94
|
+
* @param {any} view the server-shaped view
|
|
95
|
+
* @returns {"none" | "holding" | "degraded"}
|
|
96
|
+
*/
|
|
97
|
+
function stateOf(view) {
|
|
98
|
+
if (!view || typeof view !== "object") return STATE_DEGRADED;
|
|
99
|
+
if (view.arbiter !== "ok") return STATE_DEGRADED;
|
|
100
|
+
const mine = Array.isArray(view.mine) ? view.mine : [];
|
|
101
|
+
return mine.length > 0 ? STATE_HOLDING : STATE_NONE;
|
|
102
|
+
}
|
|
103
|
+
/** The claims of one section, tolerating a partial/degraded payload. */
|
|
104
|
+
function rowsOf(view, key) {
|
|
105
|
+
const rows = view && Array.isArray(view[key]) ? view[key] : [];
|
|
106
|
+
return rows.filter((row) => row && typeof row === "object" && typeof row.path === "string" && row.path !== "");
|
|
107
|
+
}
|
|
108
|
+
/** Remaining retention in whole minutes, or null when there is none. */
|
|
109
|
+
function retainedMinutes(holdUntil, at) {
|
|
110
|
+
if (typeof holdUntil !== "number" || !Number.isFinite(holdUntil)) return null;
|
|
111
|
+
const minutes = Math.max(0, Math.ceil((holdUntil - at) / 60000));
|
|
112
|
+
return minutes > 0 ? minutes : null;
|
|
113
|
+
}
|
|
114
|
+
function FileLeaseBadge(props) {
|
|
115
|
+
const t = props.t;
|
|
116
|
+
const sessionId = props.sessionId;
|
|
117
|
+
const [open, setOpen] = react.useState(false);
|
|
118
|
+
// `any`: the payload is the server-shaped view, narrowed on read.
|
|
119
|
+
const [view, setView] = react.useState(/** @type {any} */ (null));
|
|
120
|
+
// The one read: on mount so the badge is right before it is ever
|
|
121
|
+
// expanded, and again each time the panel opens (a snapshot, never a
|
|
122
|
+
// poll — ownership has no state to keep in sync).
|
|
123
|
+
const read = () => {
|
|
124
|
+
if (typeof props.fetchView !== "function") {
|
|
125
|
+
setView({ schemaVersion: 1, arbiter: "unavailable", mine: [], others: [] });
|
|
126
|
+
return Promise.resolve();
|
|
127
|
+
}
|
|
128
|
+
return Promise.resolve(props.fetchView()).then(
|
|
129
|
+
(next) => { if (next && typeof next === "object") setView(next); },
|
|
130
|
+
// A failed read is the degraded state: the panel cannot vouch
|
|
131
|
+
// for anything, so it says so instead of showing an empty list.
|
|
132
|
+
() => { setView({ schemaVersion: 1, arbiter: "unavailable", mine: [], others: [] }); }
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
react.useEffect(() => {
|
|
136
|
+
if (!sessionId) {
|
|
137
|
+
setView(null);
|
|
138
|
+
setOpen(false);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
read();
|
|
142
|
+
return undefined;
|
|
143
|
+
}, [sessionId]);
|
|
144
|
+
// Close on Escape and on any pointerdown outside this entry's wrapper:
|
|
145
|
+
// the same dismissal the other composer surfaces use, and the only way
|
|
146
|
+
// out of the panel that needs no action model behind it.
|
|
147
|
+
react.useEffect(() => {
|
|
148
|
+
if (!open || typeof document === "undefined") return undefined;
|
|
149
|
+
const onPointerDown = (event) => {
|
|
150
|
+
if (event && typeof event.target?.closest === "function" && event.target.closest("[data-weir-file-lease-root]")) return;
|
|
151
|
+
setOpen(false);
|
|
152
|
+
};
|
|
153
|
+
const onKeyDown = (event) => {
|
|
154
|
+
if (event?.key === "Escape") setOpen(false);
|
|
155
|
+
};
|
|
156
|
+
document.addEventListener("pointerdown", onPointerDown);
|
|
157
|
+
document.addEventListener("keydown", onKeyDown);
|
|
158
|
+
return () => {
|
|
159
|
+
document.removeEventListener("pointerdown", onPointerDown);
|
|
160
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
161
|
+
};
|
|
162
|
+
}, [open]);
|
|
163
|
+
if (!sessionId) return null;
|
|
164
|
+
// Before the first read settles the entry shows the idle state:
|
|
165
|
+
// ownership does not survive the process (D10), so "nothing held" is
|
|
166
|
+
// the honest answer for a session that has not been read yet.
|
|
167
|
+
const state = view === null ? STATE_NONE : stateOf(view);
|
|
168
|
+
const mine = rowsOf(view, "mine");
|
|
169
|
+
const others = rowsOf(view, "others");
|
|
170
|
+
// The badge suffix: a fact when there is one, nothing when there is
|
|
171
|
+
// not. An idle badge never says "not holding" — it simply is idle.
|
|
172
|
+
const suffix = state === STATE_HOLDING
|
|
173
|
+
? t("fileLeaseState_holding").replace("{n}", String(mine.length))
|
|
174
|
+
: state === STATE_DEGRADED ? t("fileLeaseState_degraded") : "";
|
|
175
|
+
const arbiter = view && typeof view.arbiter === "string" ? view.arbiter : "unavailable";
|
|
176
|
+
const at = Date.now();
|
|
177
|
+
const toggle = () => {
|
|
178
|
+
const next = !open;
|
|
179
|
+
setOpen(next);
|
|
180
|
+
if (next) read();
|
|
181
|
+
};
|
|
182
|
+
const row = (entry, key, trailing) => react_jsx_runtime.jsxs("div", {
|
|
183
|
+
style: rowStyle,
|
|
184
|
+
"data-weir-file-lease-row": key,
|
|
185
|
+
children: [
|
|
186
|
+
react_jsx_runtime.jsx("span", { style: nameStyle, title: entry.path, children: entry.path }),
|
|
187
|
+
...trailing
|
|
188
|
+
]
|
|
189
|
+
}, `${key}:${entry.path}`);
|
|
190
|
+
const mineRows = mine.map((entry) => {
|
|
191
|
+
const trailing = [];
|
|
192
|
+
const minutes = retainedMinutes(entry.holdUntil, at);
|
|
193
|
+
if (minutes !== null) {
|
|
194
|
+
trailing.push(react_jsx_runtime.jsx("span", { style: mutedStyle, "data-weir-file-lease-retained": entry.path, children: t("fileLeaseRetainedFor").replace("{n}", String(minutes)) }, "retained"));
|
|
195
|
+
}
|
|
196
|
+
return row(entry, "mine", trailing);
|
|
197
|
+
});
|
|
198
|
+
const otherRows = others.map((entry) => {
|
|
199
|
+
const label = entry.holder && typeof entry.holder.label === "string" ? entry.holder.label : "";
|
|
200
|
+
return row(entry, "others", [react_jsx_runtime.jsx("span", { style: mutedStyle, "data-weir-file-lease-holder": entry.path, children: label }, "holder")]);
|
|
201
|
+
});
|
|
202
|
+
// The panel is assembled as blocks so that spacing, and the existence
|
|
203
|
+
// of a heading, both follow from what is actually there.
|
|
204
|
+
const blocks = [];
|
|
205
|
+
if (state === STATE_DEGRADED) {
|
|
206
|
+
blocks.push(react_jsx_runtime.jsx("div", {
|
|
207
|
+
style: noticeStyle,
|
|
208
|
+
"data-weir-file-lease-arbiter": arbiter,
|
|
209
|
+
children: t("fileLeaseDegradedHint")
|
|
210
|
+
}, "notice"));
|
|
211
|
+
}
|
|
212
|
+
const head = (key, text) => react_jsx_runtime.jsx("div", {
|
|
213
|
+
style: blocks.length === 0 ? headStyle : { ...headStyle, marginTop: "10px" },
|
|
214
|
+
children: text
|
|
215
|
+
}, `${key}-head`);
|
|
216
|
+
if (mineRows.length > 0) {
|
|
217
|
+
blocks.push(head("mine", t("fileLeaseMine")));
|
|
218
|
+
blocks.push(react_jsx_runtime.jsx("div", { "data-weir-file-lease-section": "mine", children: mineRows }, "mine"));
|
|
219
|
+
}
|
|
220
|
+
if (otherRows.length > 0) {
|
|
221
|
+
blocks.push(head("others", t("fileLeaseOthers")));
|
|
222
|
+
blocks.push(react_jsx_runtime.jsx("div", { "data-weir-file-lease-section": "others", children: otherRows }, "others"));
|
|
223
|
+
}
|
|
224
|
+
// Nothing held anywhere: one plain line, and (when protection is
|
|
225
|
+
// degraded) one notice above it — never a section skeleton.
|
|
226
|
+
if (mineRows.length === 0 && otherRows.length === 0) {
|
|
227
|
+
blocks.push(react_jsx_runtime.jsx("div", {
|
|
228
|
+
style: blocks.length === 0 ? mutedStyle : { ...mutedStyle, marginTop: "6px" },
|
|
229
|
+
"data-weir-file-lease-empty": "",
|
|
230
|
+
children: t("fileLeaseEmpty")
|
|
231
|
+
}, "empty"));
|
|
232
|
+
}
|
|
233
|
+
return react_jsx_runtime.jsxs("span", {
|
|
234
|
+
style: rootStyle,
|
|
235
|
+
"data-weir-file-lease-root": "",
|
|
236
|
+
children: [
|
|
237
|
+
react_jsx_runtime.jsxs("button", {
|
|
238
|
+
type: "button",
|
|
239
|
+
style: badgeStyle,
|
|
240
|
+
onClick: toggle,
|
|
241
|
+
"aria-expanded": open,
|
|
242
|
+
"data-weir-file-lease": "",
|
|
243
|
+
"data-weir-file-lease-state": state,
|
|
244
|
+
title: t("fileLeaseTitle"),
|
|
245
|
+
children: [
|
|
246
|
+
dot(state),
|
|
247
|
+
react_jsx_runtime.jsx("span", { children: t("fileLeaseLabel") }),
|
|
248
|
+
suffix === "" ? null : react_jsx_runtime.jsx("span", { "data-weir-file-lease-summary": "", children: suffix })
|
|
249
|
+
]
|
|
250
|
+
}),
|
|
251
|
+
open ? react_jsx_runtime.jsx("div", {
|
|
252
|
+
role: "dialog",
|
|
253
|
+
"aria-label": t("fileLeasePanelTitle"),
|
|
254
|
+
style: panelStyle,
|
|
255
|
+
"data-weir-file-lease-panel": "",
|
|
256
|
+
onKeyDown: (event) => { if (event?.key === "Escape") setOpen(false); },
|
|
257
|
+
children: blocks
|
|
258
|
+
}) : null
|
|
259
|
+
]
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// Locale data face (pure data, NO ctx calls — a chunk factory re-executes
|
|
263
|
+
// under HMR, so a chunk-local register would throw on the duplicate
|
|
264
|
+
// (namespace, locale) pair): the entry registers these under
|
|
265
|
+
// "settings.weir.file-lease" inside a ctx.effect when this chunk arrives
|
|
266
|
+
// (lib/client.js registerChunkDictionaries). Pinned by test/client.test.js's
|
|
267
|
+
// dictionary union pin.
|
|
268
|
+
//
|
|
269
|
+
// Copy rules: plain user-facing words only — no internal vocabulary, no
|
|
270
|
+
// "read-only" disclaimers, no status word for the idle state. Badge
|
|
271
|
+
// suffixes carry their own leading separator so the badge reads as one
|
|
272
|
+
// string in both locales.
|
|
273
|
+
const dictionaries = {
|
|
274
|
+
en: {
|
|
275
|
+
fileLeaseLabel: "Edit Lock",
|
|
276
|
+
fileLeaseTitle: "Files in use",
|
|
277
|
+
fileLeasePanelTitle: "Files in use",
|
|
278
|
+
fileLeaseState_holding: " · {n}",
|
|
279
|
+
fileLeaseState_degraded: " · unprotected",
|
|
280
|
+
fileLeaseMine: "You are editing",
|
|
281
|
+
fileLeaseOthers: "Another session is editing",
|
|
282
|
+
fileLeaseEmpty: "No files in use.",
|
|
283
|
+
fileLeaseDegradedHint: "Protection is temporarily unavailable; editing continues.",
|
|
284
|
+
fileLeaseRetainedFor: "{n} min left"
|
|
285
|
+
},
|
|
286
|
+
zh: {
|
|
287
|
+
fileLeaseLabel: "编辑锁",
|
|
288
|
+
fileLeaseTitle: "文件占用",
|
|
289
|
+
fileLeasePanelTitle: "文件占用",
|
|
290
|
+
fileLeaseState_holding: " · {n}",
|
|
291
|
+
fileLeaseState_degraded: " · 未加锁",
|
|
292
|
+
fileLeaseMine: "你正在编辑",
|
|
293
|
+
fileLeaseOthers: "其他会话正在编辑",
|
|
294
|
+
fileLeaseEmpty: "暂无文件占用。",
|
|
295
|
+
fileLeaseDegradedHint: "保护暂不可用,编辑不受影响。",
|
|
296
|
+
fileLeaseRetainedFor: "保留 {n} 分钟"
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
exports.dictionaries = dictionaries;
|
|
301
|
+
exports.FileLeaseBadge = FileLeaseBadge;
|
|
302
|
+
exports.stateOf = stateOf;
|
|
303
|
+
exports.rowsOf = rowsOf;
|
|
304
|
+
exports.retainedMinutes = retainedMinutes;
|
|
305
|
+
return module.exports;
|
|
306
|
+
}
|
|
307
|
+
});
|
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.
|
|
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.file-lease-badge.js":"cd30546307c91c94fe05484557e46f58c53fe55657f62b0a0415a072dfb3b582","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":"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) => {
|
|
@@ -122,7 +122,7 @@ window.__ModuleLoader__.load({
|
|
|
122
122
|
register: null,
|
|
123
123
|
lspToggle: null,
|
|
124
124
|
hashEdit: null,
|
|
125
|
-
|
|
125
|
+
fileLease: null,
|
|
126
126
|
worktree: null,
|
|
127
127
|
};
|
|
128
128
|
// ---- Package-local chunk wiring (composition root) ----
|
|
@@ -218,15 +218,15 @@ window.__ModuleLoader__.load({
|
|
|
218
218
|
if (!arrival?.chunks) return null;
|
|
219
219
|
return react_jsx_runtime.jsx(arrival.chunks.LspToggle, { ...props, settingsBus, t: chunkDictionaries.lspToggle ?? props.t });
|
|
220
220
|
}
|
|
221
|
-
const
|
|
222
|
-
chunkDictionaries.register?.("settings.weir.
|
|
221
|
+
const loadFileLeaseChunk = lazyChunks(() => require.async("./client.file-lease-badge.js").then((chunk) => {
|
|
222
|
+
chunkDictionaries.register?.("settings.weir.file-lease", chunk.dictionaries);
|
|
223
223
|
return chunk;
|
|
224
224
|
}));
|
|
225
|
-
/** Composer-bar
|
|
226
|
-
function
|
|
227
|
-
const arrival = useChunkArrival(
|
|
225
|
+
/** Composer-bar file-lease entry: nothing until its chunk arrives. */
|
|
226
|
+
function FileLeaseBadgeWrapper(props) {
|
|
227
|
+
const arrival = useChunkArrival(loadFileLeaseChunk, typeof props.sessionId === "string" && props.sessionId !== "");
|
|
228
228
|
if (!arrival?.chunks) return null;
|
|
229
|
-
return react_jsx_runtime.jsx(arrival.chunks.
|
|
229
|
+
return react_jsx_runtime.jsx(arrival.chunks.FileLeaseBadge, { ...props, t: chunkDictionaries.fileLease ?? props.t });
|
|
230
230
|
}
|
|
231
231
|
// ---- Tab glyphs (ui-entries-and-icons) ----
|
|
232
232
|
// Three 16x16 stroke glyphs drawn in currentColor, matching the host
|
|
@@ -427,7 +427,7 @@ window.__ModuleLoader__.load({
|
|
|
427
427
|
chunkDictionaries.register = registerChunkDictionaries;
|
|
428
428
|
chunkDictionaries.lspToggle = ctx.locale.bind("settings.weir.lsp-toggle");
|
|
429
429
|
chunkDictionaries.hashEdit = ctx.locale.bind("settings.weir.hash-edit");
|
|
430
|
-
chunkDictionaries.
|
|
430
|
+
chunkDictionaries.fileLease = ctx.locale.bind("settings.weir.file-lease");
|
|
431
431
|
chunkDictionaries.worktree = ctx.locale.bind("settings.weir.worktree");
|
|
432
432
|
const pageT = ctx.locale.bind("settings.weir.page");
|
|
433
433
|
const capabilityT = ctx.locale.bind("settings.weir.capability");
|
|
@@ -619,28 +619,20 @@ window.__ModuleLoader__.load({
|
|
|
619
619
|
key: "hash_edit",
|
|
620
620
|
locale: NS
|
|
621
621
|
}, HashEditToolView)), "ui-weir-settings: hash_edit toolview");
|
|
622
|
-
//
|
|
623
|
-
//
|
|
622
|
+
// File-lease ownership entry (edit-lock rebuild D13): the read-only
|
|
623
|
+
// badge of the file-lease feature. It carries NO action verb and no
|
|
624
|
+
// command bridge — every ownership state resolves by itself (turn end,
|
|
625
|
+
// expiry, negotiation) — and its single read is the read-only view
|
|
626
|
+
// endpoint, so nothing in this surface can write to the conversation.
|
|
624
627
|
ctx.effect(() => ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
625
628
|
name: "conversation.input.right",
|
|
626
|
-
id: "weir-
|
|
629
|
+
id: "weir-file-lease",
|
|
627
630
|
order: 101,
|
|
628
631
|
locale: NS,
|
|
629
632
|
inject: (sessionId) => {
|
|
630
633
|
if (!sessionId) return {};
|
|
631
634
|
return {
|
|
632
635
|
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
636
|
// Structured view: read-only, never written to the conversation.
|
|
645
637
|
fetchView: async () => {
|
|
646
638
|
const result = await httpChannel("api/weir-edit-lock/view", { sessionId });
|
|
@@ -649,7 +641,7 @@ window.__ModuleLoader__.load({
|
|
|
649
641
|
}
|
|
650
642
|
};
|
|
651
643
|
}
|
|
652
|
-
},
|
|
644
|
+
}, FileLeaseBadgeWrapper)), "ui-weir-settings: file lease badge");
|
|
653
645
|
// ---- Worktree lane surfaces ----
|
|
654
646
|
// Each surface registers in its own effect so one failure cannot take
|
|
655
647
|
// down the others (same discipline as the hash_edit view). Reads go to
|
|
@@ -1064,15 +1056,14 @@ window.__ModuleLoader__.load({
|
|
|
1064
1056
|
require.async("./client.chain-model.js"),
|
|
1065
1057
|
require.async("./client.robash-model.js"),
|
|
1066
1058
|
require.async("./client.lsp-model.js"),
|
|
1067
|
-
require.async("./client.notify-permissions.js")
|
|
1068
|
-
|
|
1069
|
-
]).then(([settingsPage, chainEditor, robashEditor, disabledCategoriesEditor, lspPanel, chainModel, robashModel, lspModel, notifyPermissions, editLockMaintenance]) => {
|
|
1059
|
+
require.async("./client.notify-permissions.js")
|
|
1060
|
+
]).then(([settingsPage, chainEditor, robashEditor, disabledCategoriesEditor, lspPanel, chainModel, robashModel, lspModel, notifyPermissions]) => {
|
|
1070
1061
|
// The whole settings card tree shares one t (the page
|
|
1071
1062
|
// namespace): every batch chunk's data face merges into ONE
|
|
1072
1063
|
// registration — a duplicate (namespace, locale) pair would
|
|
1073
1064
|
// throw, so per-feature namespaces are not an option here.
|
|
1074
1065
|
const pageDictionaries = { en: {}, zh: {} };
|
|
1075
|
-
for (const chunk of [settingsPage, chainModel, robashModel, lspModel, disabledCategoriesEditor, notifyPermissions
|
|
1066
|
+
for (const chunk of [settingsPage, chainModel, robashModel, lspModel, disabledCategoriesEditor, notifyPermissions]) {
|
|
1076
1067
|
Object.assign(pageDictionaries.en, chunk.dictionaries?.en);
|
|
1077
1068
|
Object.assign(pageDictionaries.zh, chunk.dictionaries?.zh);
|
|
1078
1069
|
}
|
|
@@ -1094,8 +1085,7 @@ window.__ModuleLoader__.load({
|
|
|
1094
1085
|
// The HTTP channel adapter reaches every fetch-talking editor
|
|
1095
1086
|
// through the same prop (composition-root injection: a chunk
|
|
1096
1087
|
// 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 })
|
|
1088
|
+
NotifyPermissionsField: (editorProps) => react_jsx_runtime.jsx(notifyPermissions.NotifyPermissionsField, { ...editorProps, channel: httpChannel })
|
|
1099
1089
|
};
|
|
1100
1090
|
return { settingsPage, editors };
|
|
1101
1091
|
}, (error) => {
|
|
@@ -47,7 +47,7 @@ window.__ModuleLoader__.load({
|
|
|
47
47
|
// scatter). test/client-settings-page.test.js pins this list to
|
|
48
48
|
// RESTART_KEYS from src/settings/sections.js, so a declaration
|
|
49
49
|
// change on either side cannot drift the two sides.
|
|
50
|
-
const RESTART_FIELDS = ["intentGateClassifier", "intentGateProvider", "intentGateModel", "intentGateReasoningEffort", "intentGateTimeoutMs", "jevEndpoint", "jevModel", "jevApiKeyEnv", "todoEnabled", "todoMaxConsecutive", "todoErrorRetryMax", "todoErrorBackoffBaseMs", "todoErrorBackoffCapMs", "hashlineHideStockEdit"
|
|
50
|
+
const RESTART_FIELDS = ["intentGateClassifier", "intentGateProvider", "intentGateModel", "intentGateReasoningEffort", "intentGateTimeoutMs", "jevEndpoint", "jevModel", "jevApiKeyEnv", "todoEnabled", "todoMaxConsecutive", "todoErrorRetryMax", "todoErrorBackoffBaseMs", "todoErrorBackoffCapMs", "hashlineHideStockEdit"];
|
|
51
51
|
// ---- condition declaration shorthands (frozen pure data, D2) ----
|
|
52
52
|
const enabledWhen = (key) => Object.freeze({ key, equals: true });
|
|
53
53
|
const CLASSIFIER_LLM = Object.freeze({ key: "intentGateClassifier", equals: "llm" });
|
|
@@ -107,10 +107,8 @@ window.__ModuleLoader__.load({
|
|
|
107
107
|
] },
|
|
108
108
|
{ id: "editing", fields: [
|
|
109
109
|
{ field: "hashlineHideStockEdit", kind: "boolean" },
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{ field: "editLockStaleSweep", kind: "boolean", parent: "editLockEnabled", when: enabledWhen("editLockEnabled") },
|
|
113
|
-
// Diagnostics must remain reachable when enforcement is off.
|
|
110
|
+
// Edit Lock diagnostics seat: the composition root binds no editor
|
|
111
|
+
// for it (edit-lock rebuild), so the row renders nothing.
|
|
114
112
|
{ kind: "custom", slot: "editLockMaintenance" }
|
|
115
113
|
] },
|
|
116
114
|
{ id: "worktree", fields: [
|
|
@@ -194,14 +192,6 @@ window.__ModuleLoader__.load({
|
|
|
194
192
|
guardSoftThreshold: 0.72,
|
|
195
193
|
guardHardThreshold: 0.88,
|
|
196
194
|
hashlineHideStockEdit: true,
|
|
197
|
-
editLockEnabled: true,
|
|
198
|
-
editLockHoldDefaultMinutes: 30,
|
|
199
|
-
editLockHoldSingleMaxMinutes: 30,
|
|
200
|
-
editLockHoldCumulativeMaxMinutes: 120,
|
|
201
|
-
editLockNudgeAttempts: 2,
|
|
202
|
-
editLockNudgeFallback: "release",
|
|
203
|
-
editLockAutoResume: true,
|
|
204
|
-
editLockStaleSweep: true,
|
|
205
195
|
robashEnabled: true,
|
|
206
196
|
lspEnabled: false,
|
|
207
197
|
lspIdleMs: 600000,
|
|
@@ -597,7 +587,6 @@ window.__ModuleLoader__.load({
|
|
|
597
587
|
const childLabelStyle = { ...labelStyle, fontSize: "13px" };
|
|
598
588
|
const labelTextStyle = { display: "inline-flex", alignItems: "center", gap: "6px" };
|
|
599
589
|
const hintStyle = { fontSize: "12px", lineHeight: "16px", color: "var(--dsw-alias-label-secondary)" };
|
|
600
|
-
const optOutStyle = { ...hintStyle, color: "var(--dsw-alias-state-warn-primary)", borderLeft: "2px solid var(--dsw-alias-state-warn-primary)", paddingLeft: "8px", marginTop: "4px" };
|
|
601
590
|
// Card-based group sections (design D7): layered background, l1 border,
|
|
602
591
|
// radius, the group title inside the card top, hairline l2 separators
|
|
603
592
|
// between rows. Colors only from theme tokens; geometry hardcoded.
|
|
@@ -637,9 +626,6 @@ window.__ModuleLoader__.load({
|
|
|
637
626
|
react_jsx_runtime.jsxs("div", { style: labelGroupStyle, children: [
|
|
638
627
|
labelWithTag(descriptor, t, depth > 0 ? childLabelStyle : labelStyle),
|
|
639
628
|
react_jsx_runtime.jsx("span", { style: hintStyle, children: t(`${descriptor.field}Hint`) }),
|
|
640
|
-
descriptor.field === "editLockEnabled" && resolveEffectiveValue(descriptor, field, field?.saved) !== true
|
|
641
|
-
? react_jsx_runtime.jsx("span", { style: optOutStyle, role: "alert", children: t("editLockOptOutWarning") })
|
|
642
|
-
: null
|
|
643
629
|
] }),
|
|
644
630
|
react_jsx_runtime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "4px" }, children: [
|
|
645
631
|
descriptor.kind === "boolean" ? react_jsx_runtime.jsx(primitives.Switch, {
|
|
@@ -925,9 +911,13 @@ window.__ModuleLoader__.load({
|
|
|
925
911
|
}));
|
|
926
912
|
}
|
|
927
913
|
if (node.slot === "editLockMaintenance") {
|
|
928
|
-
// Edit Lock maintenance
|
|
929
|
-
//
|
|
930
|
-
|
|
914
|
+
// The Edit Lock maintenance seat is GONE (edit-lock rebuild):
|
|
915
|
+
// the composition root no longer binds an editor for it, and
|
|
916
|
+
// the row disappears with the settings keys. An unbound seat
|
|
917
|
+
// must leave the card intact instead of rendering an undefined
|
|
918
|
+
// element type, so the row simply renders nothing here.
|
|
919
|
+
const MaintenanceField = props.editors?.EditLockMaintenanceField;
|
|
920
|
+
return typeof MaintenanceField === "function" ? wrap(react_jsx_runtime.jsx(MaintenanceField, { t })) : null;
|
|
931
921
|
}
|
|
932
922
|
// notifyPermissions: the macOS-only permission entry
|
|
933
923
|
return wrap(react_jsx_runtime.jsx(props.editors.NotifyPermissionsField, { t }));
|
|
@@ -1113,13 +1103,6 @@ window.__ModuleLoader__.load({
|
|
|
1113
1103
|
guardHardThresholdHint: "Above this fill ratio compaction is requested (default 0.88; not a completion guarantee).",
|
|
1114
1104
|
hashlineHideStockEdit: "Anchor editing only",
|
|
1115
1105
|
hashlineHideStockEditHint: "Hides the built-in edit tool; file edits go through hash_edit anchors only.",
|
|
1116
|
-
editLockEnabled: "Edit Lock",
|
|
1117
|
-
editLockEnabledHint: "Sessions take turns editing the same files instead of overwriting each other.",
|
|
1118
|
-
editLockOptOutWarning: "Edit Lock is set to off. After saving, restart DeepSeek Harness to apply the change. Once enforcement is off, ordinary editing is UNPROTECTED and can overwrite another session's work. Old fences and history are retained and may block files when you re-enable Edit Lock.",
|
|
1119
|
-
editLockAutoResume: "Message resumes editing",
|
|
1120
|
-
editLockAutoResumeHint: "After a session is stopped, sending any message restores its editing right and re-confirms its kept files (default on).",
|
|
1121
|
-
editLockStaleSweep: "Sweep stale locks",
|
|
1122
|
-
editLockStaleSweepHint: "Locks whose target file was deleted or moved are re-verified and released as you send messages (default on).",
|
|
1123
1106
|
worktreeEnabled: "Worktree lanes",
|
|
1124
1107
|
worktreeEnabledHint: "Lets the assistant work in isolated git worktrees: lane tools, the /worktree command, and the lanes panel.",
|
|
1125
1108
|
worktreeAutoSetup: "Auto-install in new lanes",
|
|
@@ -1248,13 +1231,6 @@ window.__ModuleLoader__.load({
|
|
|
1248
1231
|
guardHardThresholdHint: "上下文用量超过该比例时请求立即压缩(默认 0.88,不保证压缩完成)。",
|
|
1249
1232
|
hashlineHideStockEdit: "仅用锚点编辑",
|
|
1250
1233
|
hashlineHideStockEditHint: "隐藏内置 edit 工具,文件修改一律走 hash_edit 锚点编辑。",
|
|
1251
|
-
editLockEnabled: "编辑锁",
|
|
1252
|
-
editLockEnabledHint: "多个会话轮流编辑同一批文件,而不是互相覆盖。",
|
|
1253
|
-
editLockOptOutWarning: "编辑锁已设为关闭。保存后须重启 DeepSeek Harness 才会生效。停用强制执行后,普通编辑不受保护,可能覆盖其他会话的工作。旧围栏与历史仍会保留,重新启用编辑锁时可能再次阻塞文件。",
|
|
1254
|
-
editLockAutoResume: "发消息即恢复编辑",
|
|
1255
|
-
editLockAutoResumeHint: "会话被停止后,发送任意消息即自动恢复其编辑权并确认保留的文件(默认开启)。",
|
|
1256
|
-
editLockStaleSweep: "自动清理失效锁",
|
|
1257
|
-
editLockStaleSweepHint: "目标文件已被删除或移动的锁,在你发消息时自动复核并释放(默认开启)。",
|
|
1258
1234
|
worktreeEnabled: "Worktree 车道",
|
|
1259
1235
|
worktreeEnabledHint: "让助手在隔离的 git worktree 中工作:车道工具、/worktree 命令与车道面板。",
|
|
1260
1236
|
worktreeAutoSetup: "新车道自动安装依赖",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weir-harness",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Weir \u2014 orchestrator-centric DSH agent preset: intent gate, category delegation, todo driver, pull-based background jobs, auto-compaction, hashline edit.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"./todo-driver": "./src/todo-driver/index.js",
|
|
44
44
|
"./context-guard": "./src/context-guard/index.js",
|
|
45
45
|
"./hashline-edit": "./src/hashline-edit/index.js",
|
|
46
|
-
"./
|
|
46
|
+
"./file-lease": "./src/file-lease/plugin.js",
|
|
47
47
|
"./blackboard": "./src/blackboard/index.js",
|
|
48
48
|
"./blackboard-remote": "./src/blackboard/remote.js",
|
|
49
49
|
"./worktree": "./src/worktree/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Augmentations of the narrow checked-graph platform contract
|
|
2
|
-
// (types/platform.d.ts) for the capability-store liveness/lock modules:
|
|
3
|
-
//
|
|
2
|
+
// (types/platform.d.ts) for the capability-store liveness/lock modules: they
|
|
3
|
+
// pull these declarations into the checked graph.
|
|
4
4
|
// Same discipline as the base contract — runtime modules remain supplied by
|
|
5
5
|
// Node; these declarations only describe what the checked graph uses.
|
|
6
6
|
declare module 'node:fs' {
|
|
@@ -21,9 +21,8 @@ import { link, mkdir, open, readFile, readdir, rename, unlink } from 'node:fs/pr
|
|
|
21
21
|
// Directory fsync is a POSIX crash-durability idiom: on Windows the directory
|
|
22
22
|
// handle opens but sync fails EPERM (verified on Node v24, NTFS). The win32
|
|
23
23
|
// store row (SUPPORTED_PLATFORMS in store.js) therefore commits with
|
|
24
|
-
// file-level fsync + atomic rename only
|
|
25
|
-
//
|
|
26
|
-
// durability tradeoff is documented in docs/features/session-capability-manager.md.
|
|
24
|
+
// file-level fsync + atomic rename only. The durability tradeoff is documented
|
|
25
|
+
// in docs/features/session-capability-manager.md.
|
|
27
26
|
const DIR_SYNC_SUPPORTED = process.platform === 'darwin' || process.platform === 'linux'
|
|
28
27
|
|
|
29
28
|
// Windows file-locking etiquette: antivirus and the search indexer briefly
|