@twinsearth/w2m-dsh-plugin 1.7.6 → 1.7.8

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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "The w2m fleet plugins: one account, many machines, any of Windows, macOS or Linux.",
8
- "version": "1.7.6"
8
+ "version": "1.7.8"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "w2m-fleet",
13
13
  "source": "./",
14
14
  "description": "Read fleet state and dispatch commands across the machines registered to one account. Read-only MCP surface: the five exposed tools cannot change anything.",
15
- "version": "1.7.6",
15
+ "version": "1.7.8",
16
16
  "author": {
17
17
  "name": "TwinsEarth"
18
18
  },
@@ -26,5 +26,5 @@
26
26
  ]
27
27
  }
28
28
  ],
29
- "version": "1.7.6"
29
+ "version": "1.7.8"
30
30
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "w2m-fleet",
3
3
  "description": "Drive the same project on online Windows, macOS and Linux machines from one account: read fleet state, dispatch commands and read results. The MCP surface is deliberately read-only.",
4
- "version": "1.7.6",
4
+ "version": "1.7.8",
5
5
  "author": {
6
6
  "name": "TwinsEarth"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -7,6 +7,30 @@
7
7
 
8
8
  All notable changes to this project are documented here.
9
9
 
10
+ ## [1.7.8] — 2026-10-26
11
+
12
+ **GPW 启用失败修复(第二个):`TypeError: Cannot read properties of undefined (reading 'layers')`。**
13
+
14
+ - 根因:v1.7.7 为做黑名单同步拒绝,在**激活期**调用根上下文的 `ctx.tools.guard()`。tools 服务是**按 agent 组合**的,
15
+ 根作用域实例在激活阶段还没有 layer 表,于是抛 `reading 'layers'`,dsh-gpw 仍无法激活。
16
+ - 修复:改为在 `agent/created` 监听里对**每个 agent 自己的上下文** `agent.ctx.tools.guard(...)` 注册黑名单守卫,
17
+ 并用 `ctx.effect` 统一释放;根上下文不再读取 tools 服务(`inject` 回到只有 `permissionPresets`)。
18
+ - 保留 `tools/pre-execute` 三分类拦截;guard 缺失的宿主不会因此激活失败(pre-execute 仍会拒绝黑名单)。
19
+ - 回归测试升级:`test/gpw-activation.test.mjs` 现在用严格 Cordis 语义 Proxy(读取根 `tools` 即抛错)验证激活,
20
+ 并通过伪造 `agent/created` 断言守卫注册在 agent 上下文。
21
+ - 同时补全 bundle patch 中 GPW v2 的配置项(requireFullAccess / whitelist / blacklist / pendingTimeoutMs)。
22
+ - 证据:`docs/acceptance/1.7.8/GPW-AGENT-SCOPE.json`。
23
+ ## [1.7.7] — 2026-10-26
24
+
25
+ **GPW 子插件启用失败修复:`cannot get property "tools" without inject`。**
26
+
27
+ - 根因:`gpw.mjs` 读取 `ctx.tools.guard` 做黑名单同步拦截,但 `inject` 只声明了 `permissionPresets`。
28
+ Cordis 对未声明服务读取直接抛错,插件激活因此失败(组件启用失败:1 entry did not activate dsh-gpw)。
29
+ - 修复:`inject = ['permissionPresets', 'tools']`;guard 读取改为防御式(老宿主无 guard 扩展时不影响
30
+ `tools/pre-execute` 拦截)。
31
+ - 新增回归测试 `test/gpw-activation.test.mjs`:用严格 Cordis 语义的 Proxy 上下文调用 `apply()`,
32
+ 未声明服务读取即抛错;断言 `tools/pre-execute` 监听已安装。
33
+ - 证据:`docs/acceptance/1.7.7/GPW-INJECT.json`。
10
34
  ## [1.7.6] — 2026-10-25
11
35
 
12
36
  **审计修复版:测试零残留工具链 + 修复泄漏的测试夹具,并固化 v1.6.1→v1.7.4 审计报告。**
@@ -12,6 +12,6 @@
12
12
 
13
13
  [mcp_servers.w2m-fleet]
14
14
  command = "npx"
15
- args = ["-y", "-p", "@twinsearth/w2m-dsh-plugin@1.7.6", "w2m-mcp"]
15
+ args = ["-y", "-p", "@twinsearth/w2m-dsh-plugin@1.7.8", "w2m-mcp"]
16
16
  startup_timeout_sec = 20
17
17
  tool_timeout_sec = 60
package/cordis.patch.yml CHANGED
@@ -134,4 +134,10 @@
134
134
  maxPerWindow: 10
135
135
  windowMs: 60000
136
136
  minGapMs: 3000
137
+ # v1.7.5+: only act in the 完全权限 preset; the two folder lists are the gate's policy.
138
+ # Folders are added at runtime from the GPW panel into $DSH_HOME/xclient/gpw-folders.json.
139
+ requireFullAccess: true
140
+ whitelist: []
141
+ blacklist: []
142
+ pendingTimeoutMs: 120000
137
143
 
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": "1.7.7",
3
+ "case": "GPW-INJECT",
4
+ "kind": "positive",
5
+ "machines": [
6
+ "b53a37f4-1464-4512-9259-33a7df091e3e"
7
+ ],
8
+ "executed_at": "2026-10-25T16:24:13.441Z",
9
+ "executor": "lead",
10
+ "task_id": "local-gpw-inject-20261025162413",
11
+ "requirement": "enabling the dsh-gpw sub-plugin must activate it; every service it reads must be declared in inject, and the folder gate must install its tools/pre-execute listener",
12
+ "result": {
13
+ "outcome": "ok",
14
+ "assertions": {
15
+ "symptom": "dsh: warning: 1 entry did not activate dsh-gpw (@twinsearth/w2m-dsh-plugin/gpw): Error: cannot get property \"tools\" without inject",
16
+ "cause": "ctx.tools.guard was read while inject named only permissionPresets",
17
+ "fix": "inject = ['permissionPresets', 'tools']; guard read is defensive for hosts without the guard extension",
18
+ "regression": "test/gpw-activation.test.mjs mounts apply() behind a strict Cordis-like Proxy that throws on undeclared reads; 12/12 (with gpw-folder-policy) pass"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": "1.7.8",
3
+ "case": "GPW-AGENT-SCOPE",
4
+ "kind": "positive",
5
+ "machines": [
6
+ "b53a37f4-1464-4512-9259-33a7df091e3e"
7
+ ],
8
+ "executed_at": "2026-10-25T16:57:54.149Z",
9
+ "executor": "lead",
10
+ "task_id": "local-gpw-scope-20261025165754",
11
+ "requirement": "enabling dsh-gpw must activate it under a full CORDIS context: no root tools read, the pre-execute gate installed, and the blacklist guard registered on each agent context",
12
+ "result": {
13
+ "outcome": "ok",
14
+ "assertions": {
15
+ "symptom": "TypeError: Cannot read properties of undefined (reading 'layers')",
16
+ "cause": "ctx.tools.guard was called at activation on the root context; the tools service is composed per agent and has no layer table there",
17
+ "fix": "agent/created -> agent.ctx.tools.guard per agent, disposers released through ctx.effect; inject = [permissionPresets]",
18
+ "regression": "test/gpw-activation.test.mjs: strict Proxy throws on undeclared/root-tools reads; activation, agent/created guard installation and source checks all pass"
19
+ }
20
+ }
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twinsearth/w2m-dsh-plugin",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
4
4
  "description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 11 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -64,7 +64,7 @@
64
64
  "pack": "node scripts/pack.mjs",
65
65
  "reachability": "node scripts/reachability-check.mjs",
66
66
  "reachability:classify": "node scripts/reachability-classify.mjs",
67
- "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/blake2s.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/fleet-merge-race.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/portable-plugins.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs test/admission.test.mjs test/admission-routes.test.mjs test/join-degradation.test.mjs test/account-identity.test.mjs test/join-challenge.test.mjs test/exec-env-injection.test.mjs test/account-join.test.mjs test/fleet-messages.test.mjs test/message-shards.test.mjs test/fleet-messages-http.test.mjs test/message-notify.test.mjs test/update-channels.test.mjs test/update-prerelease.test.mjs test/session-watch.test.mjs test/session-readonly.test.mjs test/session-sync.test.mjs test/fleet-chat-identity.test.mjs test/identity-reader.test.mjs test/intents.test.mjs test/dataflow.test.mjs test/council-fleet.test.mjs test/fleet-memory.test.mjs test/memory-view.test.mjs test/schema-spec.test.mjs test/fleet-weight-parity.test.mjs test/task-deps.test.mjs test/dataflow-wiring.test.mjs test/task-graph.test.mjs test/ask-store.test.mjs test/turn.test.mjs test/fleet-members.test.mjs test/redact.test.mjs test/policy-matrix.test.mjs test/envelope-signature.test.mjs test/chat-crypto.test.mjs test/reduce-suggestion.test.mjs test/merge-hint.test.mjs test/fleet-chat-route.test.mjs test/maintenance.test.mjs test/maintenance-ferry.test.mjs test/grant.test.mjs test/grant-gate.test.mjs test/worktree.test.mjs test/patch-artifact.test.mjs test/merge-queue.test.mjs test/m3-workflow.test.mjs test/project-room.test.mjs test/project-attribution.test.mjs test/project-locks.test.mjs test/project-reviews.test.mjs test/project-notifications.test.mjs test/blob-sessions.test.mjs test/outbox.test.mjs test/reliability-metrics.test.mjs test/evidence.test.mjs test/grant-issue.test.mjs test/project-refs.test.mjs test/exec-capability.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.test.mjs test/gpw-folder-policy.test.mjs",
67
+ "test": "node --test --test-force-exit test/relay.test.mjs test/broadcast.test.mjs test/blake2s.test.mjs test/agent.test.mjs test/plan-sec-cmd.test.mjs test/authority.test.mjs test/blocks.test.mjs test/approval-index.test.mjs test/fleet-sync.test.mjs test/fleet-merge.test.mjs test/fleet-memory-e2e.test.mjs test/fleet-concurrency.test.mjs test/fleet-merge-race.test.mjs test/health.test.mjs test/storage.test.mjs test/exit-channel.test.mjs test/council.test.mjs test/council-session.test.mjs test/log-bounds.test.mjs test/test-report.test.mjs test/fleet-scale.test.mjs test/fleet-state.test.mjs test/crypto-blake2s.test.mjs test/installed-version.test.mjs test/permission.test.mjs test/machine-test-packet.test.mjs test/field-authority.test.mjs test/approval-gate.test.mjs test/authority-choice.test.mjs test/reachability-check.test.mjs test/reachability-classify.test.mjs test/accepted-orphans.test.mjs test/acceptance-doc.test.mjs test/layer-model.test.mjs test/cwd-containment.test.mjs test/cwd-outside-project.test.mjs test/memory.test.mjs test/tools.test.mjs test/claims.test.mjs test/fleet-target.test.mjs test/client-bundle.test.mjs test/schedule.test.mjs test/auto-update.test.mjs test/update-source.test.mjs test/update-install.test.mjs test/update-wiring.test.mjs test/signing.test.mjs test/stress.test.mjs test/shared-config.test.mjs test/metrics.test.mjs test/metrics-route.test.mjs test/tool-cards.test.mjs test/p2p-stun.test.mjs test/p2p-transport.test.mjs test/p2p-signaling.test.mjs test/p2p-node.test.mjs test/p2p-transport-fields.test.mjs test/p2p-plugin.test.mjs test/p2p-crypto.test.mjs test/stun-server.test.mjs test/loader-entry.test.mjs test/mdns-wire.test.mjs test/mdns-discover.test.mjs test/chatops-approval.test.mjs test/chatops-policy.test.mjs test/chatops-file-safety.test.mjs test/chatops-outbound-queue.test.mjs test/chatops-commands.test.mjs test/chatops-bridge.test.mjs test/chatops-config-route.test.mjs test/chatops-apply.test.mjs test/chatops-ilink-contract.test.mjs test/chatops-fleet.test.mjs test/chatops-account.test.mjs test/chatops-auth.test.mjs test/chatops-fleet-scope.test.mjs test/chatops-handoff.test.mjs test/chatops-fleet-wiring.test.mjs test/chatops-fleet-live.test.mjs test/chatops-takeover.test.mjs test/chatops-device-signing.test.mjs test/chatops-relay-verify.test.mjs test/chatops-durability.test.mjs test/chatops-run-command.test.mjs test/secrets-guard.test.mjs test/chatops-ilink-fleet-bridge.test.mjs test/agent-plugins.test.mjs test/memory-layer.test.mjs test/mcp-server.test.mjs test/portable-plugins.test.mjs test/mcp-http.test.mjs test/mcp-http-live.test.mjs test/vision.test.mjs test/ilink-shape.test.mjs test/chatops-fleet-join.test.mjs test/admission.test.mjs test/admission-routes.test.mjs test/join-degradation.test.mjs test/account-identity.test.mjs test/join-challenge.test.mjs test/exec-env-injection.test.mjs test/account-join.test.mjs test/fleet-messages.test.mjs test/message-shards.test.mjs test/fleet-messages-http.test.mjs test/message-notify.test.mjs test/update-channels.test.mjs test/update-prerelease.test.mjs test/session-watch.test.mjs test/session-readonly.test.mjs test/session-sync.test.mjs test/fleet-chat-identity.test.mjs test/identity-reader.test.mjs test/intents.test.mjs test/dataflow.test.mjs test/council-fleet.test.mjs test/fleet-memory.test.mjs test/memory-view.test.mjs test/schema-spec.test.mjs test/fleet-weight-parity.test.mjs test/task-deps.test.mjs test/dataflow-wiring.test.mjs test/task-graph.test.mjs test/ask-store.test.mjs test/turn.test.mjs test/fleet-members.test.mjs test/redact.test.mjs test/policy-matrix.test.mjs test/envelope-signature.test.mjs test/chat-crypto.test.mjs test/reduce-suggestion.test.mjs test/merge-hint.test.mjs test/fleet-chat-route.test.mjs test/maintenance.test.mjs test/maintenance-ferry.test.mjs test/grant.test.mjs test/grant-gate.test.mjs test/worktree.test.mjs test/patch-artifact.test.mjs test/merge-queue.test.mjs test/m3-workflow.test.mjs test/project-room.test.mjs test/project-attribution.test.mjs test/project-locks.test.mjs test/project-reviews.test.mjs test/project-notifications.test.mjs test/blob-sessions.test.mjs test/outbox.test.mjs test/reliability-metrics.test.mjs test/evidence.test.mjs test/grant-issue.test.mjs test/project-refs.test.mjs test/exec-capability.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.test.mjs test/gpw-folder-policy.test.mjs test/gpw-activation.test.mjs",
68
68
  "test:acceptance-doc": "node --test --test-force-exit test/acceptance-doc.test.mjs",
69
69
  "test:account-identity": "node --test --test-force-exit test/account-identity.test.mjs",
70
70
  "test:account-join": "node --test --test-force-exit test/account-join.test.mjs",
@@ -82,7 +82,7 @@
82
82
  "test:council-session": "node --test --test-force-exit test/council-session.test.mjs",
83
83
  "test:crossnetwork": "node --test --test-force-exit test/crossnetwork.test.mjs",
84
84
  "test:crypto-blake2s": "node --test --test-force-exit test/crypto-blake2s.test.mjs",
85
- "test:e2e": "node --test --test-force-exit test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/p2p-agent.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.test.mjs test/gpw-folder-policy.test.mjs",
85
+ "test:e2e": "node --test --test-force-exit test/e2e.test.mjs test/crossnetwork.test.mjs test/signing-e2e.test.mjs test/outbound-only.test.mjs test/recovery.test.mjs test/pipeline-e2e.test.mjs test/p2p-agent.test.mjs test/secondary-link.test.mjs test/bootstrap-cli.test.mjs test/bootstrap.test.mjs test/windows-hidden-task.test.mjs test/relay-failover.test.mjs test/npm-update.test.mjs test/agent-start-nonblocking.test.mjs test/fleet-chat-capture.test.mjs test/capture-route.test.mjs test/update-resolver.test.mjs test/agent-identity-safe.test.mjs test/re-pair-stale-token.test.mjs test/gpw-folder-policy.test.mjs test/gpw-activation.test.mjs",
86
86
  "test:exec-env": "node --test --test-force-exit test/exec-env-injection.test.mjs",
87
87
  "test:exit-channel": "node --test --test-force-exit test/exit-channel.test.mjs",
88
88
  "test:field-authority": "node --test --test-force-exit test/field-authority.test.mjs",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "twinsearth-w2m-dsh",
4
- "version": "1.7.6",
4
+ "version": "1.7.8",
5
5
  "description": "Run one project across your own Windows and macOS machines from a single DeepSeek account, through 12 w2m_* tools. Four coordination modes (replicate, split, broadcast, pipeline) and six aggregate verdicts (consistent, divergent, divergent-platform, failed, partial, unverifiable). P2P hole punching by default, with a bundled relay, STUN responder and ledger fallback, so the fleet needs no third-party service. Joining needs a pairing code plus autoStartAgent: true; installing alone does not join.",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * ## The three classes and who answers
5
5
  *
6
- * A. blacklist -> denied. A synchronous `ctx.tools.guard()` is registered as well, so the denial holds no matter
6
+ * A. blacklist -> denied. A synchronous guard is registered on each agent (agent.ctx.tools.guard), so the denial holds no matter
7
7
  * which other pre-execute listener runs first.
8
8
  * B. whitelist -> allowed without a human. The whitelist is an explicit extension of the workspace (same decision,
9
9
  * plus the auto-approval budget below).
@@ -322,7 +322,17 @@ export function apply(ctx, config = {}) {
322
322
  };
323
323
 
324
324
  // --- A: the blacklist denial must hold regardless of listener order -------------------------------------------------
325
- ctx.tools?.guard?.((exec) => {
325
+ //
326
+ // v1.7.8: the guard is registered on each agent's own context, from an `agent/created` listener.
327
+ //
328
+ // Registering it on the root context (ctx.tools.guard at activation) crashed dsh-gpw with
329
+ // TypeError: Cannot read properties of undefined (reading 'layers')
330
+ // because the tools service is composed per agent: the root-scope instance has no layer table yet at activation time.
331
+ // The documented shape for per-agent behavior is agent.ctx obtained in agent/created; the disposer is kept so an
332
+ // unloaded plugin cannot leave guards behind.
333
+ /** @type {Map<object, () => void>} */
334
+ const agentGuards = new Map();
335
+ const blacklistGuard = () => (exec) => {
326
336
  if (!isFullAccess(exec?.agent)) return undefined;
327
337
  const lists = folderLists();
328
338
  if (lists.blacklist.length === 0) return undefined;
@@ -333,7 +343,30 @@ export function apply(ctx, config = {}) {
333
343
  noteRecent({ tool: String(call.name ?? 'tool'), decision: 'deny', reason: result.reason, paths: result.paths });
334
344
  ctx.logger?.warn?.(`dsh-gpw: refused ${String(call.name ?? 'tool')} -- ${result.reason}`);
335
345
  return `GPW: ${result.reason}; the call was not executed`;
346
+ };
347
+ ctx.on('agent/created', ({ agent } = {}) => {
348
+ try {
349
+ const guard = agent?.ctx?.tools?.guard;
350
+ if (typeof guard !== 'function') return; // host without the guard extension: pre-execute still denies
351
+ const dispose = guard(blacklistGuard());
352
+ if (typeof dispose === 'function') agentGuards.set(agent, dispose);
353
+ } catch {
354
+ /* the pre-execute branch below still enforces the blacklist */
355
+ }
336
356
  });
357
+ if (typeof ctx.effect === 'function') {
358
+ ctx.effect(() => () => {
359
+ for (const dispose of agentGuards.values()) {
360
+ try {
361
+ dispose();
362
+ } catch {
363
+ /* already disposed with its agent */
364
+ }
365
+ }
366
+ agentGuards.clear();
367
+ }, 'dsh-gpw: per-agent blacklist guards');
368
+ }
369
+
337
370
 
338
371
  // --- B/C: the pre-execute gate ---------------------------------------------------------------------------------------
339
372
  ctx.on('tools/pre-execute', async (exec, next) => {
@@ -283,7 +283,7 @@ const MAX_STAGES = 16;
283
283
  * `package.json` during `scripts/pack.mjs` rather than hand-maintained, and `W2M_PLUGIN_VERSION`
284
284
  * overrides it at runtime for tests and packagers.
285
285
  */
286
- const PLUGIN_VERSION = '1.7.6';
286
+ const PLUGIN_VERSION = '1.7.8';
287
287
 
288
288
  /** How long any single HTTP call may take before it is aborted, in milliseconds. */
289
289
  const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;