@aporthq/aport-agent-guardrails 1.0.22 → 1.0.23
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/bin/openclaw +73 -22
- package/docs/RELEASE.md +1 -1
- package/docs/TOOL_POLICY_MAPPING.md +2 -2
- package/docs/frameworks/openclaw.md +16 -1
- package/extensions/openclaw-aport/CHANGELOG.md +7 -0
- package/extensions/openclaw-aport/README.md +30 -2
- package/extensions/openclaw-aport/api-client.js +9 -1
- package/extensions/openclaw-aport/index.js +10 -11
- package/extensions/openclaw-aport/openclaw.plugin.json +1 -1
- package/extensions/openclaw-aport/package-lock.json +2 -2
- package/extensions/openclaw-aport/package.json +1 -1
- package/extensions/openclaw-aport/tool-mapping.js +212 -25
- package/package.json +1 -1
package/bin/openclaw
CHANGED
|
@@ -30,6 +30,48 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
30
30
|
APORT_PLUGIN_PATH="$REPO_ROOT/extensions/openclaw-aport"
|
|
31
31
|
DEFAULT_CONFIG="${OPENCLAW_HOME:-$HOME/.openclaw}"
|
|
32
32
|
|
|
33
|
+
read_local_plugin_version() {
|
|
34
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
local package_json="$APORT_PLUGIN_PATH/package.json"
|
|
38
|
+
if [ ! -f "$package_json" ]; then
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
node -e '
|
|
42
|
+
const fs = require("node:fs");
|
|
43
|
+
try {
|
|
44
|
+
const pkg = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
45
|
+
if (pkg && typeof pkg.version === "string") process.stdout.write(pkg.version);
|
|
46
|
+
} catch {}
|
|
47
|
+
' "$package_json"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
read_installed_plugin_version() {
|
|
51
|
+
if ! command -v openclaw >/dev/null 2>&1 || ! command -v node >/dev/null 2>&1; then
|
|
52
|
+
return 0
|
|
53
|
+
fi
|
|
54
|
+
openclaw plugins list --json 2>/dev/null | node -e '
|
|
55
|
+
let raw = "";
|
|
56
|
+
process.stdin.on("data", (chunk) => {
|
|
57
|
+
raw += chunk;
|
|
58
|
+
});
|
|
59
|
+
process.stdin.on("end", () => {
|
|
60
|
+
const jsonStart = raw.indexOf("{");
|
|
61
|
+
if (jsonStart < 0) return;
|
|
62
|
+
try {
|
|
63
|
+
const data = JSON.parse(raw.slice(jsonStart));
|
|
64
|
+
const plugin = Array.isArray(data.plugins)
|
|
65
|
+
? data.plugins.find((entry) => entry && entry.id === "openclaw-aport")
|
|
66
|
+
: null;
|
|
67
|
+
if (plugin && typeof plugin.version === "string") {
|
|
68
|
+
process.stdout.write(plugin.version);
|
|
69
|
+
}
|
|
70
|
+
} catch {}
|
|
71
|
+
});
|
|
72
|
+
'
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
# Parse command line arguments
|
|
34
76
|
HOSTED_AGENT_ID=""
|
|
35
77
|
if [ -n "$1" ]; then
|
|
@@ -210,19 +252,29 @@ if true; then
|
|
|
210
252
|
echo " Skipping plugin installation."
|
|
211
253
|
fi
|
|
212
254
|
else
|
|
255
|
+
LOCAL_PLUGIN_VERSION="$(read_local_plugin_version)"
|
|
256
|
+
INSTALLED_PLUGIN_VERSION="$(read_installed_plugin_version)"
|
|
213
257
|
echo ""
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if openclaw plugins install -l "$APORT_PLUGIN_PATH" 2>&1; then
|
|
217
|
-
echo " ✅ Plugin installed successfully"
|
|
258
|
+
if [ -n "$LOCAL_PLUGIN_VERSION" ] && [ "$LOCAL_PLUGIN_VERSION" = "$INSTALLED_PLUGIN_VERSION" ]; then
|
|
259
|
+
echo " ✅ Plugin version $LOCAL_PLUGIN_VERSION already installed; skipping reinstall."
|
|
218
260
|
PLUGIN_INSTALLED=true
|
|
219
261
|
else
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
echo "
|
|
224
|
-
echo "
|
|
225
|
-
|
|
262
|
+
if [ -n "$INSTALLED_PLUGIN_VERSION" ]; then
|
|
263
|
+
echo " Existing openclaw-aport version: $INSTALLED_PLUGIN_VERSION"
|
|
264
|
+
fi
|
|
265
|
+
echo " Installing plugin from: $APORT_PLUGIN_PATH"
|
|
266
|
+
echo " (Using -l to link local directory; OpenClaw accepts only registry or --link for install.)"
|
|
267
|
+
if openclaw plugins install -l "$APORT_PLUGIN_PATH" 2>&1; then
|
|
268
|
+
echo " ✅ Plugin installed successfully"
|
|
269
|
+
PLUGIN_INSTALLED=true
|
|
270
|
+
else
|
|
271
|
+
echo " ❌ Plugin installation failed."
|
|
272
|
+
echo " OpenClaw did not accept the plugin bundle, so setup is stopping here"
|
|
273
|
+
echo " instead of writing plugin config that points at a broken install."
|
|
274
|
+
echo " Retry after fixing the bundle or install it manually:"
|
|
275
|
+
echo " openclaw plugins install -l $APORT_PLUGIN_PATH"
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
226
278
|
fi
|
|
227
279
|
echo ""
|
|
228
280
|
|
|
@@ -590,19 +642,18 @@ echo " OpenClaw (or your code) calls the guardrail with a tool name and contex
|
|
|
590
642
|
echo " The guardrail maps that to a policy pack in external/aport-policies:"
|
|
591
643
|
echo
|
|
592
644
|
cat << 'TABLE'
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
payment.charge → finance.payment.charge.v1
|
|
603
|
-
database.write, data.export → data.export.create.v1
|
|
645
|
+
Tool name (examples) → Policy pack
|
|
646
|
+
------------------------------------------------
|
|
647
|
+
git.create_pr, git.merge, git.* → code.repository.merge.v1
|
|
648
|
+
exec.run, system.command.* → system.command.execute.v1
|
|
649
|
+
message + send/reply/broadcast → messaging.message.send.v1
|
|
650
|
+
message + sendAttachment/react → messaging.message.send.v1
|
|
651
|
+
serverName__toolName, mcp__* → mcp.tool.execute.v1
|
|
652
|
+
read, glob, grep → data.file.read.v1
|
|
653
|
+
write, edit, multiedit → data.file.write.v1
|
|
604
654
|
TABLE
|
|
605
|
-
echo "
|
|
655
|
+
echo " Plugin mapping source: extensions/openclaw-aport/tool-mapping.js"
|
|
656
|
+
echo " Unmapped tools are allowed by default; set allowUnmappedTools: false for strict mode."
|
|
606
657
|
echo
|
|
607
658
|
|
|
608
659
|
# 5. Enforcement summary
|
package/docs/RELEASE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release process and version policy
|
|
2
2
|
|
|
3
|
-
**Current release:** 1.0.
|
|
3
|
+
**Current release:** 1.0.23 (see [CHANGELOG.md](../CHANGELOG.md)).
|
|
4
4
|
|
|
5
5
|
We keep **one version number** across all published packages (Node core, Python core, and every framework adapter). That avoids “core is 1.2 but CLI is 0.9” and keeps the story simple for users and support.
|
|
6
6
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Tool → policy pack mapping
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The shell/API guardrail entrypoints invoke the guardrail with a **tool name** and **context JSON**. The guardrail maps the tool name to a **policy pack** in `external/aport-policies/` and evaluates the request against that policy and the passport.
|
|
4
4
|
|
|
5
|
-
This mapping is implemented in `bin/aport-guardrail-api.sh` and `bin/aport-guardrail-bash.sh`. The
|
|
5
|
+
This mapping is implemented in `bin/aport-guardrail-api.sh` and `bin/aport-guardrail-bash.sh`. The OpenClaw plugin has its own host-specific mapping in `extensions/openclaw-aport/tool-mapping.js`.
|
|
6
6
|
|
|
7
7
|
## Mapping table
|
|
8
8
|
|
|
@@ -14,6 +14,20 @@ If you already have a hosted passport on aport.io, pass the `agent_id` and skip
|
|
|
14
14
|
npx @aporthq/aport-agent-guardrails openclaw ap_your_agent_id
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
If you run `openclaw plugins install @aporthq/openclaw-aport` directly, that installs only the plugin bundle. It does not create a local passport or write the plugin config. Use the setup command above for the full APort + OpenClaw flow, or configure the plugin manually.
|
|
18
|
+
|
|
19
|
+
After a direct plugin install, the recommended next step is:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @aporthq/aport-agent-guardrails openclaw
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Hosted passport:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @aporthq/aport-agent-guardrails openclaw ap_your_agent_id
|
|
29
|
+
```
|
|
30
|
+
|
|
17
31
|
The setup command:
|
|
18
32
|
|
|
19
33
|
1. Chooses your OpenClaw config directory
|
|
@@ -137,9 +151,10 @@ Common mappings include:
|
|
|
137
151
|
|
|
138
152
|
- `exec`, `exec.run` -> `system.command.execute.v1`
|
|
139
153
|
- `git.create_pr`, `git.merge`, `git.push` -> `code.repository.merge.v1`
|
|
140
|
-
- `message
|
|
154
|
+
- `message` with send-family actions like `send`, `reply`, `broadcast`, `sendAttachment`, `upload-file`, or `react` -> `messaging.message.send.v1`
|
|
141
155
|
- `read`, `view`, `glob` -> `data.file.read.v1`
|
|
142
156
|
- `write`, `edit`, `multiedit` -> `data.file.write.v1`
|
|
157
|
+
- bundle MCP tools exposed as `serverName__toolName` -> `mcp.tool.execute.v1`
|
|
143
158
|
|
|
144
159
|
## Kill switch
|
|
145
160
|
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
# Changelog - APort OpenClaw Plugin
|
|
2
2
|
|
|
3
|
+
## 1.0.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix the OpenClaw plugin and setup flow for current public OpenClaw releases. This update makes the plugin installer and setup flow more reliable, tightens tool-to-policy mapping for current OpenClaw tools like `message` and MCP bundle tools, normalizes hosted API context for file and message tools, and skips redundant plugin reinstall when the same version is already installed.
|
|
8
|
+
|
|
3
9
|
All notable changes to the APort OpenClaw plugin will be documented in this file.
|
|
4
10
|
|
|
5
11
|
## [1.0.22] - 2026-04-13
|
|
6
12
|
|
|
7
13
|
### Fixed
|
|
14
|
+
|
|
8
15
|
- Replaced the old shell-spawning runtime with a scanner-safe JavaScript plugin runtime for local and API evaluation.
|
|
9
16
|
- Added current OpenClaw compatibility metadata in `package.json` and aligned the hook return shape with the documented `before_tool_call` contract.
|
|
10
17
|
- Kept `alwaysVerifyEachToolCall` as a deprecated manifest field so existing installs continue to load during upgrade.
|
|
@@ -33,6 +33,28 @@ After setup, start OpenClaw with the generated config:
|
|
|
33
33
|
openclaw gateway start --config ~/.openclaw/config.yaml
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## If you installed with `openclaw plugins install`
|
|
37
|
+
|
|
38
|
+
If you installed the plugin directly with:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
openclaw plugins install @aporthq/openclaw-aport
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
that installs only the plugin bundle. It does not create a passport, choose API vs local mode, or write plugin config.
|
|
45
|
+
|
|
46
|
+
Run the full APort setup immediately after install:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @aporthq/aport-agent-guardrails openclaw
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
If you already have a hosted passport, use:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx @aporthq/aport-agent-guardrails openclaw ap_your_agent_id
|
|
56
|
+
```
|
|
57
|
+
|
|
36
58
|
## What OpenClaw already gives you
|
|
37
59
|
|
|
38
60
|
OpenClaw already ships sandboxing, tool policy, elevated exec controls, and install-time scanning. Those are real security controls, not marketing copy.
|
|
@@ -57,6 +79,12 @@ If you are working from a local checkout, install the plugin directly from the e
|
|
|
57
79
|
openclaw plugins install -l /path/to/aport-agent-guardrails/extensions/openclaw-aport
|
|
58
80
|
```
|
|
59
81
|
|
|
82
|
+
That command installs only the OpenClaw plugin bundle. It does not create a passport, choose API vs local mode, or write plugin config. For a full working setup, use:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx @aporthq/aport-agent-guardrails openclaw
|
|
86
|
+
```
|
|
87
|
+
|
|
60
88
|
Then configure it in your OpenClaw config:
|
|
61
89
|
|
|
62
90
|
```yaml
|
|
@@ -95,10 +123,10 @@ The plugin keeps the existing OpenClaw-specific tool mappings. Common examples:
|
|
|
95
123
|
|
|
96
124
|
- `exec`, `exec.run` -> `system.command.execute.v1`
|
|
97
125
|
- `git.create_pr`, `git.merge`, `git.push` -> `code.repository.merge.v1`
|
|
98
|
-
- `message
|
|
126
|
+
- `message` with send-family actions like `send`, `reply`, `broadcast`, `sendAttachment`, `upload-file`, or `react` -> `messaging.message.send.v1`
|
|
99
127
|
- `read`, `view`, `glob` -> `data.file.read.v1`
|
|
100
128
|
- `write`, `edit`, `multiedit` -> `data.file.write.v1`
|
|
101
|
-
- `
|
|
129
|
+
- bundle MCP tools exposed as `serverName__toolName` -> `mcp.tool.execute.v1`
|
|
102
130
|
|
|
103
131
|
`allowUnmappedTools: true` keeps the previous OpenClaw compatibility behavior for custom skills and unmapped tools.
|
|
104
132
|
|
|
@@ -14,7 +14,15 @@ export async function verifyViaApi({ apiUrl, apiKey, policyName, context, passpo
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
if (!response.ok) {
|
|
17
|
-
|
|
17
|
+
let details = "";
|
|
18
|
+
try {
|
|
19
|
+
const text = await response.text();
|
|
20
|
+
if (text) details = text;
|
|
21
|
+
} catch {
|
|
22
|
+
details = "";
|
|
23
|
+
}
|
|
24
|
+
const suffix = details ? ` - ${details}` : "";
|
|
25
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText}${suffix}`);
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
const data = await response.json();
|
|
@@ -13,7 +13,7 @@ import { homedir } from "node:os";
|
|
|
13
13
|
import { logAuditEntry } from "./audit.js";
|
|
14
14
|
import { canonicalize, formatReasons, verifyDecisionIntegrity } from "./decision.js";
|
|
15
15
|
import { evaluateLocalDecision } from "./local-evaluator.js";
|
|
16
|
-
import { mapToolToPolicy,
|
|
16
|
+
import { mapToolToPolicy, normalizePolicyContext } from "./tool-mapping.js";
|
|
17
17
|
import { verifyViaApi } from "./api-client.js";
|
|
18
18
|
|
|
19
19
|
export { canonicalize, mapToolToPolicy, verifyDecisionIntegrity };
|
|
@@ -48,7 +48,7 @@ export default definePluginEntry({
|
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
50
|
const policyName =
|
|
51
|
-
toolName === "exec" && !mapExecToPolicy ? null : mapToolToPolicy(toolName);
|
|
51
|
+
toolName === "exec" && !mapExecToPolicy ? null : mapToolToPolicy(toolName, params);
|
|
52
52
|
|
|
53
53
|
if (!policyName) {
|
|
54
54
|
if (allowUnmappedTools) {
|
|
@@ -64,23 +64,22 @@ export default definePluginEntry({
|
|
|
64
64
|
|
|
65
65
|
let effectivePolicyName = policyName;
|
|
66
66
|
let effectiveToolName = toolName;
|
|
67
|
-
let context =
|
|
68
|
-
policyName === "system.command.execute.v1"
|
|
69
|
-
? normalizeExecContext(params, event)
|
|
70
|
-
: (params || {});
|
|
67
|
+
let context = normalizePolicyContext(policyName, toolName, params, event);
|
|
71
68
|
|
|
72
69
|
const delegated = parseGuardrailInvocation(
|
|
73
70
|
effectivePolicyName === "system.command.execute.v1" ? context.command : null,
|
|
74
71
|
);
|
|
75
72
|
if (delegated) {
|
|
76
|
-
const innerPolicy = mapToolToPolicy(delegated.innerToolName);
|
|
73
|
+
const innerPolicy = mapToolToPolicy(delegated.innerToolName, delegated.innerContext);
|
|
77
74
|
if (innerPolicy) {
|
|
78
75
|
effectivePolicyName = innerPolicy;
|
|
79
76
|
effectiveToolName = delegated.innerToolName;
|
|
80
|
-
context =
|
|
81
|
-
innerPolicy
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
context = normalizePolicyContext(
|
|
78
|
+
innerPolicy,
|
|
79
|
+
delegated.innerToolName,
|
|
80
|
+
delegated.innerContext,
|
|
81
|
+
{ params: delegated.innerContext },
|
|
82
|
+
);
|
|
84
83
|
}
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-aport",
|
|
3
3
|
"name": "APort Guardrails",
|
|
4
4
|
"description": "Deterministic pre-action authorization via APort policy enforcement. Registers before_tool_call to block disallowed tools.",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.23",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aporthq/openclaw-aport",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@aporthq/openclaw-aport",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.23",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@types/node": "^18.0.0",
|
|
@@ -1,5 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const MESSAGE_SEND_ACTIONS = new Set([
|
|
2
|
+
"send",
|
|
3
|
+
"broadcast",
|
|
4
|
+
"reply",
|
|
5
|
+
"thread-reply",
|
|
6
|
+
"sendwitheffect",
|
|
7
|
+
"sendattachment",
|
|
8
|
+
"upload-file",
|
|
9
|
+
"react",
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
function firstNonEmpty(...values) {
|
|
13
|
+
for (const value of values) {
|
|
14
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
15
|
+
}
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readAction(params) {
|
|
20
|
+
const src = params && typeof params === "object" ? params : {};
|
|
21
|
+
return firstNonEmpty(
|
|
22
|
+
src.action,
|
|
23
|
+
src.action_name,
|
|
24
|
+
src.actionName,
|
|
25
|
+
src.arguments && typeof src.arguments === "object" ? src.arguments.action : "",
|
|
26
|
+
src.input && typeof src.input === "object" ? src.input.action : "",
|
|
27
|
+
).toLowerCase();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function parseMcpToolName(toolName) {
|
|
31
|
+
const raw = String(toolName ?? "").trim();
|
|
32
|
+
if (!raw) return null;
|
|
33
|
+
|
|
34
|
+
if (raw.startsWith("mcp__")) {
|
|
35
|
+
const parts = raw.split("__");
|
|
36
|
+
if (parts.length >= 3 && parts[1] && parts.slice(2).join("__")) {
|
|
37
|
+
return {
|
|
38
|
+
serverName: parts[1],
|
|
39
|
+
toolName: parts.slice(2).join("__"),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const separatorIndex = raw.indexOf("__");
|
|
45
|
+
if (separatorIndex <= 0 || separatorIndex >= raw.length - 2) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
serverName: raw.slice(0, separatorIndex),
|
|
51
|
+
toolName: raw.slice(separatorIndex + 2),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function mapToolToPolicy(toolName, params) {
|
|
56
|
+
const rawTool = String(toolName ?? "").trim();
|
|
57
|
+
const tool = rawTool.toLowerCase();
|
|
3
58
|
|
|
4
59
|
if (tool.match(/git\.(create_pr|merge|push|commit)/)) return "code.repository.merge.v1";
|
|
5
60
|
if (tool.startsWith("git.")) return "code.repository.merge.v1";
|
|
@@ -10,9 +65,21 @@ export function mapToolToPolicy(toolName) {
|
|
|
10
65
|
if (tool.startsWith("system.command.")) return "system.command.execute.v1";
|
|
11
66
|
if (tool === "bash" || tool === "shell" || tool === "command") return "system.command.execute.v1";
|
|
12
67
|
|
|
13
|
-
if (tool
|
|
14
|
-
|
|
15
|
-
|
|
68
|
+
if (tool === "message") {
|
|
69
|
+
return MESSAGE_SEND_ACTIONS.has(readAction(params)) ? "messaging.message.send.v1" : null;
|
|
70
|
+
}
|
|
71
|
+
if (
|
|
72
|
+
tool === "message.send" ||
|
|
73
|
+
tool === "message.reply" ||
|
|
74
|
+
tool === "message.broadcast" ||
|
|
75
|
+
tool === "message.react" ||
|
|
76
|
+
tool === "messaging.message.send"
|
|
77
|
+
) {
|
|
78
|
+
return "messaging.message.send.v1";
|
|
79
|
+
}
|
|
80
|
+
if (tool.match(/(^|[._-])(whatsapp|telegram|slack|email)([._-](send|reply|broadcast|message|react))?$/)) {
|
|
81
|
+
return "messaging.message.send.v1";
|
|
82
|
+
}
|
|
16
83
|
|
|
17
84
|
if (tool === "read") return "data.file.read.v1";
|
|
18
85
|
if (tool.startsWith("file.read")) return "data.file.read.v1";
|
|
@@ -24,13 +91,8 @@ export function mapToolToPolicy(toolName) {
|
|
|
24
91
|
}
|
|
25
92
|
if (tool === "todoread") return "data.file.read.v1";
|
|
26
93
|
if (tool === "todowrite") return "data.file.write.v1";
|
|
27
|
-
if (tool === "task" || tool === "taskcreate" || tool === "taskupdate" || tool === "taskstop") {
|
|
28
|
-
return "agent.session.create.v1";
|
|
29
|
-
}
|
|
30
94
|
if (tool === "taskget" || tool === "tasklist" || tool === "taskoutput") return "data.file.read.v1";
|
|
31
|
-
if (tool === "agent" || tool === "skill" || tool === "enterworktree") return "agent.session.create.v1";
|
|
32
95
|
if (tool === "askuserquestion" || tool === "enterplanmode" || tool === "exitplanmode") return null;
|
|
33
|
-
if (tool === "croncreate" || tool === "crondelete") return "agent.session.create.v1";
|
|
34
96
|
if (tool === "cronlist") return "data.file.read.v1";
|
|
35
97
|
if (tool.startsWith("file.write")) return "data.file.write.v1";
|
|
36
98
|
if (tool.startsWith("file.edit")) return "data.file.write.v1";
|
|
@@ -45,25 +107,11 @@ export function mapToolToPolicy(toolName) {
|
|
|
45
107
|
if (tool.startsWith("browser.")) return "web.browser.v1";
|
|
46
108
|
|
|
47
109
|
if (tool.startsWith("mcp.")) return "mcp.tool.execute.v1";
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
if (tool.match(/agent\.session|session\.create/)) return "agent.session.create.v1";
|
|
51
|
-
if (tool === "sessions_spawn" || tool === "sessions_send") return "agent.session.create.v1";
|
|
52
|
-
if (tool.startsWith("session.") || tool.startsWith("sessions.")) return "agent.session.create.v1";
|
|
53
|
-
if (tool === "cron" || tool.startsWith("cron.")) return "agent.session.create.v1";
|
|
110
|
+
if (parseMcpToolName(rawTool)) return "mcp.tool.execute.v1";
|
|
54
111
|
|
|
55
112
|
if (tool === "gateway" || tool.startsWith("gateway.")) return "system.command.execute.v1";
|
|
56
113
|
if (tool === "process" || tool.startsWith("process.")) return "system.command.execute.v1";
|
|
57
114
|
|
|
58
|
-
if (tool.match(/agent\.tool|tool\.register/)) return "agent.tool.register.v1";
|
|
59
|
-
|
|
60
|
-
if (tool.match(/payment\.refund|refund/)) return "finance.payment.refund.v1";
|
|
61
|
-
if (tool.match(/payment\.charge|charge/)) return "finance.payment.charge.v1";
|
|
62
|
-
if (tool.startsWith("finance.")) return "finance.payment.refund.v1";
|
|
63
|
-
|
|
64
|
-
if (tool.match(/database\.(write|insert|update|delete)/)) return "data.export.create.v1";
|
|
65
|
-
if (tool.match(/data\.export|export/)) return "data.export.create.v1";
|
|
66
|
-
|
|
67
115
|
return null;
|
|
68
116
|
}
|
|
69
117
|
|
|
@@ -87,3 +135,142 @@ export function normalizeExecContext(params, event) {
|
|
|
87
135
|
if (params && params.workdir !== undefined && out.cwd === undefined) out.cwd = params.workdir;
|
|
88
136
|
return out;
|
|
89
137
|
}
|
|
138
|
+
|
|
139
|
+
export function normalizeFileContext(params) {
|
|
140
|
+
const src = params && typeof params === "object" ? params : {};
|
|
141
|
+
const filePath =
|
|
142
|
+
src.file_path ??
|
|
143
|
+
src.path ??
|
|
144
|
+
src.filePath ??
|
|
145
|
+
(src.arguments && typeof src.arguments === "object" ? src.arguments.file_path ?? src.arguments.path : null) ??
|
|
146
|
+
(src.input && typeof src.input === "object" ? src.input.file_path ?? src.input.path : null) ??
|
|
147
|
+
(src.args && typeof src.args === "object" ? src.args.file_path ?? src.args.path : null);
|
|
148
|
+
|
|
149
|
+
if (filePath == null || String(filePath).trim() === "") {
|
|
150
|
+
return { ...src };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
...src,
|
|
155
|
+
file_path: String(filePath),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function normalizeMessageContext(params) {
|
|
160
|
+
const src = params && typeof params === "object" ? params : {};
|
|
161
|
+
const action = readAction(src);
|
|
162
|
+
const firstTarget = Array.isArray(src.targets)
|
|
163
|
+
? src.targets.find((value) => typeof value === "string" && value.trim())
|
|
164
|
+
: "";
|
|
165
|
+
const channelId = firstNonEmpty(
|
|
166
|
+
src.channel_id,
|
|
167
|
+
src.channelId,
|
|
168
|
+
src.target,
|
|
169
|
+
firstTarget,
|
|
170
|
+
src.channel,
|
|
171
|
+
src.to,
|
|
172
|
+
src.accountId,
|
|
173
|
+
);
|
|
174
|
+
const isAttachmentAction =
|
|
175
|
+
action === "sendattachment" ||
|
|
176
|
+
action === "upload-file" ||
|
|
177
|
+
Boolean(src.media || src.buffer || src.path || src.filePath || src.filename);
|
|
178
|
+
const messageType = action === "react" ? "reaction" : isAttachmentAction ? "file" : "text";
|
|
179
|
+
const message = firstNonEmpty(
|
|
180
|
+
src.message,
|
|
181
|
+
src.text,
|
|
182
|
+
src.content,
|
|
183
|
+
src.caption,
|
|
184
|
+
src.quoteText,
|
|
185
|
+
src.emoji,
|
|
186
|
+
isAttachmentAction ? src.filename : "",
|
|
187
|
+
);
|
|
188
|
+
const threadId = firstNonEmpty(src.thread_id, src.threadId);
|
|
189
|
+
const replyTo = firstNonEmpty(src.reply_to, src.replyTo, src.messageId, src.message_id);
|
|
190
|
+
const out = {
|
|
191
|
+
...src,
|
|
192
|
+
message_type: src.message_type ?? messageType,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
if (channelId) out.channel_id = channelId;
|
|
196
|
+
if (message) out.message = message;
|
|
197
|
+
else if (messageType === "reaction") out.message = "reaction";
|
|
198
|
+
else if (messageType === "file") out.message = "[attachment]";
|
|
199
|
+
|
|
200
|
+
if (threadId) out.thread_id = threadId;
|
|
201
|
+
if (replyTo) out.reply_to = replyTo;
|
|
202
|
+
|
|
203
|
+
if (!Array.isArray(out.attachments) && isAttachmentAction) {
|
|
204
|
+
out.attachments = [
|
|
205
|
+
{
|
|
206
|
+
...(typeof src.media === "string" && src.media ? { url: src.media } : {}),
|
|
207
|
+
...(typeof src.filename === "string" && src.filename ? { filename: src.filename } : {}),
|
|
208
|
+
},
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function buildMcpParameters(src) {
|
|
216
|
+
if (src.parameters && typeof src.parameters === "object") return src.parameters;
|
|
217
|
+
if (src.arguments && typeof src.arguments === "object") return src.arguments;
|
|
218
|
+
if (src.input && typeof src.input === "object") return src.input;
|
|
219
|
+
if (src.payload && typeof src.payload === "object") return src.payload;
|
|
220
|
+
|
|
221
|
+
const {
|
|
222
|
+
server,
|
|
223
|
+
server_url,
|
|
224
|
+
serverUrl,
|
|
225
|
+
server_name,
|
|
226
|
+
serverName,
|
|
227
|
+
tool,
|
|
228
|
+
tool_name,
|
|
229
|
+
toolName,
|
|
230
|
+
parameters,
|
|
231
|
+
...rest
|
|
232
|
+
} = src;
|
|
233
|
+
return rest;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function normalizeMcpContext(toolName, params) {
|
|
237
|
+
const src = params && typeof params === "object" ? params : {};
|
|
238
|
+
const parsed = parseMcpToolName(toolName);
|
|
239
|
+
const serverName = firstNonEmpty(
|
|
240
|
+
src.server,
|
|
241
|
+
src.server_url,
|
|
242
|
+
src.serverUrl,
|
|
243
|
+
src.server_name,
|
|
244
|
+
src.serverName,
|
|
245
|
+
src.mcp_server,
|
|
246
|
+
parsed?.serverName,
|
|
247
|
+
);
|
|
248
|
+
const normalizedServer =
|
|
249
|
+
serverName && serverName.includes("://") ? serverName : serverName ? `mcp://${serverName}` : "";
|
|
250
|
+
const tool = firstNonEmpty(src.tool, src.tool_name, src.toolName, src.mcp_tool, parsed?.toolName);
|
|
251
|
+
const out = {
|
|
252
|
+
...src,
|
|
253
|
+
parameters: buildMcpParameters(src),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (normalizedServer) out.server = normalizedServer;
|
|
257
|
+
if (tool) out.tool = tool;
|
|
258
|
+
|
|
259
|
+
return out;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function normalizePolicyContext(policyName, toolName, params, event) {
|
|
263
|
+
if (policyName === "system.command.execute.v1") {
|
|
264
|
+
return normalizeExecContext(params, event);
|
|
265
|
+
}
|
|
266
|
+
if (policyName === "data.file.read.v1" || policyName === "data.file.write.v1") {
|
|
267
|
+
return normalizeFileContext(params);
|
|
268
|
+
}
|
|
269
|
+
if (policyName === "messaging.message.send.v1") {
|
|
270
|
+
return normalizeMessageContext(params);
|
|
271
|
+
}
|
|
272
|
+
if (policyName === "mcp.tool.execute.v1") {
|
|
273
|
+
return normalizeMcpContext(toolName, params);
|
|
274
|
+
}
|
|
275
|
+
return params || {};
|
|
276
|
+
}
|