@randuhmm/openclaw-agent-acl 1.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/.github/workflows/ci.yml +31 -0
- package/.github/workflows/publish.yml +47 -0
- package/ARCHITECTURE.md +155 -0
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/acl.json.example +13 -0
- package/dist/index.js +49 -0
- package/index.ts +58 -0
- package/openclaw.plugin.json +18 -0
- package/package.json +48 -0
- package/reports/plugin-inspector-issues.md +73 -0
- package/reports/plugin-inspector-report.json +231 -0
- package/reports/plugin-inspector-report.md +132 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
validate:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- uses: actions/setup-node@v4
|
|
15
|
+
with:
|
|
16
|
+
node-version: '22'
|
|
17
|
+
cache: 'npm'
|
|
18
|
+
|
|
19
|
+
- run: npm ci
|
|
20
|
+
|
|
21
|
+
- name: Install clawhub CLI
|
|
22
|
+
run: npm install -g clawhub
|
|
23
|
+
|
|
24
|
+
- name: Type check
|
|
25
|
+
run: npx tsc --noEmit
|
|
26
|
+
|
|
27
|
+
- name: Build plugin
|
|
28
|
+
run: npm run plugin:build
|
|
29
|
+
|
|
30
|
+
- name: Validate manifest
|
|
31
|
+
run: npm run plugin:validate
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
tags:
|
|
7
|
+
- 'v*'
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write # required for ClawHub OIDC trusted publishing
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: '22'
|
|
22
|
+
cache: 'npm'
|
|
23
|
+
registry-url: 'https://registry.npmjs.org'
|
|
24
|
+
|
|
25
|
+
- run: npm ci
|
|
26
|
+
|
|
27
|
+
- name: Install clawhub CLI
|
|
28
|
+
run: npm install -g clawhub
|
|
29
|
+
|
|
30
|
+
- name: Build plugin
|
|
31
|
+
run: npm run build
|
|
32
|
+
|
|
33
|
+
- name: Validate plugin package
|
|
34
|
+
run: clawhub package validate .
|
|
35
|
+
|
|
36
|
+
- name: Authenticate clawhub
|
|
37
|
+
run: clawhub login --token "$CLAWHUB_TOKEN"
|
|
38
|
+
env:
|
|
39
|
+
CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }}
|
|
40
|
+
|
|
41
|
+
- name: Publish to ClawHub
|
|
42
|
+
run: clawhub package publish randuhmm/openclaw-agent-acl
|
|
43
|
+
|
|
44
|
+
- name: Publish to npm
|
|
45
|
+
run: npm publish --access public
|
|
46
|
+
env:
|
|
47
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Architecture: openclaw-agent-acl
|
|
2
|
+
|
|
3
|
+
This document describes the internal design of the plugin, its data flow, current limitations, and the planned roadmap for future versions. Intended audience: contributors and anyone building on top of this plugin.
|
|
4
|
+
|
|
5
|
+
## Design overview
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
User prompt
|
|
9
|
+
│
|
|
10
|
+
▼
|
|
11
|
+
OpenClaw gateway
|
|
12
|
+
│ dispatches tool call
|
|
13
|
+
▼
|
|
14
|
+
before_tool_call event
|
|
15
|
+
│
|
|
16
|
+
▼
|
|
17
|
+
agent-acl plugin
|
|
18
|
+
│
|
|
19
|
+
├─ extract server name from tool name ("calendar-mcp__list_events" → "calendar-mcp")
|
|
20
|
+
│
|
|
21
|
+
├─ look up acl.servers["calendar-mcp"]
|
|
22
|
+
│ ├─ no rule → pass (unrestricted)
|
|
23
|
+
│ └─ rule found → check allowAgents.includes(ctx.agentId)
|
|
24
|
+
│ ├─ yes → pass
|
|
25
|
+
│ └─ no → block + log
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
Tool executes (or is blocked with reason returned to agent)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Key implementation details
|
|
32
|
+
|
|
33
|
+
### Hook: `before_tool_call`
|
|
34
|
+
|
|
35
|
+
The plugin registers a single async handler on OpenClaw's `before_tool_call` event. This event fires synchronously in the tool dispatch path before any MCP call is made. The handler returns `{ block: false }` to allow or `{ block: true, blockReason: string }` to deny.
|
|
36
|
+
|
|
37
|
+
### Tool name parsing
|
|
38
|
+
|
|
39
|
+
OpenClaw namespaces MCP tools as `<server>__<tool>`. The plugin splits on the first `__` to extract the server name:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const serverName = event.toolName.split('__')[0];
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This is safe as long as server names do not themselves contain `__`, which is consistent with OpenClaw's naming conventions.
|
|
46
|
+
|
|
47
|
+
### ACL config loading
|
|
48
|
+
|
|
49
|
+
`acl.json` is read once during `register()` via `readFileSync`. The file is parsed and validated at startup — missing file, invalid JSON, or wrong structure all throw with a descriptive error message that surfaces in gateway logs. The parsed config is held in memory for the lifetime of the gateway process.
|
|
50
|
+
|
|
51
|
+
### Default-open policy
|
|
52
|
+
|
|
53
|
+
Any MCP server not listed in `acl.json` is fully unrestricted. This is an intentional "lock what you need" design: adding the plugin doesn't break anything by default, and you opt specific servers into the allowlist incrementally.
|
|
54
|
+
|
|
55
|
+
### Agent ID resolution
|
|
56
|
+
|
|
57
|
+
The agent ID is taken from `ctx.agentId`, falling back to `"main"` if absent. Agent IDs in `allowAgents` must match exactly (case-sensitive).
|
|
58
|
+
|
|
59
|
+
### Plugin manifest: `trustedToolPolicies`
|
|
60
|
+
|
|
61
|
+
The `openclaw.plugin.json` declares `contracts.trustedToolPolicies: ["agent-acl"]`. This contract grants the plugin permission to intercept `before_tool_call` events and return block decisions. Without it, the gateway would not dispatch the hook to this plugin.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Known limitations
|
|
66
|
+
|
|
67
|
+
These are intentional constraints for v1.0, not bugs. Each is tracked as a future enhancement.
|
|
68
|
+
|
|
69
|
+
| # | Limitation | Impact |
|
|
70
|
+
|---|---|---|
|
|
71
|
+
| 1 | **No hot-reload** | Rule changes require gateway restart (`openclaw gateway restart`) |
|
|
72
|
+
| 2 | **Server-level granularity only** | Can't allow `mealie__search` but block `mealie__delete` for the same agent |
|
|
73
|
+
| 3 | **Allowlist only** | No denylist mode — can't say "all agents except X" |
|
|
74
|
+
| 4 | **No wildcard agent IDs** | Can't match `jonny-*` to cover all per-user agent instances |
|
|
75
|
+
| 5 | **No runtime inspection** | No API to query the loaded ACL without reading the file directly |
|
|
76
|
+
| 6 | **Single config file** | All rules live in one `acl.json`; no include/merge of multiple rule files |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Roadmap
|
|
81
|
+
|
|
82
|
+
### v1.1 — Hot-reload
|
|
83
|
+
|
|
84
|
+
Watch `aclPath` with `fs.watch` and atomically swap the in-memory ACL on change. No gateway restart needed to update rules. Implementation sketch:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { watch } from 'node:fs';
|
|
88
|
+
|
|
89
|
+
watch(aclPath, () => {
|
|
90
|
+
try {
|
|
91
|
+
acl = loadAcl(aclPath);
|
|
92
|
+
api.logger.info(`agent-acl: reloaded ${Object.keys(acl.servers).length} rule(s)`);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
api.logger.error(`agent-acl: reload failed — keeping previous rules. ${e}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### v1.2 — Per-tool granularity
|
|
100
|
+
|
|
101
|
+
Extend the schema to allow tool-level overrides within a server:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"servers": {
|
|
106
|
+
"mealie": {
|
|
107
|
+
"allowAgents": ["family-chef"],
|
|
108
|
+
"tools": {
|
|
109
|
+
"delete_recipe": { "allowAgents": [] }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Tool-level rules take precedence over server-level rules.
|
|
117
|
+
|
|
118
|
+
### v1.3 — Denylist mode
|
|
119
|
+
|
|
120
|
+
Add `denyAgents` as an alternative to `allowAgents`. Semantics: if `denyAgents` is present, all agents are allowed *except* those listed.
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"servers": {
|
|
125
|
+
"web-search": {
|
|
126
|
+
"denyAgents": ["child-agent"]
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### v1.4 — Wildcard agent IDs
|
|
133
|
+
|
|
134
|
+
Support glob-style patterns in `allowAgents`/`denyAgents`:
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{ "allowAgents": ["jonny-*", "main"] }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Matches any agent ID starting with `jonny-`, which covers per-user instances like `jonny-assistant`, `jonny-chef`, etc.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Contributing
|
|
145
|
+
|
|
146
|
+
The plugin is a single TypeScript file (`index.ts`) compiled with `tsup` to `dist/index.js`. The `dist/` output is committed to the repo so that `git:`-based installs work without a build step.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
npm install # install dev deps
|
|
150
|
+
npm run typecheck # type-check without building
|
|
151
|
+
npm run build # compile index.ts → dist/index.js
|
|
152
|
+
npm run plugin:build # build + validate plugin entry point
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
All PRs should pass `npm run plugin:build` and `npm run plugin:validate` before merge. The CI workflow (`.github/workflows/ci.yml`) enforces this on every push.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.0] — 2026-06-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release: per-agent MCP server allowlist via `before_tool_call` hook
|
|
7
|
+
- JSON config (`acl.json`) with server-level rules — servers not listed are unrestricted
|
|
8
|
+
- `aclPath` plugin config option for placing `acl.json` outside the plugin directory
|
|
9
|
+
- Validated error messages for missing, unreadable, or malformed `acl.json`
|
|
10
|
+
- `contracts.trustedToolPolicies` manifest declaration for gateway integration
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonny
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# openclaw-agent-acl
|
|
2
|
+
|
|
3
|
+
An [OpenClaw](https://openclaw.ai) plugin that restricts MCP server tool access on a per-agent basis using a simple JSON allowlist.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
By default, every agent in OpenClaw can call tools from every connected MCP server. That's fine for a single-agent setup, but breaks down when you run multiple agents with different roles or trust levels — a family cooking assistant shouldn't be able to read your email, a child's agent shouldn't have access to your calendar or smart-home controls, and a work agent probably shouldn't touch personal data.
|
|
8
|
+
|
|
9
|
+
This plugin lets you lock down specific MCP servers to designated agents without touching OpenClaw's core configuration. You define the rules once in a JSON file; everything else stays open by default.
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
OpenClaw namespaces all MCP tools as `<server>__<tool>` (e.g. `calendar-mcp__list_events`). This plugin intercepts every `before_tool_call` event, extracts the server name from the tool name, and checks whether the calling agent is in that server's allowlist. If not, the call is blocked with a clear reason returned to the agent and logged.
|
|
14
|
+
|
|
15
|
+
Servers with no rule in `acl.json` are unrestricted — you only list the servers you want to lock down.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
openclaw plugins install clawhub:randuhmm/openclaw-agent-acl
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or directly from git (for pre-release versions or self-hosted installs):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
openclaw plugins install git:github.com/randuhmm/openclaw-agent-acl
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
### 1. Create `acl.json`
|
|
32
|
+
|
|
33
|
+
Copy the example and edit it for your setup:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cp acl.json.example ~/.openclaw/acl.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"servers": {
|
|
42
|
+
"calendar-mcp": {
|
|
43
|
+
"allowAgents": ["my-assistant"]
|
|
44
|
+
},
|
|
45
|
+
"smart-home": {
|
|
46
|
+
"allowAgents": ["main", "my-assistant"]
|
|
47
|
+
},
|
|
48
|
+
"recipes": {
|
|
49
|
+
"allowAgents": ["family-chef"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `servers` — keys are MCP server names exactly as configured in OpenClaw
|
|
56
|
+
- `allowAgents` — agent IDs allowed to call tools from that server
|
|
57
|
+
- Servers **not listed** are unrestricted — all agents pass through
|
|
58
|
+
|
|
59
|
+
### 2. Point the plugin at your `acl.json`
|
|
60
|
+
|
|
61
|
+
In your `openclaw.json`, configure the `aclPath`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"plugins": {
|
|
66
|
+
"entries": {
|
|
67
|
+
"agent-acl": {
|
|
68
|
+
"enabled": true,
|
|
69
|
+
"config": {
|
|
70
|
+
"aclPath": "/home/user/.openclaw/acl.json"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
If `aclPath` is omitted, the plugin looks for `acl.json` in its own installed directory.
|
|
79
|
+
|
|
80
|
+
### 3. Restart the gateway
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
openclaw gateway restart
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Verification
|
|
87
|
+
|
|
88
|
+
Confirm the plugin is active:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
openclaw plugins inspect agent-acl --runtime
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Test it: ask a restricted agent to use a blocked tool — it will receive a refusal, and you'll see a log line like:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
agent-acl: blocked agent="family-chef" from tool="calendar-mcp__list_events"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Finding your agent IDs
|
|
101
|
+
|
|
102
|
+
Agent IDs are the identifiers used when creating agents with `openclaw agents create`. List them:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
openclaw agents list
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The default agent ID is `"main"`.
|
|
109
|
+
|
|
110
|
+
## Updating rules
|
|
111
|
+
|
|
112
|
+
Edit `acl.json` and restart the gateway to apply changes. No plugin reinstall needed.
|
|
113
|
+
|
|
114
|
+
## Troubleshooting
|
|
115
|
+
|
|
116
|
+
**Plugin is loaded but nothing is being blocked**
|
|
117
|
+
Check that the server name in `acl.json` exactly matches the server name as configured in OpenClaw (case-sensitive). The key must equal what appears before `__` in a tool name — e.g. if the tool is `Calendar__list_events`, the server key is `Calendar`, not `calendar-mcp`.
|
|
118
|
+
|
|
119
|
+
**Gateway fails to start after installing the plugin**
|
|
120
|
+
The plugin now gives a clear error message if `acl.json` is missing or malformed. Check the gateway logs — the message will include the exact path it tried to read and what went wrong.
|
|
121
|
+
|
|
122
|
+
**An agent is being blocked unexpectedly**
|
|
123
|
+
Run `openclaw agents list` to confirm the exact agent ID. Agent IDs are case-sensitive and must match the string in `allowAgents` exactly.
|
|
124
|
+
|
|
125
|
+
## Plugin manifest: `trustedToolPolicies`
|
|
126
|
+
|
|
127
|
+
The `openclaw.plugin.json` manifest declares `contracts.trustedToolPolicies: ["agent-acl"]`. This tells OpenClaw's gateway that this plugin provides a trusted tool policy and should be loaded before tool calls are dispatched — it's what grants the plugin permission to intercept `before_tool_call` events.
|
|
128
|
+
|
|
129
|
+
## Requirements
|
|
130
|
+
|
|
131
|
+
- OpenClaw `>=2026.6.8`
|
|
132
|
+
- Node.js `>=22.19`
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT
|
package/acl.json.example
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// index.ts
|
|
2
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
+
import { readFileSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
function loadAcl(aclPath) {
|
|
6
|
+
let raw;
|
|
7
|
+
try {
|
|
8
|
+
raw = readFileSync(aclPath, "utf8");
|
|
9
|
+
} catch {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`agent-acl: cannot read acl.json at "${aclPath}". Create the file or set aclPath in plugin config.`
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
let acl;
|
|
15
|
+
try {
|
|
16
|
+
acl = JSON.parse(raw);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
throw new Error(`agent-acl: acl.json at "${aclPath}" is not valid JSON: ${e}`);
|
|
19
|
+
}
|
|
20
|
+
if (!acl || typeof acl.servers !== "object" || Array.isArray(acl.servers)) {
|
|
21
|
+
throw new Error(`agent-acl: acl.json must have a top-level "servers" object`);
|
|
22
|
+
}
|
|
23
|
+
return acl;
|
|
24
|
+
}
|
|
25
|
+
var index_default = definePluginEntry({
|
|
26
|
+
id: "agent-acl",
|
|
27
|
+
name: "Agent Tool ACL",
|
|
28
|
+
description: "Restricts MCP server tool access per agent via acl.json",
|
|
29
|
+
register(api) {
|
|
30
|
+
const aclPath = api.pluginConfig?.["aclPath"] ?? join(api.rootDir, "acl.json");
|
|
31
|
+
const acl = loadAcl(aclPath);
|
|
32
|
+
api.logger.info(`agent-acl: loaded ${Object.keys(acl.servers).length} server rule(s) from ${aclPath}`);
|
|
33
|
+
api.on("before_tool_call", async (event, ctx) => {
|
|
34
|
+
const serverName = event.toolName.split("__")[0];
|
|
35
|
+
const rule = acl.servers[serverName];
|
|
36
|
+
if (!rule) return { block: false };
|
|
37
|
+
const agentId = ctx.agentId ?? "main";
|
|
38
|
+
if (rule.allowAgents.includes(agentId)) return { block: false };
|
|
39
|
+
api.logger.info(`agent-acl: blocked agent="${agentId}" from tool="${event.toolName}"`);
|
|
40
|
+
return {
|
|
41
|
+
block: true,
|
|
42
|
+
blockReason: `Agent "${agentId}" does not have access to "${serverName}" tools`
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
export {
|
|
48
|
+
index_default as default
|
|
49
|
+
};
|
package/index.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
interface AclConfig {
|
|
6
|
+
servers: Record<string, { allowAgents: string[] }>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function loadAcl(aclPath: string): AclConfig {
|
|
10
|
+
let raw: string;
|
|
11
|
+
try {
|
|
12
|
+
raw = readFileSync(aclPath, 'utf8');
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`agent-acl: cannot read acl.json at "${aclPath}". ` +
|
|
16
|
+
`Create the file or set aclPath in plugin config.`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
let acl: AclConfig;
|
|
20
|
+
try {
|
|
21
|
+
acl = JSON.parse(raw) as AclConfig;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
throw new Error(`agent-acl: acl.json at "${aclPath}" is not valid JSON: ${e}`);
|
|
24
|
+
}
|
|
25
|
+
if (!acl || typeof acl.servers !== 'object' || Array.isArray(acl.servers)) {
|
|
26
|
+
throw new Error(`agent-acl: acl.json must have a top-level "servers" object`);
|
|
27
|
+
}
|
|
28
|
+
return acl;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default definePluginEntry({
|
|
32
|
+
id: 'agent-acl',
|
|
33
|
+
name: 'Agent Tool ACL',
|
|
34
|
+
description: 'Restricts MCP server tool access per agent via acl.json',
|
|
35
|
+
register(api) {
|
|
36
|
+
const aclPath: string =
|
|
37
|
+
(api.pluginConfig?.['aclPath'] as string | undefined) ??
|
|
38
|
+
join(api.rootDir!, 'acl.json');
|
|
39
|
+
|
|
40
|
+
const acl = loadAcl(aclPath);
|
|
41
|
+
api.logger.info(`agent-acl: loaded ${Object.keys(acl.servers).length} server rule(s) from ${aclPath}`);
|
|
42
|
+
|
|
43
|
+
api.on('before_tool_call', async (event, ctx) => {
|
|
44
|
+
const serverName = event.toolName.split('__')[0];
|
|
45
|
+
const rule = acl.servers[serverName];
|
|
46
|
+
if (!rule) return { block: false };
|
|
47
|
+
|
|
48
|
+
const agentId = ctx.agentId ?? 'main';
|
|
49
|
+
if (rule.allowAgents.includes(agentId)) return { block: false };
|
|
50
|
+
|
|
51
|
+
api.logger.info(`agent-acl: blocked agent="${agentId}" from tool="${event.toolName}"`);
|
|
52
|
+
return {
|
|
53
|
+
block: true,
|
|
54
|
+
blockReason: `Agent "${agentId}" does not have access to "${serverName}" tools`,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "agent-acl",
|
|
3
|
+
"name": "Agent Tool ACL",
|
|
4
|
+
"description": "Restricts MCP server tool access per agent via a JSON allowlist.",
|
|
5
|
+
"configSchema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"aclPath": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Absolute path to acl.json. Defaults to acl.json in the installed plugin root."
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"additionalProperties": false
|
|
14
|
+
},
|
|
15
|
+
"contracts": {
|
|
16
|
+
"trustedToolPolicies": ["agent-acl"]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@randuhmm/openclaw-agent-acl",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Per-agent MCP tool access control for OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup index.ts --format esm --outDir dist --external openclaw --external node:fs --external node:path",
|
|
8
|
+
"plugin:build": "npm run build",
|
|
9
|
+
"plugin:validate": "clawhub package validate .",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"openclaw": {
|
|
13
|
+
"extensions": [
|
|
14
|
+
"./dist/index.js"
|
|
15
|
+
],
|
|
16
|
+
"compat": {
|
|
17
|
+
"pluginApi": ">=2026.3.24-beta.2",
|
|
18
|
+
"minGatewayVersion": "2026.3.24-beta.2"
|
|
19
|
+
},
|
|
20
|
+
"build": {
|
|
21
|
+
"openclawVersion": "2026.6.10"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^25.9.3",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.4.0"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=22.19"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/randuhmm/openclaw-agent-acl"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"openclaw",
|
|
39
|
+
"openclaw-plugin",
|
|
40
|
+
"mcp",
|
|
41
|
+
"acl",
|
|
42
|
+
"agent",
|
|
43
|
+
"tool-policy"
|
|
44
|
+
],
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"openclaw": "^2026.6.8"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# OpenClaw Plugin Issue Findings
|
|
2
|
+
|
|
3
|
+
Generated: deterministic
|
|
4
|
+
Status: PASS
|
|
5
|
+
|
|
6
|
+
## Triage Summary
|
|
7
|
+
|
|
8
|
+
| Metric | Value |
|
|
9
|
+
| -------------------------- | ----- |
|
|
10
|
+
| Issue findings | 0 |
|
|
11
|
+
| Open issue findings | 0 |
|
|
12
|
+
| Runtime-covered findings | 0 |
|
|
13
|
+
| Runtime-partial findings | 0 |
|
|
14
|
+
| P0 | 0 |
|
|
15
|
+
| P1 | 0 |
|
|
16
|
+
| Open P0 | 0 |
|
|
17
|
+
| Open P1 | 0 |
|
|
18
|
+
| Live issues | 0 |
|
|
19
|
+
| Live P0 issues | 0 |
|
|
20
|
+
| Compat gaps | 0 |
|
|
21
|
+
| Deprecation warnings | 0 |
|
|
22
|
+
| Inspector gaps | 0 |
|
|
23
|
+
| Open inspector gaps | 0 |
|
|
24
|
+
| Runtime coverage artifacts | 0 |
|
|
25
|
+
| Upstream metadata | 0 |
|
|
26
|
+
| Contract probes | 0 |
|
|
27
|
+
|
|
28
|
+
## Triage Overview
|
|
29
|
+
|
|
30
|
+
| Class | Count | P0 | Meaning |
|
|
31
|
+
| ------------------- | ----- | -- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| live-issue | 0 | 0 | Potential runtime breakage in the target OpenClaw/plugin pair. P0 only when it is not a deprecated compat seam. |
|
|
33
|
+
| compat-gap | 0 | - | Compatibility behavior is needed but missing from the target OpenClaw compat registry. |
|
|
34
|
+
| deprecation-warning | 0 | - | Plugin uses a supported but deprecated compatibility seam; keep it wired while migration exists. |
|
|
35
|
+
| inspector-gap | 0 | - | Plugin Inspector needs stronger capture/probe evidence before making contract judgments. Runtime-covered rows are proof-backed and not open report work. |
|
|
36
|
+
| upstream-metadata | 0 | - | Plugin package or manifest metadata should improve upstream; not a target OpenClaw live break by itself. |
|
|
37
|
+
| fixture-regression | 0 | - | Fixture no longer exposes an expected seam; investigate fixture pin or scanner drift. |
|
|
38
|
+
|
|
39
|
+
## P0 Live Issues
|
|
40
|
+
|
|
41
|
+
_none_
|
|
42
|
+
|
|
43
|
+
## Other Live Issues
|
|
44
|
+
|
|
45
|
+
_none_
|
|
46
|
+
|
|
47
|
+
## Compat Gaps
|
|
48
|
+
|
|
49
|
+
_none_
|
|
50
|
+
|
|
51
|
+
## Deprecation Warnings
|
|
52
|
+
|
|
53
|
+
_none_
|
|
54
|
+
|
|
55
|
+
## Inspector Proof Gaps
|
|
56
|
+
|
|
57
|
+
_none_
|
|
58
|
+
|
|
59
|
+
## Runtime-Covered Inspector Gaps
|
|
60
|
+
|
|
61
|
+
_none_
|
|
62
|
+
|
|
63
|
+
## Upstream Metadata Issues
|
|
64
|
+
|
|
65
|
+
_none_
|
|
66
|
+
|
|
67
|
+
## Issues
|
|
68
|
+
|
|
69
|
+
_none_
|
|
70
|
+
|
|
71
|
+
## Contract Probe Backlog
|
|
72
|
+
|
|
73
|
+
_none_
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generatedAt": "deterministic",
|
|
3
|
+
"targetOpenClaw": {
|
|
4
|
+
"configuredPath": null,
|
|
5
|
+
"status": "disabled",
|
|
6
|
+
"compatRecords": [],
|
|
7
|
+
"compatRecordStatuses": {},
|
|
8
|
+
"hookNames": [],
|
|
9
|
+
"apiRegistrars": [],
|
|
10
|
+
"capturedRegistrars": [],
|
|
11
|
+
"sdkExports": [],
|
|
12
|
+
"reservedSdkExports": [],
|
|
13
|
+
"supportedFacadeSdkExports": [],
|
|
14
|
+
"publicPluginOwnedSdkExports": [],
|
|
15
|
+
"manifestFields": [],
|
|
16
|
+
"manifestContractFields": []
|
|
17
|
+
},
|
|
18
|
+
"status": "pass",
|
|
19
|
+
"summary": {
|
|
20
|
+
"breakageCount": 0,
|
|
21
|
+
"warningCount": 0,
|
|
22
|
+
"deprecationWarningCount": 0,
|
|
23
|
+
"issueCount": 0
|
|
24
|
+
},
|
|
25
|
+
"fixtures": [
|
|
26
|
+
{
|
|
27
|
+
"id": "openclaw-agent-acl",
|
|
28
|
+
"name": "Agent Tool ACL",
|
|
29
|
+
"priority": "high",
|
|
30
|
+
"seams": [
|
|
31
|
+
"plugin-runtime"
|
|
32
|
+
],
|
|
33
|
+
"why": "local OpenClaw plugin root",
|
|
34
|
+
"status": "ok",
|
|
35
|
+
"hooks": [
|
|
36
|
+
"before_tool_call"
|
|
37
|
+
],
|
|
38
|
+
"hookDetails": [
|
|
39
|
+
{
|
|
40
|
+
"name": "before_tool_call",
|
|
41
|
+
"file": "index.ts",
|
|
42
|
+
"line": 43,
|
|
43
|
+
"ref": "index.ts:43"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"registrations": [
|
|
47
|
+
"definePluginEntry"
|
|
48
|
+
],
|
|
49
|
+
"registrationDetails": [
|
|
50
|
+
{
|
|
51
|
+
"name": "definePluginEntry",
|
|
52
|
+
"file": "index.ts",
|
|
53
|
+
"line": 31,
|
|
54
|
+
"ref": "index.ts:31"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"manifestContracts": [
|
|
58
|
+
"trustedToolPolicies"
|
|
59
|
+
],
|
|
60
|
+
"manifestFiles": [
|
|
61
|
+
"openclaw.plugin.json"
|
|
62
|
+
],
|
|
63
|
+
"sourceFiles": [
|
|
64
|
+
"index.ts"
|
|
65
|
+
],
|
|
66
|
+
"pluginManifests": [
|
|
67
|
+
{
|
|
68
|
+
"path": "openclaw.plugin.json",
|
|
69
|
+
"id": "agent-acl",
|
|
70
|
+
"name": "Agent Tool ACL",
|
|
71
|
+
"version": null,
|
|
72
|
+
"keys": [
|
|
73
|
+
"configSchema",
|
|
74
|
+
"contracts",
|
|
75
|
+
"description",
|
|
76
|
+
"id",
|
|
77
|
+
"name"
|
|
78
|
+
],
|
|
79
|
+
"contracts": [
|
|
80
|
+
"trustedToolPolicies"
|
|
81
|
+
],
|
|
82
|
+
"providerAuthEnvVars": {},
|
|
83
|
+
"channelEnvVars": {},
|
|
84
|
+
"activation": null
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"securityManifests": [],
|
|
88
|
+
"package": {
|
|
89
|
+
"path": "package.json",
|
|
90
|
+
"name": "@randuhmm/openclaw-agent-acl",
|
|
91
|
+
"version": "1.0.0",
|
|
92
|
+
"type": "module",
|
|
93
|
+
"main": null,
|
|
94
|
+
"npmPack": {
|
|
95
|
+
"advertised": false,
|
|
96
|
+
"private": false,
|
|
97
|
+
"filesMode": "implicit",
|
|
98
|
+
"files": [],
|
|
99
|
+
"invalidFileSpecs": []
|
|
100
|
+
},
|
|
101
|
+
"dependencies": [],
|
|
102
|
+
"peerDependencies": [
|
|
103
|
+
"openclaw"
|
|
104
|
+
],
|
|
105
|
+
"optionalDependencies": [],
|
|
106
|
+
"openclaw": {
|
|
107
|
+
"extensions": [
|
|
108
|
+
"./dist/index.js"
|
|
109
|
+
],
|
|
110
|
+
"runtimeExtensions": [],
|
|
111
|
+
"setupEntry": null,
|
|
112
|
+
"compatPluginApi": ">=2026.3.24-beta.2",
|
|
113
|
+
"buildOpenClawVersion": "2026.6.10",
|
|
114
|
+
"buildPluginSdkVersion": null,
|
|
115
|
+
"install": null,
|
|
116
|
+
"release": null,
|
|
117
|
+
"unsupportedMetadata": [],
|
|
118
|
+
"entrypoints": [
|
|
119
|
+
{
|
|
120
|
+
"kind": "extension",
|
|
121
|
+
"specifier": "./dist/index.js",
|
|
122
|
+
"relativePath": "dist/index.js",
|
|
123
|
+
"exists": true,
|
|
124
|
+
"requiresBuild": true
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"packages": [
|
|
130
|
+
{
|
|
131
|
+
"path": "package.json",
|
|
132
|
+
"name": "@randuhmm/openclaw-agent-acl",
|
|
133
|
+
"version": "1.0.0",
|
|
134
|
+
"type": "module",
|
|
135
|
+
"main": null,
|
|
136
|
+
"npmPack": {
|
|
137
|
+
"advertised": false,
|
|
138
|
+
"private": false,
|
|
139
|
+
"filesMode": "implicit",
|
|
140
|
+
"files": [],
|
|
141
|
+
"invalidFileSpecs": []
|
|
142
|
+
},
|
|
143
|
+
"dependencies": [],
|
|
144
|
+
"peerDependencies": [
|
|
145
|
+
"openclaw"
|
|
146
|
+
],
|
|
147
|
+
"optionalDependencies": [],
|
|
148
|
+
"openclaw": {
|
|
149
|
+
"extensions": [
|
|
150
|
+
"./dist/index.js"
|
|
151
|
+
],
|
|
152
|
+
"runtimeExtensions": [],
|
|
153
|
+
"setupEntry": null,
|
|
154
|
+
"compatPluginApi": ">=2026.3.24-beta.2",
|
|
155
|
+
"buildOpenClawVersion": "2026.6.10",
|
|
156
|
+
"buildPluginSdkVersion": null,
|
|
157
|
+
"install": null,
|
|
158
|
+
"release": null,
|
|
159
|
+
"unsupportedMetadata": [],
|
|
160
|
+
"entrypoints": [
|
|
161
|
+
{
|
|
162
|
+
"kind": "extension",
|
|
163
|
+
"specifier": "./dist/index.js",
|
|
164
|
+
"relativePath": "dist/index.js",
|
|
165
|
+
"exists": true,
|
|
166
|
+
"requiresBuild": true
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"sdkImports": [
|
|
173
|
+
"openclaw/plugin-sdk/plugin-entry"
|
|
174
|
+
],
|
|
175
|
+
"sdkImportDetails": [
|
|
176
|
+
{
|
|
177
|
+
"specifier": "openclaw/plugin-sdk/plugin-entry",
|
|
178
|
+
"file": "index.ts",
|
|
179
|
+
"line": 1,
|
|
180
|
+
"ref": "index.ts:1"
|
|
181
|
+
}
|
|
182
|
+
],
|
|
183
|
+
"sdkDeprecations": []
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
"issues": [],
|
|
187
|
+
"contractProbes": [],
|
|
188
|
+
"logs": [
|
|
189
|
+
{
|
|
190
|
+
"fixture": "openclaw-agent-acl",
|
|
191
|
+
"code": "seam-inventory",
|
|
192
|
+
"level": "log",
|
|
193
|
+
"message": "observed 1 hooks, 1 registrations, and 1 manifest contracts",
|
|
194
|
+
"evidence": [
|
|
195
|
+
"hook:before_tool_call",
|
|
196
|
+
"registration:definePluginEntry",
|
|
197
|
+
"manifestContract:trustedToolPolicies"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"fixture": "openclaw-agent-acl",
|
|
202
|
+
"code": "package-metadata",
|
|
203
|
+
"level": "log",
|
|
204
|
+
"message": "selected package metadata for plugin contract checks",
|
|
205
|
+
"evidence": [
|
|
206
|
+
"package.json",
|
|
207
|
+
"@randuhmm/openclaw-agent-acl",
|
|
208
|
+
"version:1.0.0"
|
|
209
|
+
]
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"fixture": "openclaw-agent-acl",
|
|
213
|
+
"code": "declarative-contracts",
|
|
214
|
+
"level": "log",
|
|
215
|
+
"message": "fixture declares manifest contracts that can be checked without executing plugin code",
|
|
216
|
+
"evidence": [
|
|
217
|
+
"trustedToolPolicies"
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"fixture": "openclaw",
|
|
222
|
+
"code": "target-openclaw-unavailable",
|
|
223
|
+
"level": "log",
|
|
224
|
+
"message": "target OpenClaw checkout was not available, so compat record coverage was not checked",
|
|
225
|
+
"evidence": [
|
|
226
|
+
"not configured"
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
"decisions": []
|
|
231
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# OpenClaw Plugin Compatibility Report
|
|
2
|
+
|
|
3
|
+
Generated: deterministic
|
|
4
|
+
Status: PASS
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
| Metric | Value |
|
|
9
|
+
| -------------------------- | ----- |
|
|
10
|
+
| Fixtures | 1 |
|
|
11
|
+
| High-priority fixtures | 1 |
|
|
12
|
+
| Hard breakages | 0 |
|
|
13
|
+
| Warnings | 0 |
|
|
14
|
+
| Compatibility suggestions | 0 |
|
|
15
|
+
| Issue findings | 0 |
|
|
16
|
+
| Open issue findings | 0 |
|
|
17
|
+
| Runtime-covered findings | 0 |
|
|
18
|
+
| Runtime-partial findings | 0 |
|
|
19
|
+
| P0 issues | 0 |
|
|
20
|
+
| P1 issues | 0 |
|
|
21
|
+
| Open P0 issues | 0 |
|
|
22
|
+
| Open P1 issues | 0 |
|
|
23
|
+
| Live issues | 0 |
|
|
24
|
+
| Live P0 issues | 0 |
|
|
25
|
+
| Compat gaps | 0 |
|
|
26
|
+
| Deprecation warnings | 0 |
|
|
27
|
+
| Inspector gaps | 0 |
|
|
28
|
+
| Open inspector gaps | 0 |
|
|
29
|
+
| Runtime coverage artifacts | 0 |
|
|
30
|
+
| Upstream metadata | 0 |
|
|
31
|
+
| Contract probes | 0 |
|
|
32
|
+
| Decision rows | 0 |
|
|
33
|
+
|
|
34
|
+
## Triage Overview
|
|
35
|
+
|
|
36
|
+
| Class | Count | P0 | Meaning |
|
|
37
|
+
| ------------------- | ----- | -- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| live-issue | 0 | 0 | Potential runtime breakage in the target OpenClaw/plugin pair. P0 only when it is not a deprecated compat seam. |
|
|
39
|
+
| compat-gap | 0 | - | Compatibility behavior is needed but missing from the target OpenClaw compat registry. |
|
|
40
|
+
| deprecation-warning | 0 | - | Plugin uses a supported but deprecated compatibility seam; keep it wired while migration exists. |
|
|
41
|
+
| inspector-gap | 0 | - | Plugin Inspector needs stronger capture/probe evidence before making contract judgments. Runtime-covered rows are proof-backed and not open report work. |
|
|
42
|
+
| upstream-metadata | 0 | - | Plugin package or manifest metadata should improve upstream; not a target OpenClaw live break by itself. |
|
|
43
|
+
| fixture-regression | 0 | - | Fixture no longer exposes an expected seam; investigate fixture pin or scanner drift. |
|
|
44
|
+
|
|
45
|
+
## P0 Live Issues
|
|
46
|
+
|
|
47
|
+
_none_
|
|
48
|
+
|
|
49
|
+
## Other Live Issues
|
|
50
|
+
|
|
51
|
+
_none_
|
|
52
|
+
|
|
53
|
+
## Compat Gaps
|
|
54
|
+
|
|
55
|
+
_none_
|
|
56
|
+
|
|
57
|
+
## Deprecation Warnings
|
|
58
|
+
|
|
59
|
+
_none_
|
|
60
|
+
|
|
61
|
+
## Inspector Proof Gaps
|
|
62
|
+
|
|
63
|
+
_none_
|
|
64
|
+
|
|
65
|
+
## Runtime-Covered Inspector Gaps
|
|
66
|
+
|
|
67
|
+
_none_
|
|
68
|
+
|
|
69
|
+
## Upstream Metadata Issues
|
|
70
|
+
|
|
71
|
+
_none_
|
|
72
|
+
|
|
73
|
+
## Hard Breakages
|
|
74
|
+
|
|
75
|
+
_none_
|
|
76
|
+
|
|
77
|
+
## Target OpenClaw Compat Records
|
|
78
|
+
|
|
79
|
+
| Metric | Value |
|
|
80
|
+
| ------------------------ | -------- |
|
|
81
|
+
| Configured path | - |
|
|
82
|
+
| Status | disabled |
|
|
83
|
+
| Compat registry | - |
|
|
84
|
+
| Compat records | 0 |
|
|
85
|
+
| Compat status counts | - |
|
|
86
|
+
| Record ids | - |
|
|
87
|
+
| Hook registry | - |
|
|
88
|
+
| Hook names | 0 |
|
|
89
|
+
| API builder | - |
|
|
90
|
+
| API registrars | 0 |
|
|
91
|
+
| Captured registration | - |
|
|
92
|
+
| Captured registrars | 0 |
|
|
93
|
+
| Package metadata | - |
|
|
94
|
+
| Plugin SDK exports | 0 |
|
|
95
|
+
| Manifest types | - |
|
|
96
|
+
| Manifest fields | 0 |
|
|
97
|
+
| Manifest contract fields | 0 |
|
|
98
|
+
|
|
99
|
+
## Warnings
|
|
100
|
+
|
|
101
|
+
_none_
|
|
102
|
+
|
|
103
|
+
## Suggestions To OpenClaw Compat Layer
|
|
104
|
+
|
|
105
|
+
_none_
|
|
106
|
+
|
|
107
|
+
## Issue Findings
|
|
108
|
+
|
|
109
|
+
_none_
|
|
110
|
+
|
|
111
|
+
## Contract Probe Backlog
|
|
112
|
+
|
|
113
|
+
_none_
|
|
114
|
+
|
|
115
|
+
## Fixture Seam Inventory
|
|
116
|
+
|
|
117
|
+
| Fixture | Priority | Seams | Hooks | Registrations | Manifest contracts |
|
|
118
|
+
| ------------------ | -------- | -------------- | ---------------- | ----------------- | ------------------- |
|
|
119
|
+
| openclaw-agent-acl | high | plugin-runtime | before_tool_call | definePluginEntry | trustedToolPolicies |
|
|
120
|
+
|
|
121
|
+
## Decision Matrix
|
|
122
|
+
|
|
123
|
+
_none_
|
|
124
|
+
|
|
125
|
+
## Raw Logs
|
|
126
|
+
|
|
127
|
+
| Fixture | Code | Level | Message | Evidence | Compat record |
|
|
128
|
+
| ------------------ | --------------------------- | ----- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------- |
|
|
129
|
+
| openclaw-agent-acl | seam-inventory | log | observed 1 hooks, 1 registrations, and 1 manifest contracts | hook:before_tool_call, registration:definePluginEntry, manifestContract:trustedToolPolicies | - |
|
|
130
|
+
| openclaw-agent-acl | package-metadata | log | selected package metadata for plugin contract checks | package.json, @randuhmm/openclaw-agent-acl, version:1.0.0 | - |
|
|
131
|
+
| openclaw-agent-acl | declarative-contracts | log | fixture declares manifest contracts that can be checked without executing plugin code | trustedToolPolicies | - |
|
|
132
|
+
| openclaw | target-openclaw-unavailable | log | target OpenClaw checkout was not available, so compat record coverage was not checked | not configured | - |
|