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.
- package/cordis.patch.yml +7 -18
- package/lib/client.capability-badge.js +2 -0
- package/lib/client.capability-manager.js +46 -179
- package/lib/client.capability-model.js +428 -3
- package/lib/client.file-lease-badge.js +307 -0
- package/lib/client.file-lease-tool-model.js +189 -0
- package/lib/client.file-lease-tool-view.js +434 -0
- package/lib/client.js +124 -36
- package/lib/client.mcp-manager.js +563 -0
- package/lib/client.settings-page.js +10 -34
- package/package.json +2 -2
- package/skills/mcp-server-setup/SKILL.md +124 -0
- package/src/capabilities/apply-engine.js +18 -4
- package/src/capabilities/capability-remote.js +296 -45
- package/src/capabilities/initial-selection.js +3 -2
- package/src/capabilities/lifecycle-preload.js +69 -11
- package/src/capabilities/lifecycle-snapshot.js +127 -39
- package/src/capabilities/mcp-admission.js +2 -2
- package/src/capabilities/mcp-config.js +344 -0
- package/src/capabilities/mcp-credentials.js +180 -0
- package/src/capabilities/mcp-diagnostics.js +169 -0
- package/src/capabilities/mcp-drain.js +18 -4
- package/src/capabilities/mcp-facade.js +3 -3
- package/src/capabilities/mcp-gate.js +51 -52
- package/src/capabilities/mcp-install-tool.js +276 -0
- package/src/capabilities/mcp-manager.js +368 -142
- package/src/capabilities/mcp-mount.js +219 -79
- package/src/capabilities/mcp-operations.js +359 -0
- package/src/capabilities/mcp-process.js +43 -0
- package/src/capabilities/mcp-registry.js +74 -60
- package/src/capabilities/mcp-runtime.js +103 -0
- package/src/capabilities/read-payloads.js +8 -3
- package/src/capabilities/skill-applicability.js +185 -0
- package/src/capabilities/skill-builtin-migration.js +2 -1
- package/src/capabilities/skill-selection-plugin.js +161 -56
- 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 +1299 -0
- package/src/file-lease/index.js +34 -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 +1013 -0
- package/src/file-lease/singleton.js +155 -0
- package/src/file-lease/spawn.js +62 -0
- package/src/file-lease/tools.js +847 -0
- package/src/file-lease/view.js +144 -0
- package/src/file-lease/watch.js +473 -0
- package/src/hashline-edit/index.js +76 -42
- package/src/hashline-edit/write-tool.js +90 -0
- package/src/intent-gate/index.js +29 -12
- package/src/intent-gate/matcher.js +36 -2
- package/src/lease-broker/entry.js +98 -0
- package/src/lease-broker/node-contracts.d.ts +63 -0
- package/src/lease-broker/protocol.js +237 -0
- package/src/lease-broker/server.js +402 -0
- package/src/lease-broker/state.js +482 -0
- package/src/lsp/tools.js +137 -27
- package/src/process/index.js +4 -2
- package/src/settings/index.js +2 -44
- package/src/settings/sections.js +2 -24
- package/src/shared/audit.js +9 -1
- package/src/shared/child-scope.js +2 -0
- 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/src/worktree/rules.js +1 -1
- 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
|
@@ -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": "1.
|
|
3
|
+
"version": "2.1.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",
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mcp-server-setup
|
|
3
|
+
description: "Install, add, or configure a new MCP server with credential-safe preview, exact Host confirmation, strict discovery, and separate session Apply. Do not use for merely calling or discussing an existing MCP tool."
|
|
4
|
+
metadata:
|
|
5
|
+
short-description: Credential-safe MCP server installation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MCP Server Setup
|
|
9
|
+
|
|
10
|
+
Use this workflow for genuine requests to install, add, or configure a new MCP
|
|
11
|
+
server. It is not a tool-repair or general MCP discussion workflow.
|
|
12
|
+
|
|
13
|
+
## Boundaries
|
|
14
|
+
|
|
15
|
+
- Weir supports `stdio` and `streamable-http` only. Do not propose legacy SSE,
|
|
16
|
+
OAuth flows, a marketplace, or adoption of an unmanaged server.
|
|
17
|
+
- Never ask the user to paste a token, password, private endpoint, or other
|
|
18
|
+
secret into chat. Use an existing DSH credential reference such as
|
|
19
|
+
`{ "ref": "GITHUB_TOKEN" }`. A reference may include a non-secret `prefix`.
|
|
20
|
+
- Every environment or HTTP-header value on this tool surface is a credential
|
|
21
|
+
reference. Mark each sensitive stdio argument with a reference object instead
|
|
22
|
+
of placing its value in a plain argument string.
|
|
23
|
+
- Any URL containing userinfo, a query, or a fragment is sensitive. Supply the
|
|
24
|
+
whole URL through a credential reference; do not put it in the plan.
|
|
25
|
+
- Installation never selects the server, changes a default, or Applies a
|
|
26
|
+
capability set. Do not imply that a mounted server is already authorized.
|
|
27
|
+
|
|
28
|
+
## 1. Establish the plan
|
|
29
|
+
|
|
30
|
+
Read the server author's current documentation when it is available. Identify
|
|
31
|
+
the transport, executable or endpoint, non-secret arguments, required credential
|
|
32
|
+
references, working directory, and relevant timeout/reconnect options. Keep the
|
|
33
|
+
plan within the `mcp_install` schema. Unknown fields and cross-transport fields
|
|
34
|
+
are rejected.
|
|
35
|
+
|
|
36
|
+
For stdio, `command` and non-secret `args` describe the process. For
|
|
37
|
+
`streamable-http`, use `url` and credential-referenced `headers`; stdio fields
|
|
38
|
+
are forbidden. `failOnStartupError` is not configurable: initial mounting is
|
|
39
|
+
always strict. Discovering zero tools is still a valid result.
|
|
40
|
+
|
|
41
|
+
Example stdio plan shape (references, not secrets):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"action": "preview",
|
|
46
|
+
"plan": {
|
|
47
|
+
"identity": "github",
|
|
48
|
+
"label": "GitHub",
|
|
49
|
+
"transport": "stdio",
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "@example/mcp-server", { "ref": "MCP_CLI_TOKEN" }],
|
|
52
|
+
"env": { "GITHUB_TOKEN": { "ref": "GITHUB_TOKEN" } }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Example HTTP plan shape:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"action": "preview",
|
|
62
|
+
"plan": {
|
|
63
|
+
"identity": "private-docs",
|
|
64
|
+
"transport": "streamable-http",
|
|
65
|
+
"url": { "ref": "PRIVATE_MCP_URL" },
|
|
66
|
+
"headers": {
|
|
67
|
+
"Authorization": { "ref": "PRIVATE_MCP_TOKEN", "prefix": "Bearer " }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 2. Preview before installation
|
|
74
|
+
|
|
75
|
+
Call `mcp_install` with `action: "preview"` and the plan. Check the bounded
|
|
76
|
+
redacted plan, digest, target conflicts, namespace conflicts, capabilities, and
|
|
77
|
+
validation outcome. Preview does not start a process, contact the server, write
|
|
78
|
+
credentials or configuration, or change selection. Correct the plan and preview
|
|
79
|
+
again when necessary.
|
|
80
|
+
|
|
81
|
+
`mcp_install` is single-server and fresh-identity only. Do not use it to edit,
|
|
82
|
+
restart, remove, batch-import, or set a default. Use the management UI or the
|
|
83
|
+
shared `/capabilities` MCP commands for those operations.
|
|
84
|
+
|
|
85
|
+
## 3. Let the Host obtain approval
|
|
86
|
+
|
|
87
|
+
After a clean preview, call `mcp_install` with `action: "install"` and the same
|
|
88
|
+
plan. The Host displays the exact redacted plan, its digest, and process or
|
|
89
|
+
network effects. Only the user can approve that card. There is no `confirmed`
|
|
90
|
+
argument; chat text, custom answers, and extra selections are not approval.
|
|
91
|
+
|
|
92
|
+
Cancellation, close, timeout, missing confirmation support, delegated callers,
|
|
93
|
+
ownership changes, plan changes, registry changes, and namespace changes stop
|
|
94
|
+
before probe or write. Report the outcome and request a fresh preview when
|
|
95
|
+
required; never fabricate approval or retry an unchanged conflict in a loop.
|
|
96
|
+
|
|
97
|
+
## 4. Interpret the actual outcome
|
|
98
|
+
|
|
99
|
+
An approved install probes before registration. A failed probe does not create
|
|
100
|
+
a registry entry. A mounted result reports the real identity, incarnation,
|
|
101
|
+
generation, and bounded discovery summary.
|
|
102
|
+
|
|
103
|
+
- `mounted`: registration and strict mount succeeded.
|
|
104
|
+
- `saved-degraded`: configuration was saved (`saved: true`) but strict mounting
|
|
105
|
+
failed. This is not installation success; offer Edit, Test, or Restart.
|
|
106
|
+
- `owner-changed` or `indeterminate`: inspect actual registry/mount facts. Never
|
|
107
|
+
claim rollback or zero effects.
|
|
108
|
+
- `cleanup-failed` / `cleanup-unknown`: follow the recovery action; do not start
|
|
109
|
+
an immediate same-name replacement.
|
|
110
|
+
|
|
111
|
+
Do not run an arbitrary discovered tool as an installation test. Discovery and
|
|
112
|
+
the reported mount outcome are the evidence.
|
|
113
|
+
|
|
114
|
+
## 5. Keep authorization separate
|
|
115
|
+
|
|
116
|
+
After a mounted result, direct the user to the separate **Select for this
|
|
117
|
+
session** action and explicit **Apply**. Installation and the optional
|
|
118
|
+
new-session default are independent of the current draft and accepted sets.
|
|
119
|
+
Editing or restarting preserves the registration incarnation; removing and
|
|
120
|
+
re-adding the same identity creates a new one and requires reauthorization.
|
|
121
|
+
|
|
122
|
+
Credential rotation takes effect on the next explicit probe or restart, not as
|
|
123
|
+
a claimed live reconnect. Local credential storage avoids registry/profile and
|
|
124
|
+
transcript disclosure; it is not an isolation boundary against a same-UID agent.
|
|
@@ -203,7 +203,7 @@ export function digestApplyRequest(normalized) {
|
|
|
203
203
|
*/
|
|
204
204
|
export function createApplyEngine(options) {
|
|
205
205
|
const {
|
|
206
|
-
store, locateSession, inventory, provider,
|
|
206
|
+
store, locateSession, inventory, provider, mcpRegistry,
|
|
207
207
|
fence = createAdmissionFence(),
|
|
208
208
|
drain = createDrainCoordinator(),
|
|
209
209
|
conditions = () => [],
|
|
@@ -365,6 +365,19 @@ export function createApplyEngine(options) {
|
|
|
365
365
|
const missing = normalized.skills.filter(key => (byKey.get(key) ?? []).length !== 1)
|
|
366
366
|
if (missing.length) return { status: 'rejected', reason: 'selected-identity-missing', step: 2, missing }
|
|
367
367
|
|
|
368
|
+
let mcpIncarnations
|
|
369
|
+
if (mcpRegistry) {
|
|
370
|
+
let current
|
|
371
|
+
try { current = await mcpRegistry.read() } catch (error) {
|
|
372
|
+
return { status: 'rejected', reason: `mcp-registry-unavailable:${message(error)}`, step: 2 }
|
|
373
|
+
}
|
|
374
|
+
if (current.kind !== 'ok' && current.kind !== 'absent') return { status: 'rejected', reason: `mcp-registry-unavailable:${current.kind}`, step: 2 }
|
|
375
|
+
const servers = current.kind === 'ok' ? current.servers : {}
|
|
376
|
+
const missingMcp = normalized.mcpServers.filter(identity => !servers[identity])
|
|
377
|
+
if (missingMcp.length) return { status: 'rejected', reason: 'unregistered-mcp', step: 2, missing: missingMcp }
|
|
378
|
+
mcpIncarnations = Object.fromEntries(normalized.mcpServers.map(identity => [identity, servers[identity].incarnation ?? null]))
|
|
379
|
+
}
|
|
380
|
+
|
|
368
381
|
// Step 3: prepare a pure snapshot, filtered view and verified content
|
|
369
382
|
// handles. Nothing is published, installed or notified here.
|
|
370
383
|
let prepared
|
|
@@ -388,7 +401,7 @@ export function createApplyEngine(options) {
|
|
|
388
401
|
|
|
389
402
|
const previousSets = record.kind === 'ok' ? safeSets(record.payload) : { skills: [], mcpServers: [] }
|
|
390
403
|
const removedServers = previousSets.mcpServers.filter(server => !normalized.mcpServers.includes(server))
|
|
391
|
-
const payload = { skills: prepared.identities, mcpServers: normalized.mcpServers, origin: 'apply' }
|
|
404
|
+
const payload = { skills: prepared.identities, mcpServers: normalized.mcpServers, ...(mcpIncarnations ? { mcpIncarnations } : {}), origin: 'apply' }
|
|
392
405
|
const requestDigest = digestApplyRequest(normalized)
|
|
393
406
|
|
|
394
407
|
// Step 4: enter the admission fence, close removed-server gates, start draining.
|
|
@@ -496,7 +509,7 @@ export function createApplyEngine(options) {
|
|
|
496
509
|
status: 'applied',
|
|
497
510
|
revision: result.revision,
|
|
498
511
|
receipt: result.receipt,
|
|
499
|
-
effective: { skills: structuredClone(prepared.identities), mcpServers: [...normalized.mcpServers] },
|
|
512
|
+
effective: { skills: structuredClone(prepared.identities), mcpServers: [...normalized.mcpServers], ...(mcpIncarnations ? { mcpIncarnations } : {}) },
|
|
500
513
|
unresolvedWarnings: normalized.unresolved.map(entry => ({ ...entry })),
|
|
501
514
|
...(draining.length ? { draining } : {}),
|
|
502
515
|
...(warnings.length ? { warnings } : {}),
|
|
@@ -564,6 +577,7 @@ export function createApplyEngine(options) {
|
|
|
564
577
|
return {
|
|
565
578
|
skills: Array.isArray(value.skills) ? value.skills : [],
|
|
566
579
|
mcpServers: Array.isArray(value.mcpServers) ? /** @type {string[]} */ (value.mcpServers) : [],
|
|
580
|
+
...(value.mcpIncarnations && typeof value.mcpIncarnations === 'object' && !Array.isArray(value.mcpIncarnations) ? { mcpIncarnations: structuredClone(value.mcpIncarnations) } : {}),
|
|
567
581
|
}
|
|
568
582
|
}
|
|
569
583
|
|
|
@@ -613,7 +627,7 @@ export function createApplyEngine(options) {
|
|
|
613
627
|
// just-committed record — reported, never fatal.
|
|
614
628
|
try {
|
|
615
629
|
const sets = safeSets(payload)
|
|
616
|
-
publishSnapshot?.(sessionId, { revision, skills: structuredClone(sets.skills), mcpServers: [...sets.mcpServers] })
|
|
630
|
+
publishSnapshot?.(sessionId, { revision, skills: structuredClone(sets.skills), mcpServers: [...sets.mcpServers], ...(sets.mcpIncarnations ? { mcpIncarnations: structuredClone(sets.mcpIncarnations) } : {}) })
|
|
617
631
|
} catch (error) {
|
|
618
632
|
degraded('snapshot-publication-degraded', error)
|
|
619
633
|
}
|