@aporthq/aport-agent-guardrails 1.0.26 → 1.0.27

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # APort Claude Code hook: reads tool_name + tool_input from JSON stdin,
2
+ # APort Claude Code hook: reads tool_name + tool_input from JSON stdin (path-based Read uses guardrail).
3
3
  # maps to APort policy, calls guardrail, outputs hookSpecificOutput deny or exit 0.
4
4
  # Exit 0 = allow, exit 2 = block. Other exits = hook error (Claude Code may fail-open).
5
5
  # Output format: Claude Code official schema (hookSpecificOutput.permissionDecision), NOT Cursor format.
@@ -14,6 +14,8 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
14
14
  . "$ROOT_DIR/bin/aport-resolve-paths.sh"
15
15
  # shellcheck source=bin/lib/guardrail-mode.sh
16
16
  . "$ROOT_DIR/bin/lib/guardrail-mode.sh"
17
+ # shellcheck source=bin/lib/hook-read-policy.sh
18
+ . "$ROOT_DIR/bin/lib/hook-read-policy.sh"
17
19
  load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.claude}"
18
20
 
19
21
  GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
@@ -49,7 +51,8 @@ set -e
49
51
  if [ "$JQ_EXIT" -ne 0 ] || [ -z "$TOOL_NAME" ]; then
50
52
  TOOL_NAME="unknown"
51
53
  fi
52
- TOOL_NAME_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | tr '[:upper:]' '[:lower:]')"
54
+ # Strip permission-rule specifiers (e.g. Agent(Explore) -> Agent) before normalization.
55
+ TOOL_NAME_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | sed 's/(.*$//' | tr '[:upper:]' '[:lower:]')"
53
56
  set +e
54
57
  TOOL_INPUT="$(echo "$INPUT" | jq -c '.tool_input // {}' 2> /dev/null)"
55
58
  JQ_EXIT=$?
@@ -81,40 +84,45 @@ GUARDRAIL_TOOL=""
81
84
  CONTEXT_JSON="{}"
82
85
 
83
86
  case "$TOOL_NAME_NORM" in
84
- bash | shell)
87
+ bash | shell | powershell | monitor)
85
88
  GUARDRAIL_TOOL="bash"
86
- CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // "")}')"
89
+ CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // .script // "")}')"
87
90
  ;;
88
- read | glob | ls | grep | todoread | toolsearch | askuserquestion | readfile | semanticsearch)
89
- # Read-family + user-interaction tools: allow without calling evaluator
91
+ read | readfile | semanticsearch)
92
+ if ! aport_hook_try_read_evaluation "$TOOL_NAME_NORM" "$TOOL_INPUT"; then
93
+ exit 0
94
+ fi
95
+ ;;
96
+ glob | ls | grep | lsp | todoread | toolsearch | askuserquestion | listmcpresourcestool | readmcpresourcetool | waitformcpservers)
97
+ # Search/list/read tools without a single file_path: allow without evaluator
90
98
  exit 0
91
99
  ;;
92
- taskget | tasklist | taskoutput | cronlist)
93
- # Read-only task/cron queries: allow without evaluator
100
+ taskget | tasklist | taskoutput | cronlist | schedulewakeup | pushnotification)
101
+ # Read-only task/cron queries and notifications: allow without evaluator
94
102
  exit 0
95
103
  ;;
96
104
  enterplanmode | exitplanmode)
97
105
  # Internal state transitions: allow without evaluator
98
106
  exit 0
99
107
  ;;
100
- write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook)
108
+ write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook | shareonboardingguide)
101
109
  GUARDRAIL_TOOL="write"
102
110
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{file_path: (.file_path // .path // "")}')"
103
111
  ;;
104
112
  websearch | webfetch)
105
- GUARDRAIL_TOOL="webfetch"
106
- CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // .query // "")}')"
113
+ GUARDRAIL_TOOL="websearch"
114
+ CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // ""), query: (.query // "")}')"
107
115
  ;;
108
116
  browser)
109
117
  GUARDRAIL_TOOL="browser"
110
118
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // "")}')"
111
119
  ;;
112
- task | taskcreate | taskupdate | taskstop | agent | skill | enterworktree | subagent | subagentstart)
120
+ agent | task | taskcreate | taskupdate | taskstop | skill | enterworktree | exitworktree | subagent | subagentstart | sendmessage | teamcreate | teamdelete | remotetrigger)
113
121
  GUARDRAIL_TOOL="session.create"
114
- CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // "")}')"
122
+ CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // .task // .message // ""), subagent_type: (.subagent_type // .agent_type // "")}')"
115
123
  ;;
116
124
  croncreate | crondelete)
117
- GUARDRAIL_TOOL="cron"
125
+ GUARDRAIL_TOOL="session.create"
118
126
  CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .schedule // "")}')"
119
127
  ;;
120
128
  mcp__* | mcp:* | callmcptool)
@@ -134,6 +142,14 @@ if [ -n "$HOOK_DECISION_FILE" ]; then
134
142
  export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
135
143
  fi
136
144
 
145
+ # Read tools: send only file_path to the evaluator (Claude may attach large file bodies in tool_input).
146
+ if [ "$GUARDRAIL_TOOL" = "read" ]; then
147
+ CONTEXT_JSON="$(printf '%s' "$CONTEXT_JSON" | jq -c '{file_path: (.file_path // .path // "")}' 2> /dev/null || echo '{"file_path":""}')"
148
+ if [ -z "$(printf '%s' "$CONTEXT_JSON" | jq -r '.file_path // ""' 2> /dev/null)" ]; then
149
+ exit 0
150
+ fi
151
+ fi
152
+
137
153
  # Call core evaluator (guardrail expects tool name, not policy ID)
138
154
  set +e
139
155
  "$GUARDRAIL" "$GUARDRAIL_TOOL" "$CONTEXT_JSON" 2> /dev/null
@@ -18,6 +18,8 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
18
18
  . "$ROOT_DIR/bin/aport-resolve-paths.sh"
19
19
  # shellcheck source=bin/lib/guardrail-mode.sh
20
20
  . "$ROOT_DIR/bin/lib/guardrail-mode.sh"
21
+ # shellcheck source=bin/lib/hook-read-policy.sh
22
+ . "$ROOT_DIR/bin/lib/hook-read-policy.sh"
21
23
  load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}"
22
24
 
23
25
  GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
@@ -82,8 +84,10 @@ HOOK_EVENT="$(echo "$INPUT" | jq -r '.hook_event_name // ""' 2> /dev/null)"
82
84
  TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name // ""' 2> /dev/null)"
83
85
 
84
86
  if [ "$HOOK_EVENT" = "beforeReadFile" ] || { [ -z "$HOOK_EVENT" ] && [ -z "$TOOL_NAME" ] && echo "$INPUT" | jq -e '.file_path and .content' &> /dev/null; }; then
85
- # beforeReadFile: file reads allow without evaluator (same as Claude Code)
86
- exit 0
87
+ FILE_PATH="$(echo "$INPUT" | jq -r '.file_path // ""' 2> /dev/null || true)"
88
+ if ! aport_hook_try_read_evaluation_from_file_path "$FILE_PATH"; then
89
+ exit 0
90
+ fi
87
91
 
88
92
  elif [ "$HOOK_EVENT" = "subagentStart" ] || { [ -z "$HOOK_EVENT" ] && echo "$INPUT" | jq -e '.subagent_id' &> /dev/null; }; then
89
93
  # subagentStart: sub-agent spawning
@@ -96,36 +100,48 @@ elif [ "$HOOK_EVENT" = "beforeMCPExecution" ] || { [ -n "$TOOL_NAME" ] && echo "
96
100
  CONTEXT_JSON="$(safe_jq "$INPUT" '{tool_name: (.tool_name // ""), tool_input: (.tool_input // {})}')"
97
101
 
98
102
  elif [ -n "$TOOL_NAME" ]; then
99
- # preToolUse: Cursor tool names — Shell, Read, Write, Grep, Delete, Task, MCP:<name>
100
- # Normalize: trim whitespace, lowercase (patterns must match TOOL_NORM)
101
- TOOL_NORM="$(echo "$TOOL_NAME" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')"
103
+ # preToolUse: Shell, Read, Write, Grep, Delete, Task, WebSearch, Agent, MCP:*, etc.
104
+ # See docs/FRAMEWORK_TOOL_MAPPING_AUDIT.md and https://cursor.com/docs/agent/hooks
105
+ TOOL_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | sed 's/(.*$//' | tr '[:upper:]' '[:lower:]')"
102
106
  case "$TOOL_NORM" in
103
- shell)
107
+ shell | bash)
104
108
  GUARDRAIL_TOOL="bash"
105
109
  CONTEXT_JSON="$(safe_jq "$INPUT" '{command: (.tool_input.command // "")}')"
106
110
  ;;
107
- read | grep | glob | semanticsearch)
108
- # Read-family: allow without calling evaluator (matches Claude Code behavior)
111
+ read | readfile | semanticsearch)
112
+ TOOL_INPUT="$(safe_jq "$INPUT" '.tool_input // {}')"
113
+ if ! aport_hook_try_read_evaluation "$TOOL_NORM" "$TOOL_INPUT"; then
114
+ exit 0
115
+ fi
116
+ ;;
117
+ grep | glob | ls | lsp | listmcpresourcestool | readmcpresourcetool | toolsearch | waitformcpservers | taskget | tasklist | taskoutput | cronlist)
109
118
  exit 0
110
119
  ;;
111
- write | strreplace | editnotebook)
120
+ write | strreplace | edit | multiedit | editnotebook | applypatch | notebookedit | delete)
112
121
  GUARDRAIL_TOOL="write"
113
122
  CONTEXT_JSON="$(safe_jq "$INPUT" '{file_path: (.tool_input.file_path // .tool_input.path // "")}')"
114
123
  ;;
115
- delete)
116
- GUARDRAIL_TOOL="write"
117
- CONTEXT_JSON="$(safe_jq "$INPUT" '{file_path: (.tool_input.file_path // .tool_input.path // "")}')"
124
+ websearch | webfetch)
125
+ GUARDRAIL_TOOL="websearch"
126
+ CONTEXT_JSON="$(safe_jq "$INPUT" '{url: (.tool_input.url // ""), query: (.tool_input.query // "")}')"
127
+ ;;
128
+ browser)
129
+ GUARDRAIL_TOOL="browser"
130
+ CONTEXT_JSON="$(safe_jq "$INPUT" '{url: (.tool_input.url // "")}')"
118
131
  ;;
119
- task)
132
+ task | agent | taskcreate | taskupdate | taskstop | skill | subagent | subagentstart | sendmessage | teamcreate | teamdelete)
120
133
  GUARDRAIL_TOOL="session.create"
121
134
  CONTEXT_JSON="$(safe_jq "$INPUT" '{description: (.tool_input.description // .tool_input.prompt // "")}')"
122
135
  ;;
123
- mcp:*)
136
+ croncreate | crondelete)
137
+ GUARDRAIL_TOOL="session.create"
138
+ CONTEXT_JSON="$(safe_jq "$INPUT" '{description: (.tool_input.description // .tool_input.schedule // "")}')"
139
+ ;;
140
+ mcp__* | mcp:* | callmcptool)
124
141
  GUARDRAIL_TOOL="mcp.tool"
125
142
  CONTEXT_JSON="$(safe_jq "$INPUT" '{tool_name: (.tool_name // ""), tool_input: (.tool_input // {})}')"
126
143
  ;;
127
144
  *)
128
- # Unknown preToolUse tool: fail-closed
129
145
  deny "🛡️ APort: unknown tool '$TOOL_NAME' — fail-closed policy"
130
146
  ;;
131
147
  esac
@@ -154,6 +170,15 @@ if [ -n "$HOOK_DECISION_FILE" ]; then
154
170
  export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
155
171
  fi
156
172
 
173
+ # Read tools: send only file_path to the evaluator (Cursor may attach large file bodies in tool_input).
174
+ if [ "$GUARDRAIL_TOOL" = "read" ]; then
175
+ CONTEXT_JSON="$(printf '%s' "$CONTEXT_JSON" | jq -c '{file_path: (.file_path // .path // "")}' 2> /dev/null || echo '{"file_path":""}')"
176
+ if [ -z "$(printf '%s' "$CONTEXT_JSON" | jq -r '.file_path // ""' 2> /dev/null)" ]; then
177
+ echo '{"permission":"allow","allowed":true}'
178
+ exit 0
179
+ fi
180
+ fi
181
+
157
182
  # Call core evaluator
158
183
  set +e
159
184
  "$GUARDRAIL" "$GUARDRAIL_TOOL" "$CONTEXT_JSON" 2> /dev/null
@@ -301,6 +301,17 @@ else
301
301
  LIMITS=$(echo "$PASSPORT" | jq ".limits.\"$POLICY_BASE\" // {}")
302
302
  fi
303
303
 
304
+ is_default_sensitive_read_path() {
305
+ local path_lower
306
+ path_lower="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
307
+ case "$path_lower" in
308
+ .env* | */.env* | .aws/* | */.aws/* | .ssh/* | */.ssh/* | *credentials* | *id_rsa* | *id_dsa* | *id_ecdsa* | *id_ed25519* | *.pem | *.key | *password* | .gnupg/* | */.gnupg/* | .kube/* | */.kube/*)
309
+ return 0
310
+ ;;
311
+ esac
312
+ return 1
313
+ }
314
+
304
315
  # Evaluate policy-specific limits
305
316
  if [[ "$POLICY_ID" == "code.repository.merge"* ]]; then
306
317
  FILES_CHANGED=$(echo "$CONTEXT_JSON" | jq -r '.files_changed // .files // 0')
@@ -426,6 +437,10 @@ fi
426
437
  if [[ "$POLICY_ID" == "data.file.read.v1" ]]; then
427
438
  FILE_PATH=$(echo "$CONTEXT_JSON" | jq -r '.file_path // .path // ""')
428
439
  if [ -n "$FILE_PATH" ]; then
440
+ if is_default_sensitive_read_path "$FILE_PATH"; then
441
+ write_decision false "$POLICY_ID" "oap.blocked_pattern" "File path matches default sensitive read pattern"
442
+ fi
443
+
429
444
  # Check allowed paths
430
445
  PATH_ALLOWED=false
431
446
  while IFS= read -r allowed_path; do
@@ -0,0 +1,61 @@
1
+ # Shared path-based read tool handling for Claude Code and Cursor hooks.
2
+ # shellcheck shell=bash
3
+ #
4
+ # Usage (after defining safe_jq in the hook):
5
+ # aport_hook_try_read_evaluation "$TOOL_NORM" "$TOOL_INPUT"
6
+ # On success: sets GUARDRAIL_TOOL=read and CONTEXT_JSON with file_path; returns 0.
7
+ # On skip (no path / not a path-based read tool): returns 1 — caller may allow without evaluator.
8
+
9
+ aport_hook_read_tools_with_path() {
10
+ case "$1" in
11
+ read | readfile | semanticsearch) return 0 ;;
12
+ *) return 1 ;;
13
+ esac
14
+ }
15
+
16
+ aport_hook_read_context_json() {
17
+ local file_path="$1"
18
+ jq -n -c --arg file_path "$file_path" '{file_path: $file_path}' 2> /dev/null
19
+ }
20
+
21
+ aport_hook_try_read_evaluation() {
22
+ local tool_norm="$1"
23
+ local tool_input="$2"
24
+ local file_path context
25
+
26
+ if ! aport_hook_read_tools_with_path "$tool_norm"; then
27
+ return 1
28
+ fi
29
+
30
+ file_path="$(echo "$tool_input" | jq -r '.file_path // .path // ""' 2> /dev/null || true)"
31
+ if [ -z "$file_path" ]; then
32
+ return 1
33
+ fi
34
+
35
+ context="$(aport_hook_read_context_json "$file_path")"
36
+ if [ -z "$context" ]; then
37
+ return 1
38
+ fi
39
+
40
+ GUARDRAIL_TOOL="read"
41
+ CONTEXT_JSON="$context"
42
+ return 0
43
+ }
44
+
45
+ aport_hook_try_read_evaluation_from_file_path() {
46
+ local file_path="$1"
47
+ local context
48
+
49
+ if [ -z "$file_path" ]; then
50
+ return 1
51
+ fi
52
+
53
+ context="$(aport_hook_read_context_json "$file_path")"
54
+ if [ -z "$context" ]; then
55
+ return 1
56
+ fi
57
+
58
+ GUARDRAIL_TOOL="read"
59
+ CONTEXT_JSON="$context"
60
+ return 0
61
+ }
@@ -5,6 +5,7 @@ _aport_tool_mapping_file() {
5
5
  lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" && pwd)"
6
6
 
7
7
  local candidates=(
8
+ "$lib_dir/../../packages/core/src/core/tool-pack-mapping.json"
8
9
  "$lib_dir/../../python/aport_guardrails/core/tool-pack-mapping.json"
9
10
  "$lib_dir/../python/aport_guardrails/core/tool-pack-mapping.json"
10
11
  "$lib_dir/../../aport/runtime/python/aport_guardrails/core/tool-pack-mapping.json"
@@ -26,7 +27,7 @@ resolve_policy_id_from_tool_name() {
26
27
  local normalized=""
27
28
  local policy_id=""
28
29
 
29
- normalized="$(printf '%s' "$tool_name" | tr '[:upper:]' '[:lower:]')"
30
+ normalized="$(printf '%s' "$tool_name" | tr '[:upper:]' '[:lower:]' | sed 's/^functions\.//' | sed 's/(.*$//')"
30
31
  mapping_file="$(_aport_tool_mapping_file)" || return 1
31
32
 
32
33
  policy_id="$(
@@ -43,6 +44,9 @@ resolve_policy_id_from_tool_name() {
43
44
  ' "$mapping_file"
44
45
  )"
45
46
 
47
+ # Do not apply JSON "default" here: bash/API guardrail must deny unmapped tool names.
48
+ # Python/TypeScript adapters use tool_to_pack_id() / toolToPackId() which apply default for custom tools.
49
+
46
50
  if [[ -n "$policy_id" && "$policy_id" != "null" ]]; then
47
51
  printf '%s\n' "$policy_id"
48
52
  return 0
@@ -0,0 +1,158 @@
1
+ # Framework tool mapping audit
2
+
3
+ Last reviewed against upstream docs and this repo’s mapping sources.
4
+
5
+ ## Mapping layers (do not conflate)
6
+
7
+ | Layer | Source | Used by |
8
+ |-------|--------|---------|
9
+ | **Host tool name** | Framework-native string (`Bash`, `exec`, `sessions_spawn`, LangChain tool `.name`) | Hooks / plugin receive this |
10
+ | **Guardrail tool id** | Hook alias passed to `aport-guardrail-bash.sh` (`bash`, `session.create`, `cron`, …) | Claude Code / Cursor hooks only |
11
+ | **Policy pack id** | `tool-pack-mapping.json` or `mapToolToPolicy()` | All evaluators |
12
+ | **Passport capability** | `external/aport-policies/*/policy.json` → `requires_capabilities` | Passport + evaluator |
13
+
14
+ Canonical pack mapping for **generic** tool names: `packages/core/src/core/tool-pack-mapping.json` (mirrored in `python/aport_guardrails/core/tool-pack-mapping.json`). TypeScript/Python adapters call `toolToPackId()` / `tool_to_pack_id()`. Bash/API guardrails use `bin/lib/tool-mapping.sh` (same JSON + `default` fallback).
15
+
16
+ ---
17
+
18
+ ## Framework summary
19
+
20
+ | Framework | Integration | Tool mapping implementation | Unmapped tools |
21
+ |-----------|-------------|----------------------------|----------------|
22
+ | **Claude Code** | `bin/aport-claude-code-hook.sh` | Host name → guardrail id → JSON | **Fail-closed** (deny) |
23
+ | **Cursor** | `bin/aport-cursor-hook.sh` | Same pattern as Claude Code | **Fail-closed** |
24
+ | **OpenClaw** | `extensions/openclaw-aport/tool-mapping.js` | `mapToolToPolicy()` in plugin | **Allow** if `allowUnmappedTools: true` (default) |
25
+ | **LangChain / LangGraph** | `APortCallback` / `APortGuardrailCallback` | `tool_to_pack_id(tool.name)` only | Uses JSON `default` → `system.command.execute.v1` |
26
+ | **CrewAI** | Python/TS hook middleware | `tool_to_pack_id(tool_name)` only | Same as LangChain |
27
+ | **DeerFlow** | `OAPGuardrailProvider` | `tool_to_pack_id(tool_name)` only | Same as LangChain |
28
+ | **n8n** | Not shipped | — | — |
29
+ | **Generic CLI** | `aport-guardrail-bash.sh` | JSON only | Deny if no rule and no default |
30
+
31
+ ---
32
+
33
+ ## Claude Code
34
+
35
+ **Official tools** ([Tools reference](https://code.claude.com/docs/en/tools-reference)) include: `Agent`, `Bash`, `Edit`, `Write`, `Read`, `Glob`, `Grep`, `WebSearch`, `WebFetch`, `Skill`, `TaskCreate`/`TaskUpdate`/`TaskStop`/`TaskGet`/`TaskList`, `CronCreate`/`CronDelete`/`CronList`, `EnterWorktree`/`ExitWorktree`, `PowerShell`, `Monitor`, MCP tools (`mcp__server__tool`), etc.
36
+
37
+ | Host tool | Guardrail id | Policy pack | Capability |
38
+ |-----------|--------------|-------------|------------|
39
+ | `Bash`, `PowerShell`, `Monitor` | `bash` | `system.command.execute.v1` | `system.command.execute` |
40
+ | `Read`, `Glob`, `Grep`, `LSP`, MCP list/read/wait tools, `TaskGet`/`TaskList`, `CronList`, … | *(allow, no evaluator)* | — | — |
41
+ | `Write`, `Edit`, `MultiEdit`, `NotebookEdit`, `TodoWrite`, … | `write` | `data.file.write.v1` | `data.file.write` |
42
+ | `WebSearch`, `WebFetch` | `websearch` | `web.fetch.v1` | `web.fetch` |
43
+ | `Browser` | `browser` | `web.browser.v1` | `web.browser` |
44
+ | `Agent`, `Task`, `TaskCreate`, `Skill`, worktree, teams, … | `session.create` | `agent.session.create.v1` | `agent.session.create` |
45
+ | `CronCreate`, `CronDelete` | `cron` | `agent.session.create.v1` | `agent.session.create` |
46
+ | `mcp__*`, `CallMcpTool` | `mcp.tool` | `mcp.tool.execute.v1` | `mcp.tool.execute` |
47
+
48
+ Programmatic map: `packages/claude-code/src/claudeCodeTools.ts`.
49
+
50
+ ---
51
+
52
+ ## Cursor
53
+
54
+ **Documented preToolUse tools** ([Cursor hooks](https://cursor.com/docs/agent/hooks)): `Shell`, `Read`, `Write`, `Grep`, `Delete`, `Task`, `WebSearch` (when emitted), `Agent`, `Edit`, MCP (`MCP:…` / `mcp:…`), plus `beforeShellExecution` / `subagentStart` events.
55
+
56
+ Hook aligned with Claude Code (v1.0.27+): `WebSearch`, `WebFetch`, `Agent`, `Edit`, `ApplyPatch`, `browser`, `cron`, MCP variants. Read-family tools still skip the evaluator for latency.
57
+
58
+ **Caveats (upstream, not APort):**
59
+
60
+ - Built-in web search may not always emit `preToolUse` depending on Cursor model/routing ([forum report](https://forum.cursor.com/t/bug-pretooluse-agent-hooks-do-not-emit-for-built-in-web-search-in-cursor-agent-works-in-claude-code/160761)).
61
+ - `ApplyPatch` may not emit hooks in some CLI builds ([Codex issue #16732](https://github.com/openai/codex/issues/16732)) — APort cannot enforce what the host does not emit.
62
+
63
+ ---
64
+
65
+ ## OpenClaw
66
+
67
+ **Official session tools** ([Session tools](https://docs.openclaw.ai/concepts/session-tool)): `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `sessions_yield`, `subagents`, `session_status`. Plus `exec`, `message`, `read`/`write`/`edit`, `git.*`, MCP `server__tool`, etc.
68
+
69
+ | OpenClaw tool | Policy pack | Capability |
70
+ |---------------|-------------|------------|
71
+ | `exec`, `exec.*`, `process`, `gateway` | `system.command.execute.v1` | `system.command.execute` |
72
+ | `message` (send-family actions) | `messaging.message.send.v1` | `messaging.message.send` |
73
+ | `read`, `view`, `glob` | `data.file.read.v1` | `data.file.read` |
74
+ | `write`, `edit`, `multiedit` | `data.file.write.v1` | `data.file.write` |
75
+ | `web_search`, `webfetch`, `browser` | `web.fetch.v1` / `web.browser.v1` | `web.fetch` / `web.browser` |
76
+ | `sessions_spawn`, `sessions_send`, `sessions_yield`, `subagents`, `session_status` | `agent.session.create.v1` | `agent.session.create` |
77
+ | `sessions_list`, `sessions_history` | `data.file.read.v1` | `data.file.read` |
78
+ | `server__tool`, `mcp.*` | `mcp.tool.execute.v1` | `mcp.tool.execute` |
79
+ | `git.*` | `code.repository.merge.v1` | `repo.merge` / `repo.pr.create` |
80
+
81
+ Plugin: `extensions/openclaw-aport/tool-mapping.js`. With `allowUnmappedTools: false`, unmapped tools are denied.
82
+
83
+ ---
84
+
85
+ ## LangChain / CrewAI / DeerFlow
86
+
87
+ No host-specific tool names: mapping is **only** `tool-pack-mapping.json` on whatever name the tool registers (e.g. `bash`, `read_file`, `web_search`, custom `my_tool`).
88
+
89
+ DeerFlow-tested names (see `python/aport_guardrails/tests/test_oap_provider_e2e.py`):
90
+
91
+ | Tool name | Policy pack |
92
+ |-----------|-------------|
93
+ | `bash` | `system.command.execute.v1` |
94
+ | `write_file`, `str_replace` | `data.file.write.v1` |
95
+ | `read_file`, `ls`, `present_file`, `view_image` | `data.file.read.v1` |
96
+ | `web_search` | `web.fetch.v1` |
97
+ | `git.create_pr` | `code.repository.merge.v1` |
98
+ | `mcp.github.issues` | `mcp.tool.execute.v1` |
99
+ | `messaging.send` | `messaging.message.send.v1` |
100
+
101
+ Custom LangChain tools must use names that match a prefix/substring in `tool-pack-mapping.json`, or they resolve to **`default`** (`system.command.execute.v1`).
102
+
103
+ ---
104
+
105
+ ## Policy packs reference
106
+
107
+ | Policy pack | Capability id | Notes |
108
+ |-------------|---------------|--------|
109
+ | `system.command.execute.v1` | `system.command.execute` | Shell/exec; JSON `default` |
110
+ | `data.file.read.v1` | `data.file.read` | |
111
+ | `data.file.write.v1` | `data.file.write` | |
112
+ | `web.fetch.v1` | `web.fetch` | WebSearch/WebFetch |
113
+ | `web.browser.v1` | `web.browser` | Browser automation (no separate `cron.v1`) |
114
+ | `agent.session.create.v1` | `agent.session.create` | Subagents, tasks, **cron** guardrail alias `cron` |
115
+ | `mcp.tool.execute.v1` | `mcp.tool.execute` | |
116
+ | `messaging.message.send.v1` | `messaging.message.send` | |
117
+ | `code.repository.merge.v1` | `repo.merge`, `repo.pr.create` | |
118
+
119
+ There is **no** `cron.v1` policy; scheduled-task tools map to `agent.session.create.v1`.
120
+
121
+ **Guardrail id note:** Hooks pass `session.create` (not a bare `cron` alias) for `CronCreate`/`CronDelete` so `cronlist` is not misclassified via a `cron*` prefix.
122
+
123
+ ## Deploy / security review (internal)
124
+
125
+ | Check | Status |
126
+ |-------|--------|
127
+ | `packages/core` and `python/.../tool-pack-mapping.json` identical | Enforced in `tests/unit/test-tool-pack-mapping.sh` |
128
+ | Bash `resolve_policy_id_from_tool_name` fail-closed on unknown | Yes — JSON `default` is **not** applied in bash (adapters still use default) |
129
+ | Hooks fail-closed on unknown host tools | Claude Code + Cursor |
130
+ | `cronlist` → read, not session | Fixed (avoid bare `cron` prefix) |
131
+ | OpenClaw `cronlist` before `cron*` match | Fixed (explicit tool names only) |
132
+ | Published npm tarball includes mapping JSON | `python/aport_guardrails/core/tool-pack-mapping.json` in root `package.json` `files` |
133
+
134
+ **Known DRY debt (acceptable for now):** host hooks (`aport-*-hook.sh`) duplicate case lists; OpenClaw uses `tool-mapping.js`. Both must stay aligned with `tool-pack-mapping.json` when adding tools. `claudeCodeTools.ts` is documentation-only.
135
+
136
+ **Prefix caveat:** bare `agent` prefix matches any tool name starting with `agent` (e.g. hypothetical `agentic_search`). Prefer explicit host names in hooks.
137
+
138
+ ---
139
+
140
+ ## Tests
141
+
142
+ | Test | Coverage |
143
+ |------|----------|
144
+ | `tests/unit/test-tool-pack-mapping.sh` | JSON resolution (Claude/OpenClaw/DeerFlow names) |
145
+ | `tests/unit/test-claude-code-hook.sh` | Claude hook allow/deny |
146
+ | `tests/unit/test-cursor-hook.sh` | Cursor hook |
147
+ | `tests/extensions/openclaw-aport.test.js` | `mapToolToPolicy()` |
148
+
149
+ ---
150
+
151
+ ## Maintenance
152
+
153
+ When a framework adds tools:
154
+
155
+ 1. Confirm the **exact** `tool_name` from host docs or a captured hook payload.
156
+ 2. Update the **hook** (Claude/Cursor) or **openclaw-aport/tool-mapping.js** if host-specific.
157
+ 3. Add prefixes to **`tool-pack-mapping.json`** (and sync Python copy).
158
+ 4. Extend unit tests and this audit doc.
package/docs/RELEASE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Release process and version policy
2
2
 
3
- **Current release:** 1.0.26 (see [CHANGELOG.md](../CHANGELOG.md)).
3
+ **Current release:** 1.0.27 (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
 
@@ -14,7 +14,10 @@ This mapping is implemented in `bin/aport-guardrail-api.sh` and `bin/aport-guard
14
14
  | `read`, `file.read`, `data.file.read` | `data.file.read.v1` | API / evaluator |
15
15
  | `write`, `file.write`, `data.file.write` | `data.file.write.v1` | API / evaluator |
16
16
  | `mcp.tool.*`, `mcp.*` | `mcp.tool.execute.v1` | API / evaluator |
17
- | `agent.session.*`, `session.create`, `session.*` | `agent.session.create.v1` | API / evaluator |
17
+ | `agent.session.*`, `session.create`, `session.*`, `cron`, `sessions_spawn`, `sessions_send`, `sessions_yield`, `subagents`, `session_status` | `agent.session.create.v1` | API / evaluator |
18
+ | `sessions_list`, `sessions_history`, `view` | `data.file.read.v1` | API / evaluator |
19
+ | `websearch`, `web_search`, `webfetch`, `web_fetch` | `web.fetch.v1` | API / evaluator |
20
+ | `browser`, `web.browser` | `web.browser.v1` | API / evaluator |
18
21
  | `agent.tool.*`, `tool.register`, `tool.*` | `agent.tool.register.v1` | API / evaluator |
19
22
  | `payment.refund`, `payment.*`, `finance.payment.refund` | `finance.payment.refund.v1` | `external/aport-policies/finance.payment.refund.v1/` |
20
23
  | `payment.charge`, `finance.payment.charge` | `finance.payment.charge.v1` | `external/aport-policies/finance.payment.charge.v1/` |
@@ -41,6 +44,8 @@ To add a new tool → policy mapping, edit the `case` block in:
41
44
 
42
45
  and add a new pattern and policy pack ID. The policy pack must exist under `external/aport-policies/<pack_id>/` (or in local-overrides / API).
43
46
 
47
+ Per-framework host tool names and hook behavior: [FRAMEWORK_TOOL_MAPPING_AUDIT.md](FRAMEWORK_TOOL_MAPPING_AUDIT.md).
48
+
44
49
  ## Reference
45
50
 
46
51
  - OAP spec: `external/aport-spec/`
@@ -71,16 +71,20 @@ This command intentionally runs the same supported installer flow (`npx @aporthq
71
71
 
72
72
  | Claude Code tool | APort policy | Default |
73
73
  |--------------------|---------------------------|----------|
74
- | Bash | system.command.execute.v1 | Enforce |
75
- | Read, Glob, LS, Grep, TodoRead | data.file.read.v1 | **Allow by default** (no evaluator call) |
76
- | Write, Edit, MultiEdit, TodoWrite | data.file.write.v1 | Enforce |
74
+ | Bash, PowerShell, Monitor | system.command.execute.v1 | Enforce |
75
+ | Read, ReadFile, SemanticSearch (with `file_path`) | data.file.read.v1 | **Enforce** (sensitive paths blocked; API/local) |
76
+ | Glob, Grep, LSP, ListMcpResourcesTool, ReadMcpResourceTool, ToolSearch, WaitForMcpServers, TaskGet, TaskList, TodoRead | data.file.read.v1 | Allow without evaluator (no single path) |
77
+ | Write, Edit, MultiEdit, NotebookEdit, TodoWrite, ShareOnboardingGuide | data.file.write.v1 | Enforce |
77
78
  | WebSearch, WebFetch | web.fetch.v1 | Enforce |
78
79
  | Browser | web.browser.v1 | Enforce |
79
- | Task | agent.session.create.v1 | Enforce |
80
+ | Agent, Task, TaskCreate, TaskUpdate, TaskStop, Skill, EnterWorktree, ExitWorktree, SendMessage, TeamCreate, TeamDelete, RemoteTrigger | agent.session.create.v1 | Enforce |
81
+ | CronCreate, CronDelete | agent.session.create.v1 | Enforce |
80
82
  | mcp__&lt;server&gt;__&lt;tool&gt; | mcp.tool.execute.v1 | Enforce |
81
83
  | **Unknown tool** | — | **Denied (fail-closed)** |
82
84
 
83
- Read-family tools (Read, Glob, LS, Grep, TodoRead) exit 0 immediately without calling the evaluator to save ~40ms per file read. The HN incident was about Bash escaping a sandbox — not reads.
85
+ Permission-rule specifiers such as `Agent(Explore)` are stripped before mapping (the hook receives `Agent(Explore)` and normalizes to `agent`).
86
+
87
+ Path-based **Read** tools call the guardrail with only `file_path` in context (not full file bodies). **Glob/Grep/LS** and similar tools still allow without an evaluator call when no single `file_path` is present.
84
88
 
85
89
  ---
86
90
 
@@ -105,14 +105,18 @@ guardrails:
105
105
 
106
106
  ## Tool-to-policy mapping
107
107
 
108
- | DeerFlow tool | OAP policy pack |
109
- |---|---|
110
- | `bash`, `write_file`, `str_replace` | `system.command.execute.v1` |
111
- | `web_search`, `web_fetch`, `image_search` | `web.fetch.v1` |
112
- | `read_file`, `ls` | `data.file.read.v1` |
113
- | `present_file`, `view_image` | `data.file.read.v1` |
114
- | `ask_clarification`, `task` | `agent.session.create.v1` |
115
- | MCP tools (dynamic) | `mcp.tool.execute.v1` |
108
+ | DeerFlow tool | OAP policy pack | Passport capability |
109
+ |---|---|---|
110
+ | `bash` | `system.command.execute.v1` | `system.command.execute` |
111
+ | `write_file`, `str_replace` | `data.file.write.v1` | `data.file.write` |
112
+ | `web_search`, `web_fetch`, `image_search` | `web.fetch.v1` | `web.fetch` |
113
+ | `read_file`, `ls`, `present_file`, `view_image` | `data.file.read.v1` | `data.file.read` |
114
+ | `git.create_pr`, `git.*` | `code.repository.merge.v1` | `repo.merge` / `repo.pr.create` |
115
+ | `messaging.send`, `message.*` | `messaging.message.send.v1` | `messaging.message.send` |
116
+ | `ask_clarification`, `task` | `agent.session.create.v1` | `agent.session.create` |
117
+ | MCP tools (`mcp.*`, dynamic names) | `mcp.tool.execute.v1` | `mcp.tool.execute` |
118
+
119
+ Mapping is implemented in `packages/core/src/core/tool-pack-mapping.json` (via `tool_to_pack_id()`). DeerFlow passes the raw tool name from each tool invocation.
116
120
 
117
121
  ## Evaluation modes
118
122
 
@@ -1,5 +1,7 @@
1
1
  # Changelog - APort OpenClaw Plugin
2
2
 
3
+ ## 1.0.27
4
+
3
5
  ## 1.0.26
4
6
 
5
7
  ## 1.0.25
@@ -152,6 +152,19 @@ function allowByList(value, list, matcher) {
152
152
  return list.some((entry) => matcher(value, entry));
153
153
  }
154
154
 
155
+ function isDefaultSensitiveReadPath(filePath) {
156
+ const value = String(filePath).toLowerCase();
157
+ return /(^|\/)\.env/.test(value) ||
158
+ /(^|\/)\.aws\//.test(value) ||
159
+ /(^|\/)\.ssh\//.test(value) ||
160
+ value.includes("credentials") ||
161
+ /(^|\/)id_(rsa|dsa|ecdsa|ed25519)/.test(value) ||
162
+ /\.(pem|key)$/.test(value) ||
163
+ value.includes("password") ||
164
+ /(^|\/)\.gnupg\//.test(value) ||
165
+ /(^|\/)\.kube\//.test(value);
166
+ }
167
+
155
168
  function makeDeny(baseParams, code, message) {
156
169
  return buildDecision({ allow: false, code, message, ...baseParams });
157
170
  }
@@ -267,6 +280,10 @@ export function evaluateLocalDecision({ policyName, context, passportFile }) {
267
280
 
268
281
  if (policyName === "data.file.read.v1") {
269
282
  const filePath = String(context.file_path ?? context.path ?? "");
283
+ if (isDefaultSensitiveReadPath(filePath)) {
284
+ return makeDeny(params, "oap.blocked_pattern", "File path matches default sensitive read pattern");
285
+ }
286
+
270
287
  if (!allowByList(filePath, limits.allowed_paths, (value, pattern) => value.startsWith(pattern) || pattern === "*")) {
271
288
  return makeDeny(params, "oap.path_not_allowed", `File path '${filePath}' is not in allowed list`);
272
289
  }
@@ -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.26",
5
+ "version": "1.0.27",
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.26",
3
+ "version": "1.0.27",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@aporthq/openclaw-aport",
9
- "version": "1.0.26",
9
+ "version": "1.0.27",
10
10
  "license": "Apache-2.0",
11
11
  "devDependencies": {
12
12
  "@types/node": "^18.0.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aporthq/openclaw-aport",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
4
4
  "description": "OpenClaw plugin for deterministic pre-action authorization via APort guardrails",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -63,7 +63,9 @@ export function mapToolToPolicy(toolName, params) {
63
63
  if (tool.match(/exec\.(run|shell)/)) return "system.command.execute.v1";
64
64
  if (tool.startsWith("exec.")) return "system.command.execute.v1";
65
65
  if (tool.startsWith("system.command.")) return "system.command.execute.v1";
66
- if (tool === "bash" || tool === "shell" || tool === "command") return "system.command.execute.v1";
66
+ if (tool === "bash" || tool === "shell" || tool === "command" || tool === "powershell" || tool === "monitor") {
67
+ return "system.command.execute.v1";
68
+ }
67
69
 
68
70
  if (tool === "message") {
69
71
  return MESSAGE_SEND_ACTIONS.has(readAction(params)) ? "messaging.message.send.v1" : null;
@@ -81,9 +83,10 @@ export function mapToolToPolicy(toolName, params) {
81
83
  return "messaging.message.send.v1";
82
84
  }
83
85
 
84
- if (tool === "read") return "data.file.read.v1";
86
+ if (tool === "read" || tool === "view") return "data.file.read.v1";
85
87
  if (tool.startsWith("file.read")) return "data.file.read.v1";
86
88
  if (tool.startsWith("data.file.read")) return "data.file.read.v1";
89
+ if (tool === "sessions_list" || tool === "sessions_history") return "data.file.read.v1";
87
90
  if (tool === "write" || tool === "edit") return "data.file.write.v1";
88
91
  if (tool === "multiedit" || tool === "notebookedit") return "data.file.write.v1";
89
92
  if (tool === "glob" || tool === "ls" || tool === "grep" || tool === "toolsearch") {
@@ -91,9 +94,35 @@ export function mapToolToPolicy(toolName, params) {
91
94
  }
92
95
  if (tool === "todoread") return "data.file.read.v1";
93
96
  if (tool === "todowrite") return "data.file.write.v1";
94
- if (tool === "taskget" || tool === "tasklist" || tool === "taskoutput") return "data.file.read.v1";
97
+ if (tool === "taskget" || tool === "tasklist" || tool === "taskoutput" || tool === "cronlist") {
98
+ return "data.file.read.v1";
99
+ }
100
+ if (
101
+ tool === "agent" ||
102
+ tool === "task" ||
103
+ tool === "taskcreate" ||
104
+ tool === "taskupdate" ||
105
+ tool === "taskstop" ||
106
+ tool === "skill" ||
107
+ tool === "enterworktree" ||
108
+ tool === "exitworktree" ||
109
+ tool === "subagent" ||
110
+ tool === "subagentstart" ||
111
+ tool === "sendmessage" ||
112
+ tool === "teamcreate" ||
113
+ tool === "teamdelete" ||
114
+ tool === "remotetrigger" ||
115
+ tool === "sessions_spawn" ||
116
+ tool === "sessions_send" ||
117
+ tool === "sessions_yield" ||
118
+ tool === "subagents" ||
119
+ tool === "session_status" ||
120
+ tool === "croncreate" ||
121
+ tool === "crondelete"
122
+ ) {
123
+ return "agent.session.create.v1";
124
+ }
95
125
  if (tool === "askuserquestion" || tool === "enterplanmode" || tool === "exitplanmode") return null;
96
- if (tool === "cronlist") return "data.file.read.v1";
97
126
  if (tool.startsWith("file.write")) return "data.file.write.v1";
98
127
  if (tool.startsWith("file.edit")) return "data.file.write.v1";
99
128
  if (tool.startsWith("data.file.write")) return "data.file.write.v1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aporthq/aport-agent-guardrails",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
4
4
  "description": "Policy enforcement guardrails for OpenClaw-compatible agent frameworks",
5
5
  "workspaces": [
6
6
  "packages/*",
@@ -1 +1,111 @@
1
- {"default":"system.command.execute.v1","rules":[{"prefixes":["git."],"pack":"code.repository.merge.v1"},{"prefixes":["exec.","system.","bash","shell","command","run_command","execute_command","gateway","process"],"pack":"system.command.execute.v1"},{"prefixes":["message.","messaging.","sms","whatsapp","slack","email"],"pack":"messaging.message.send.v1"},{"prefixes":["mcp."],"pack":"mcp.tool.execute.v1"},{"prefixes":["agent.session.","session.","sessions.","sessions_spawn","sessions_send","cron"],"pack":"agent.session.create.v1"},{"prefixes":["agent.tool."],"substrings":["tool.register"],"pack":"agent.tool.register.v1"},{"substrings":["payment.refund","finance.payment.refund"],"pack":"finance.payment.refund.v1"},{"substrings":["payment.charge","finance.payment.charge"],"pack":"finance.payment.charge.v1"},{"prefixes":["database.","data.export"],"pack":"data.export.create.v1"},{"prefixes":["write_file","str_replace","write","edit","file.write","file.edit","data.file.write"],"pack":"data.file.write.v1"},{"prefixes":["read_file","ls","present_file","view_image","read","file.read","data.file.read"],"pack":"data.file.read.v1"},{"prefixes":["web_search","web_fetch","image_search","webfetch","web.search","web.fetch"],"pack":"web.fetch.v1"},{"prefixes":["browser","web.browser"],"pack":"web.browser.v1"},{"prefixes":["ask_clarification"],"substrings":["task"],"pack":"agent.session.create.v1"}]}
1
+ {
2
+ "default": "system.command.execute.v1",
3
+ "rules": [
4
+ { "prefixes": ["git."], "pack": "code.repository.merge.v1" },
5
+ {
6
+ "prefixes": [
7
+ "exec.",
8
+ "system.",
9
+ "bash",
10
+ "shell",
11
+ "command",
12
+ "run_command",
13
+ "execute_command",
14
+ "gateway",
15
+ "process",
16
+ "powershell",
17
+ "monitor"
18
+ ],
19
+ "pack": "system.command.execute.v1"
20
+ },
21
+ {
22
+ "prefixes": ["message.", "messaging.", "sms", "whatsapp", "slack", "email"],
23
+ "pack": "messaging.message.send.v1"
24
+ },
25
+ { "prefixes": ["mcp."], "pack": "mcp.tool.execute.v1" },
26
+ {
27
+ "prefixes": [
28
+ "agent.session.",
29
+ "session.",
30
+ "sessions.",
31
+ "sessions_spawn",
32
+ "sessions_send",
33
+ "sessions_yield",
34
+ "subagents",
35
+ "session_status",
36
+ "croncreate",
37
+ "crondelete",
38
+ "taskcreate",
39
+ "taskupdate",
40
+ "taskstop",
41
+ "subagent",
42
+ "subagentstart",
43
+ "skill",
44
+ "enterworktree",
45
+ "exitworktree",
46
+ "sendmessage",
47
+ "teamcreate",
48
+ "teamdelete",
49
+ "remotetrigger",
50
+ "schedulewakeup"
51
+ ],
52
+ "pack": "agent.session.create.v1"
53
+ },
54
+ { "prefixes": ["agent.tool."], "substrings": ["tool.register"], "pack": "agent.tool.register.v1" },
55
+ { "prefixes": ["agent"], "pack": "agent.session.create.v1" },
56
+ { "substrings": ["payment.refund", "finance.payment.refund"], "pack": "finance.payment.refund.v1" },
57
+ { "substrings": ["payment.charge", "finance.payment.charge"], "pack": "finance.payment.charge.v1" },
58
+ { "prefixes": ["database.", "data.export"], "pack": "data.export.create.v1" },
59
+ {
60
+ "prefixes": [
61
+ "write_file",
62
+ "str_replace",
63
+ "write",
64
+ "edit",
65
+ "multiedit",
66
+ "notebookedit",
67
+ "file.write",
68
+ "file.edit",
69
+ "data.file.write",
70
+ "shareonboardingguide"
71
+ ],
72
+ "pack": "data.file.write.v1"
73
+ },
74
+ {
75
+ "prefixes": [
76
+ "read_file",
77
+ "ls",
78
+ "present_file",
79
+ "view_image",
80
+ "view",
81
+ "sessions_list",
82
+ "sessions_history",
83
+ "cronlist",
84
+ "read",
85
+ "glob",
86
+ "grep",
87
+ "lsp",
88
+ "file.read",
89
+ "data.file.read",
90
+ "listmcpresourcestool",
91
+ "readmcpresourcetool",
92
+ "toolsearch",
93
+ "waitformcpservers"
94
+ ],
95
+ "pack": "data.file.read.v1"
96
+ },
97
+ {
98
+ "prefixes": [
99
+ "web_search",
100
+ "web_fetch",
101
+ "websearch",
102
+ "image_search",
103
+ "webfetch",
104
+ "web.search",
105
+ "web.fetch"
106
+ ],
107
+ "pack": "web.fetch.v1"
108
+ },
109
+ { "prefixes": ["browser", "web.browser"], "pack": "web.browser.v1" }
110
+ ]
111
+ }