@aporthq/aport-agent-guardrails 1.0.19 β†’ 1.0.21

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.
Files changed (42) hide show
  1. package/README.md +16 -15
  2. package/bin/agent-guardrails +57 -17
  3. package/bin/aport-create-passport.sh +1 -1
  4. package/bin/aport-guardrail-api.sh +8 -35
  5. package/bin/aport-guardrail-bash.sh +18 -53
  6. package/bin/aport-resolve-paths.sh +2 -2
  7. package/bin/frameworks/generic.sh +59 -3
  8. package/bin/frameworks/next-steps.d/crewai-native.txt +21 -0
  9. package/bin/frameworks/next-steps.d/crewai.txt +7 -2
  10. package/bin/lib/error.sh +3 -3
  11. package/bin/lib/runtime-manifest.txt +17 -0
  12. package/bin/lib/runtime.sh +81 -0
  13. package/bin/lib/tool-mapping.sh +52 -0
  14. package/bin/lib/validation.sh +25 -15
  15. package/docs/FRAMEWORK_ROADMAP.md +1 -1
  16. package/docs/PROVIDER.md +90 -0
  17. package/docs/RELEASE.md +3 -3
  18. package/docs/development/ERROR_CODES.md +3 -3
  19. package/docs/frameworks/crewai.md +102 -68
  20. package/docs/frameworks/deerflow.md +1 -1
  21. package/docs/frameworks/openclaw.md +51 -22
  22. package/extensions/openclaw-aport/CHANGELOG.md +5 -1
  23. package/extensions/openclaw-aport/openclaw.plugin.json +1 -1
  24. package/extensions/openclaw-aport/package-lock.json +2 -2
  25. package/extensions/openclaw-aport/package.json +1 -1
  26. package/external/aport-spec/LICENSE +1 -1
  27. package/external/aport-spec/README.md +1 -0
  28. package/external/aport-spec/conformance/src/ed25519.ts +1 -1
  29. package/external/aport-spec/oap/CHANGELOG.md +1 -1
  30. package/external/aport-spec/oap/conformance.md +2 -2
  31. package/external/aport-spec/oap/oap-spec.md +2 -2
  32. package/external/aport-spec/oap/security.md +4 -4
  33. package/external/aport-spec/oap/vc/examples/oap-decision-vc.json +1 -1
  34. package/external/aport-spec/oap/vc/examples/oap-passport-vc.json +1 -1
  35. package/external/aport-spec/oap/vc/tools/examples/vc-to-decision.js +1 -1
  36. package/external/aport-spec/oap/vc/tools/examples/vc-to-passport.js +1 -1
  37. package/external/aport-spec/oap/vc/tools/src/index.ts +2 -2
  38. package/external/aport-spec/oap/vc/tools/test-simple.js +2 -2
  39. package/external/aport-spec/oap/vc/vc-mapping.md +4 -4
  40. package/external/aport-spec/oap/well-known-schema.json +85 -0
  41. package/external/aport-spec/well-known.md +203 -0
  42. package/package.json +3 -2
package/README.md CHANGED
@@ -89,23 +89,24 @@ The security concern is that agent tools and skills can execute sensitive action
89
89
 
90
90
  ## πŸ”Œ Supported frameworks
91
91
 
92
- **APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the framework adapter/provider package shown in the framework doc.
92
+ **APort Agent Guardrail** adapters and providers are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the Python package shown in the framework doc.
93
93
 
94
- **Two ways to use APort:** (1) **Guardrails (CLI/setup)** β€” run the installer to create your passport and config; (2) **Core (library)** β€” use the evaluator or framework callback in your app so each tool call is verified. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
94
+ **Two ways to use APort:** (1) **Guardrails (CLI/setup)** β€” run the installer to create your passport and config; (2) **Core (library)** β€” use the `OAPGuardrailProvider` ([docs/PROVIDER.md](docs/PROVIDER.md)) in your app so each tool call is verified. One provider per language (Python + TypeScript), works with any framework. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
95
95
 
96
96
  **CLI-supported frameworks:** `openclaw`, `langchain`, `crewai`, `cursor`, `claude-code`, `deerflow`, `n8n`. OpenClaw/Cursor/Claude Code include runtime-specific integration scripts; DeerFlow/LangChain/CrewAI use framework docs plus generic setup output from the CLI. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
97
97
 
98
98
  | Framework | Doc | Integration | Install |
99
99
  |-----------|-----|--------------|--------|
100
- | **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | `before_tool_call` plugin | `npx @aporthq/aport-agent-guardrails openclaw` |
100
+ | **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | Native `GuardrailProvider` or plugin | `npm i @aporthq/aport-agent-guardrails-core && npx @aporthq/aport-agent-guardrails openclaw` |
101
101
  | **Cursor** | [docs/frameworks/cursor.md](docs/frameworks/cursor.md) | `beforeShellExecution` / `preToolUse` hooks β†’ writes `~/.cursor/hooks.json`. **Runtime enforcement is the bash hook;** the Node package `@aporthq/aport-agent-guardrails-cursor` is a helper only (Evaluator, `getHookPath()`). | `npx @aporthq/aport-agent-guardrails cursor` |
102
102
  | **Claude Code** | [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md) | PreToolUse hook β†’ writes `~/.claude/settings.json` (Claude Code format; not Cursor). | `npx @aporthq/aport-agent-guardrails claude-code` |
103
103
  | **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
104
- | **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** `@before_tool_call` hook, `register_aport_guardrail` | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
104
+ | **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** released hook adapter by default; native `GuardrailProvider` mode for CrewAI builds with native provider support | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
105
105
  | **DeerFlow** | [docs/frameworks/deerflow.md](docs/frameworks/deerflow.md) | **Python:** generic OAP provider wiring in DeerFlow config | `npx @aporthq/aport-agent-guardrails deerflow` then follow printed `uv`/config steps |
106
106
  | **n8n** | [docs/frameworks/n8n.md](docs/frameworks/n8n.md) | *Coming soon* β€” custom node and runtime in progress | β€” |
107
+ | **VoltAgent** | [VoltAgent PR #1171](https://github.com/VoltAgent/voltagent/pull/1171) | *In progress* β€” pluggable `GuardrailProvider` interface landing upstream in `@voltagent/core` | β€” |
107
108
 
108
- Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain and CrewAI, the Node CLI only runs the wizard and writes config; you must then run the printed `pip install` and setup commands to install the runtime adapter.** **Python** adapters on PyPI; **Node** adapters on npm (same version as the CLI).
109
+ Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain, CrewAI, and DeerFlow, the CLI writes config and installs the local runtime into the framework config directory; then install the Python package and wire the provider/callback shown in the framework doc.** **Python** packages are on PyPI; **Node** packages are on npm (same version as the CLI).
109
110
 
110
111
  **Passport path:** Each framework has its own **default** passport path (where that framework stores data): e.g. Cursor β†’ `~/.cursor/aport/passport.json`, OpenClaw β†’ `~/.openclaw/aport/passport.json`, LangChain β†’ `~/.aport/langchain/aport/passport.json`. The passport wizard’s **first question** is β€œPassport file path [default]:” β€” press Enter for the framework default or type a different path. In non-interactive mode (e.g. CI) use **`--output /path/to/passport.json`** to choose the path. Roadmap: [docs/FRAMEWORK_ROADMAP.md](docs/FRAMEWORK_ROADMAP.md).
111
112
 
@@ -125,15 +126,15 @@ npx @aporthq/aport-agent-guardrails
125
126
  # or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
126
127
  ```
127
128
 
128
- **Python (LangChain or CrewAI only):** Run the wizard via the Node command above, then install the Python adapter and run the framework setup (see the printed next steps). Or use the Python CLI to see the exact commands:
129
+ **Python (LangChain, CrewAI, or DeerFlow):** Run the wizard via the Node command above, then install the Python package and follow the printed next steps. Or use the Python CLI to see the exact commands:
129
130
  ```bash
130
131
  pip install aport-agent-guardrails
131
- aport setup --framework=langchain # prints: npx ... langchain, then pip install aport-agent-guardrails-langchain, aport-langchain setup
132
- # or --framework=crewai for CrewAI
132
+ aport setup --framework=langchain
133
+ # or --framework=crewai / deerflow
133
134
  ```
134
- Then run the printed `npx` command to create passport and config, and the printed `pip` / `aport-<framework> setup` to install the adapter.
135
+ Then run the printed `npx` command to create passport and config, and the printed Python integration step for your framework.
135
136
 
136
- This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for LangChain/CrewAI: `pip install aport-agent-guardrails-langchain` + `aport-langchain setup`).
137
+ This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for CrewAI: by default install `aport-agent-guardrails-crewai` for released CrewAI, or opt into native-provider mode if your CrewAI build supports it).
137
138
 
138
139
  **2. Hosted passport (optional)** β€” If you already have an agent_id from [aport.io](https://aport.io), use it to skip the wizard: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
139
140
 
@@ -147,7 +148,7 @@ aport-guardrail system.command.execute '{"command":"rm -rf /"}' # DENY (blocked
147
148
  ```
148
149
  *(If you use `npx` without `-g`, run `npx aport-guardrail ...`.)*
149
150
 
150
- **Python:** Use the guardrail in your app (e.g. add `APortCallback()` to your LangChain agent, or `register_aport_guardrail()` for CrewAI). The guardrail runs on every tool call. To test allow/deny from the shell without Node, use `npx aport-guardrail ...` as above, or see your framework doc for in-app testing.
151
+ **Python:** Use the guardrail in your app (e.g. add `APortCallback()` to your LangChain agent, use `register_aport_guardrail()` for released CrewAI, or `enable_guardrail(OAPGuardrailProvider(...))` for CrewAI builds with native provider support). The guardrail runs on every tool call. To test allow/deny from the shell without Node, use `npx aport-guardrail ...` as above, or see your framework doc for in-app testing.
151
152
 
152
153
  **Check passport status and audit:**
153
154
 
@@ -397,14 +398,14 @@ See [Verification methods](docs/VERIFICATION_METHODS.md) for a detailed comparis
397
398
  | `aport` | OpenClaw one-command setup (passport + plugin + wrappers). Optional: `aport <agent_id>` for hosted passport. |
398
399
  | `aport-guardrail` | Run guardrail check from the CLI (e.g. `aport-guardrail system.command.execute '{"command":"ls"}'`). Uses passport from your framework config dir. |
399
400
 
400
- **Python:** After `pip install aport-agent-guardrails` you get `aport` (setup helper). For LangChain or CrewAI, install the adapter and framework setup:
401
+ **Python:** After `pip install aport-agent-guardrails` you get `aport` (setup helper). For LangChain or CrewAI, install the framework package and setup:
401
402
 
402
403
  | Command | Purpose |
403
404
  |--------|---------|
404
405
  | `aport setup --framework=langchain` | Print next-step commands (npx wizard, then `pip install aport-agent-guardrails-langchain`, `aport-langchain setup`). |
405
- | `aport setup --framework=crewai` | Same for CrewAI: npx wizard, then `pip install aport-agent-guardrails-crewai`, `aport-crewai setup`. |
406
+ | `aport setup --framework=crewai` | Default released CrewAI path: bootstrap config/runtime, then use `pip install aport-agent-guardrails-crewai` and `aport-crewai setup`. |
407
+ | `aport setup --framework=crewai --integration-mode=native` | Native CrewAI path: bootstrap config/runtime, then use `uv add aport-agent-guardrails` and `OAPGuardrailProvider`. |
406
408
  | `aport-langchain setup` | LangChain config and wizard (after installing `aport-agent-guardrails-langchain`). |
407
- | `aport-crewai setup` | CrewAI config and wizard (after installing `aport-agent-guardrails-crewai`). |
408
409
 
409
410
  Use the framework-specific doc for where config and passport live and for any extra steps (e.g. Cursor: restart IDE; LangChain/CrewAI: add callback/hook in code).
410
411
 
@@ -429,7 +430,7 @@ Use the framework-specific doc for where config and passport live and for any ex
429
430
  | β†’ [Cursor](docs/frameworks/cursor.md) | beforeShellExecution / preToolUse hooks, `~/.cursor/hooks.json` |
430
431
  | β†’ [Claude Code](docs/frameworks/claude-code.md) | PreToolUse hook, `~/.claude/settings.json` |
431
432
  | β†’ [LangChain / LangGraph](docs/frameworks/langchain.md) | `APortCallback` handler |
432
- | β†’ [CrewAI](docs/frameworks/crewai.md) | `@before_tool_call` hook, `register_aport_guardrail` |
433
+ | β†’ [CrewAI](docs/frameworks/crewai.md) | Released hook adapter by default; native provider mode when available |
433
434
  | β†’ [DeerFlow](docs/frameworks/deerflow.md) | Generic provider wiring via DeerFlow `config.yaml` |
434
435
  | β†’ [n8n](docs/frameworks/n8n.md) | Custom node, branch on allow/deny |
435
436
  | [Framework roadmap](docs/FRAMEWORK_ROADMAP.md) | Support status and roadmap |
@@ -28,9 +28,22 @@ SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_PATH")" && pwd)"
28
28
  ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
29
29
  FRAMEWORKS_DIR="$SCRIPT_DIR/frameworks"
30
30
  LIB_DIR="$SCRIPT_DIR/lib"
31
+ SUPPORTED_FRAMEWORKS=(openclaw langchain crewai cursor claude-code deerflow n8n)
32
+
33
+ framework_supported() {
34
+ local candidate="${1:-}"
35
+ local supported
36
+ for supported in "${SUPPORTED_FRAMEWORKS[@]}"; do
37
+ if [[ "$candidate" == "$supported" ]]; then
38
+ return 0
39
+ fi
40
+ done
41
+ return 1
42
+ }
31
43
 
32
44
  # Parse --framework= and -f (skip detection when set)
33
45
  framework=""
46
+ integration_mode=""
34
47
  REST=()
35
48
  while [[ $# -gt 0 ]]; do
36
49
  case "$1" in
@@ -47,6 +60,19 @@ while [[ $# -gt 0 ]]; do
47
60
  exit 1
48
61
  fi
49
62
  ;;
63
+ --integration-mode=*)
64
+ integration_mode="${1#--integration-mode=}"
65
+ shift
66
+ ;;
67
+ --integration-mode)
68
+ if [[ $# -gt 1 ]]; then
69
+ integration_mode="$2"
70
+ shift 2
71
+ else
72
+ echo "[aport] ERROR: --integration-mode requires a value (compat or native)" >&2
73
+ exit 1
74
+ fi
75
+ ;;
50
76
  *)
51
77
  REST+=("$1")
52
78
  shift
@@ -59,16 +85,11 @@ if [[ -z "$framework" ]] && [[ ${#REST[@]} -gt 0 ]]; then
59
85
  first_arg="${REST[0]}"
60
86
  # Check if first arg looks like a framework name (lowercase alphanumeric + hyphen)
61
87
  if [[ "$first_arg" =~ ^[a-z0-9-]+$ ]]; then
62
- # Valid framework names
63
- valid_frameworks=(openclaw langchain crewai cursor claude-code n8n deerflow)
64
- for valid_fw in "${valid_frameworks[@]}"; do
65
- if [[ "$first_arg" == "$valid_fw" ]]; then
66
- framework="$first_arg"
67
- # Remove framework from REST so remaining args are pass-through
68
- REST=("${REST[@]:1}")
69
- break
70
- fi
71
- done
88
+ if framework_supported "$first_arg"; then
89
+ framework="$first_arg"
90
+ # Remove framework from REST so remaining args are pass-through
91
+ REST=("${REST[@]:1}")
92
+ fi
72
93
  fi
73
94
  fi
74
95
 
@@ -98,7 +119,7 @@ if [[ -z "$framework" ]]; then
98
119
  echo " ─────────────────────────────────────────"
99
120
  echo " Multiple frameworks detected: $detected_list"
100
121
  echo " Choose one: openclaw | langchain | crewai | cursor | claude-code | deerflow | n8n"
101
- echo " Example: npx @aporthq/agent-guardrails --framework=openclaw"
122
+ echo " Example: npx @aporthq/aport-agent-guardrails --framework=openclaw"
102
123
  echo ""
103
124
  read -p " Framework [${framework:-openclaw}]: " choice
104
125
  framework="${choice:-${framework:-openclaw}}"
@@ -121,8 +142,8 @@ if [[ -z "$framework" ]]; then
121
142
  echo " ─────────────────────────────────────────"
122
143
  echo " No framework detected in current directory."
123
144
  echo " Choose: openclaw | langchain | crewai | cursor | claude-code | deerflow | n8n"
124
- echo " Example: npx @aporthq/agent-guardrails openclaw"
125
- echo " npx @aporthq/agent-guardrails --framework=langchain"
145
+ echo " Example: npx @aporthq/aport-agent-guardrails openclaw"
146
+ echo " npx @aporthq/aport-agent-guardrails --framework=langchain"
126
147
  echo ""
127
148
  read -p " Framework [openclaw]: " framework
128
149
  framework="${framework:-openclaw}"
@@ -136,6 +157,29 @@ if [[ ! "$framework" =~ ^[a-z0-9-]+$ ]]; then
136
157
  exit 1
137
158
  fi
138
159
 
160
+ if ! framework_supported "$framework"; then
161
+ echo "[aport] ERROR: Unknown or unsupported framework: $framework" >&2
162
+ echo " Supported: ${SUPPORTED_FRAMEWORKS[*]}" >&2
163
+ exit 1
164
+ fi
165
+
166
+ echo "[aport] Selected framework: $framework" >&2
167
+
168
+ if [[ -n "$integration_mode" ]]; then
169
+ case "$integration_mode" in
170
+ compat|native) ;;
171
+ *)
172
+ echo "[aport] ERROR: Unsupported integration mode: $integration_mode (expected compat or native)" >&2
173
+ exit 1
174
+ ;;
175
+ esac
176
+ if [[ "$framework" != "crewai" ]]; then
177
+ echo "[aport] ERROR: --integration-mode is only supported for CrewAI" >&2
178
+ exit 1
179
+ fi
180
+ REST+=("--integration-mode=$integration_mode")
181
+ fi
182
+
139
183
  script="$FRAMEWORKS_DIR/${framework}.sh"
140
184
 
141
185
  # n8n: custom node not yet available; warn before running wizard-only script
@@ -160,7 +204,3 @@ if [[ -x "$generic_script" ]]; then
160
204
  export APORT_FRAMEWORK="$framework"
161
205
  exec "$generic_script" ${REST+"${REST[@]}"}
162
206
  fi
163
-
164
- echo "[aport] ERROR: Unknown or unsupported framework: $framework" >&2
165
- echo " Supported: openclaw, langchain, crewai, cursor, claude-code, deerflow, n8n" >&2
166
- exit 1
@@ -15,7 +15,7 @@
15
15
  set -e
16
16
 
17
17
  PASSPORT_FILE=""
18
- NON_INTERACTIVE=""
18
+ NON_INTERACTIVE="${APORT_NONINTERACTIVE:-${CI:-}}"
19
19
  # Parse --output, --non-interactive, --framework=
20
20
  while [ $# -gt 0 ]; do
21
21
  case "$1" in
@@ -14,6 +14,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
14
14
  # Resolve paths: config_dir/aport/ (new) or config_dir (legacy); same as bash guardrail
15
15
  # shellcheck source=bin/aport-resolve-paths.sh
16
16
  . "${SCRIPT_DIR}/bin/aport-resolve-paths.sh"
17
+ # shellcheck source=bin/lib/tool-mapping.sh
18
+ . "${SCRIPT_DIR}/bin/lib/tool-mapping.sh"
17
19
 
18
20
  NODE_EVALUATOR="$SCRIPT_DIR/src/evaluator.js"
19
21
 
@@ -48,41 +50,12 @@ if [ -z "$APORT_AGENT_ID" ] && [ ! -f "$PASSPORT_FILE" ]; then
48
50
  exit 1
49
51
  fi
50
52
 
51
- # Map tool to policy pack ID
52
- POLICY_ID=""
53
- case "$TOOL_NAME" in
54
- git.create_pr | git.merge | git.push | git.*)
55
- POLICY_ID="code.repository.merge.v1"
56
- ;;
57
- exec.run | exec.* | system.command.* | system.*)
58
- POLICY_ID="system.command.execute.v1"
59
- ;;
60
- message.send | message.* | messaging.*)
61
- POLICY_ID="messaging.message.send.v1"
62
- ;;
63
- mcp.tool.* | mcp.*)
64
- POLICY_ID="mcp.tool.execute.v1"
65
- ;;
66
- agent.session.* | session.create | session.*)
67
- POLICY_ID="agent.session.create.v1"
68
- ;;
69
- agent.tool.* | tool.register | tool.*)
70
- POLICY_ID="agent.tool.register.v1"
71
- ;;
72
- payment.refund | payment.* | finance.payment.refund)
73
- POLICY_ID="finance.payment.refund.v1"
74
- ;;
75
- payment.charge | finance.payment.charge)
76
- POLICY_ID="finance.payment.charge.v1"
77
- ;;
78
- database.write | database.* | data.export)
79
- POLICY_ID="data.export.create.v1"
80
- ;;
81
- *)
82
- echo "Error: Tool '$TOOL_NAME' is not mapped to a policy pack" >&2
83
- exit 1
84
- ;;
85
- esac
53
+ # Map tool to policy pack ID from the shared JSON source of truth.
54
+ POLICY_ID="$(resolve_policy_id_from_tool_name "$TOOL_NAME" || true)"
55
+ if [[ -z "$POLICY_ID" ]]; then
56
+ echo "Error: Tool '$TOOL_NAME' is not mapped to a policy pack" >&2
57
+ exit 1
58
+ fi
86
59
 
87
60
  # Call Node.js evaluator with API
88
61
  if [ -n "$DEBUG_APORT" ]; then
@@ -13,6 +13,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
13
13
  # Source validation library for input sanitization
14
14
  # shellcheck source=bin/lib/validation.sh
15
15
  . "${SCRIPT_DIR}/bin/lib/validation.sh"
16
+ # shellcheck source=bin/lib/tool-mapping.sh
17
+ . "${SCRIPT_DIR}/bin/lib/tool-mapping.sh"
16
18
 
17
19
  # Get script directory to find submodules (external/ per GIT_SUBMODULES_EXPLAINED.md)
18
20
  POLICIES_DIR="$SCRIPT_DIR/external/aport-policies"
@@ -94,11 +96,14 @@ write_decision() {
94
96
  local deny_message="${4:-Policy evaluation failed}"
95
97
 
96
98
  local decision_id=$(uuidgen 2> /dev/null || echo "local-$(date +%s)")
97
- local passport_id=$(jq -r '.passport_id // "unknown"' "$PASSPORT_FILE")
99
+ local passport_id=$(jq -r '.passport_id // .agent_id // "unknown"' "$PASSPORT_FILE")
100
+ local agent_id=$(jq -r '.agent_id // .passport_id // "unknown"' "$PASSPORT_FILE")
98
101
  local owner_id=$(jq -r '.owner_id // "unknown"' "$PASSPORT_FILE")
99
102
  local assurance_level=$(jq -r '.assurance_level // "L0"' "$PASSPORT_FILE")
100
103
  local passport_digest=$(compute_passport_digest "$PASSPORT_FILE")
101
104
  local issued_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
105
+ local created_at="$issued_at"
106
+ local expires_in=3600
102
107
  local expires_at=$(date -u -v+1H +%Y-%m-%dT%H:%M:%SZ 2> /dev/null || date -u -d '+1 hour' +%Y-%m-%dT%H:%M:%SZ)
103
108
 
104
109
  # Build reasons array per OAP v1.0 spec
@@ -126,12 +131,15 @@ write_decision() {
126
131
  --arg decision_id "$decision_id" \
127
132
  --arg policy_id "$policy_id" \
128
133
  --arg passport_id "$passport_id" \
134
+ --arg agent_id "$agent_id" \
129
135
  --arg owner_id "$owner_id" \
130
136
  --arg assurance_level "$assurance_level" \
131
137
  --argjson allow "$allow" \
132
138
  --argjson reasons "$reasons" \
133
139
  --arg issued_at "$issued_at" \
140
+ --arg created_at "$created_at" \
134
141
  --arg expires_at "$expires_at" \
142
+ --argjson expires_in "$expires_in" \
135
143
  --arg passport_digest "$passport_digest" \
136
144
  --arg prev_decision_id "$prev_decision_id" \
137
145
  --arg prev_content_hash "$prev_content_hash" \
@@ -139,12 +147,15 @@ write_decision() {
139
147
  decision_id: $decision_id,
140
148
  policy_id: $policy_id,
141
149
  passport_id: $passport_id,
150
+ agent_id: $agent_id,
142
151
  owner_id: $owner_id,
143
152
  assurance_level: $assurance_level,
144
153
  allow: $allow,
145
154
  reasons: $reasons,
146
155
  issued_at: $issued_at,
156
+ created_at: $created_at,
147
157
  expires_at: $expires_at,
158
+ expires_in: $expires_in,
148
159
  passport_digest: $passport_digest,
149
160
  signature: "local-unsigned",
150
161
  kid: "oap:local:dev-key",
@@ -216,58 +227,12 @@ if [ "$SPEC_VERSION" != "oap/1.0" ]; then
216
227
  write_decision false "unknown" "oap.passport_version_mismatch" "Passport spec version is '$SPEC_VERSION', expected 'oap/1.0'"
217
228
  fi
218
229
 
219
- # Map tool to policy pack ID
220
- POLICY_ID=""
221
- case "$TOOL_NAME" in
222
- git.create_pr | git.merge | git.push | git.*)
223
- POLICY_ID="code.repository.merge.v1"
224
- ;;
225
- exec | exec.run | exec.* | system.* | bash | shell | command)
226
- POLICY_ID="system.command.execute.v1"
227
- ;;
228
- gateway | gateway.* | process | process.*)
229
- # High risk operations - treat as command execution
230
- POLICY_ID="system.command.execute.v1"
231
- ;;
232
- message.send | message.* | messaging.* | sms | whatsapp | slack | email)
233
- POLICY_ID="messaging.message.send.v1"
234
- ;;
235
- read | file.read | file.read.* | data.file.read | data.file.read.*)
236
- POLICY_ID="data.file.read.v1"
237
- ;;
238
- write | edit | file.write | file.write.* | file.edit | file.edit.* | data.file.write | data.file.write.*)
239
- POLICY_ID="data.file.write.v1"
240
- ;;
241
- web_fetch | webfetch | web.fetch | web.fetch.* | web_search | websearch | web.search | web.search.*)
242
- POLICY_ID="web.fetch.v1"
243
- ;;
244
- browser | browser.* | web.browser | web.browser.*)
245
- POLICY_ID="web.browser.v1"
246
- ;;
247
- mcp.*)
248
- POLICY_ID="mcp.tool.execute.v1"
249
- ;;
250
- agent.session.* | session.create | session.* | sessions.* | sessions_spawn | sessions_send)
251
- POLICY_ID="agent.session.create.v1"
252
- ;;
253
- cron | cron.*)
254
- # Scheduled tasks - treat as session management
255
- POLICY_ID="agent.session.create.v1"
256
- ;;
257
- agent.tool.* | tool.register)
258
- POLICY_ID="agent.tool.register.v1"
259
- ;;
260
- payment.refund | refund | payment.charge | charge | payment.* | finance.*)
261
- POLICY_ID="finance.payment.refund.v1"
262
- ;;
263
- database.write | database.insert | database.update | database.delete | data.export | data.export.*)
264
- POLICY_ID="data.export.create.v1"
265
- ;;
266
- *)
267
- # Unknown tool - deny by default for security
268
- write_decision false "unknown" "oap.unknown_capability" "Tool '$TOOL_NAME' is not mapped to a policy pack"
269
- ;;
270
- esac
230
+ # Map tool to policy pack ID from the shared JSON source of truth.
231
+ POLICY_ID="$(resolve_policy_id_from_tool_name "$TOOL_NAME" || true)"
232
+ if [[ -z "$POLICY_ID" ]]; then
233
+ # Unknown tool - deny by default for security.
234
+ write_decision false "unknown" "oap.unknown_capability" "Tool '$TOOL_NAME' is not mapped to a policy pack"
235
+ fi
271
236
 
272
237
  # Capability-specific context summary for audit log (command, recipient, repo/branch, file_path, etc.)
273
238
  CONTEXT_SUMMARY=""
@@ -56,8 +56,8 @@ resolve_aport_paths() {
56
56
  # 1) Explicit path set and file exists β†’ use it (plugin or wrapper)
57
57
  if [ -n "${OPENCLAW_PASSPORT_FILE:-}" ] && [ -f "$OPENCLAW_PASSPORT_FILE" ]; then
58
58
  # Validate env-provided path if validator is available
59
- if type validate_passport_path &> /dev/null; then
60
- if ! validate_passport_path "$OPENCLAW_PASSPORT_FILE"; then
59
+ if type validate_explicit_passport_path &> /dev/null; then
60
+ if ! validate_explicit_passport_path "$OPENCLAW_PASSPORT_FILE"; then
61
61
  echo "[aport] WARN: OPENCLAW_PASSPORT_FILE path failed validation: $OPENCLAW_PASSPORT_FILE" >&2
62
62
  fi
63
63
  fi
@@ -13,23 +13,79 @@ FRAMEWORKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" && pwd)"
13
13
  source "$LIB/common.sh"
14
14
  # shellcheck source=../lib/passport.sh
15
15
  source "$LIB/passport.sh"
16
+ # shellcheck source=../lib/runtime.sh
17
+ source "$LIB/runtime.sh"
16
18
  # shellcheck source=../lib/config.sh
17
19
  source "$LIB/config.sh"
18
20
 
19
21
  framework="${APORT_FRAMEWORK:?APORT_FRAMEWORK must be set by the dispatcher}"
22
+ crewai_integration_mode="${APORT_CREWAI_INTEGRATION_MODE:-compat}"
23
+
24
+ FORWARD_ARGS=()
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ --integration-mode=*)
28
+ crewai_integration_mode="${1#--integration-mode=}"
29
+ ;;
30
+ --integration-mode)
31
+ if [[ $# -lt 2 ]]; then
32
+ log_error "--integration-mode requires compat or native"
33
+ exit 1
34
+ fi
35
+ crewai_integration_mode="$2"
36
+ shift
37
+ ;;
38
+ *)
39
+ FORWARD_ARGS+=("$1")
40
+ ;;
41
+ esac
42
+ shift
43
+ done
44
+
45
+ case "$crewai_integration_mode" in
46
+ compat | native) ;;
47
+ *)
48
+ log_error "Unsupported CrewAI integration mode: $crewai_integration_mode"
49
+ exit 1
50
+ ;;
51
+ esac
52
+
53
+ if [[ "$framework" != "crewai" && "$crewai_integration_mode" != "compat" ]]; then
54
+ log_error "--integration-mode is only supported for CrewAI"
55
+ exit 1
56
+ fi
57
+
58
+ resolve_next_steps_file() {
59
+ if [[ "$framework" == "crewai" ]]; then
60
+ if [[ "$crewai_integration_mode" == "native" ]]; then
61
+ echo "$FRAMEWORKS_DIR/next-steps.d/crewai-native.txt"
62
+ return
63
+ fi
64
+ fi
65
+ echo "$FRAMEWORKS_DIR/next-steps.d/$framework.txt"
66
+ }
20
67
 
21
68
  run_setup() {
22
69
  log_info "Setting up APort guardrails for $framework..."
23
70
  config_dir="$(write_config_template "$framework")"
24
71
  mkdir -p "$config_dir/aport"
25
72
  chmod 700 "$config_dir/aport"
73
+ install_runtime_tree "$config_dir"
26
74
  export APORT_FRAMEWORK="$framework"
27
- run_passport_wizard "$@"
75
+ if [[ "$framework" == "crewai" ]]; then
76
+ log_info "CrewAI integration mode: $crewai_integration_mode"
77
+ fi
78
+ if ((${#FORWARD_ARGS[@]} > 0)); then
79
+ run_passport_wizard "${FORWARD_ARGS[@]}"
80
+ else
81
+ run_passport_wizard
82
+ fi
28
83
  # Harden permissions on passport (contains policy/capabilities)
29
84
  [ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
85
+ log_info "Local runtime installed at: $config_dir/aport/runtime"
30
86
 
31
87
  # Print framework-specific next steps from data file
32
- next_steps="$FRAMEWORKS_DIR/next-steps.d/$framework.txt"
88
+ next_steps="$(resolve_next_steps_file)"
33
89
  if [ -f "$next_steps" ]; then
34
90
  echo ""
35
91
  # Substitute $config_dir in the template
@@ -57,4 +113,4 @@ run_setup() {
57
113
  fi
58
114
  }
59
115
 
60
- run_setup "$@"
116
+ run_setup
@@ -0,0 +1,21 @@
1
+ Next steps (CrewAI native provider mode):
2
+ ───────────────────────────────────────
3
+ Requires a CrewAI build with native guardrail provider support.
4
+
5
+ 1. Install the Python runtime package:
6
+ uv add aport-agent-guardrails
7
+ 2. Config written to: $config_dir
8
+ 3. Enable the native provider before running your crew:
9
+
10
+ from crewai.hooks import enable_guardrail
11
+ from aport_guardrails.providers import OAPGuardrailProvider
12
+ enable_guardrail(
13
+ OAPGuardrailProvider(framework="crewai", config_path="$config_dir/config.yaml"),
14
+ fail_closed=True,
15
+ )
16
+ crew.kickoff()
17
+
18
+ Released CrewAI compatibility mode:
19
+ rerun without --integration-mode=native
20
+
21
+ See: docs/frameworks/crewai.md
@@ -1,6 +1,8 @@
1
1
  Next steps (CrewAI):
2
2
  ───────────────────
3
- 1. Install the Python adapter (required for runtime enforcement):
3
+ Default mode targets released CrewAI without native guardrail provider support.
4
+
5
+ 1. Install the CrewAI adapter package:
4
6
  pip install aport-agent-guardrails-crewai
5
7
  aport-crewai setup
6
8
  2. Config written to: $config_dir
@@ -10,4 +12,7 @@
10
12
  register_aport_guardrail()
11
13
  crew.kickoff()
12
14
 
13
- Or: @with_aport_guardrail on your entry point. See: docs/frameworks/crewai.md
15
+ Optional native mode:
16
+ rerun with --integration-mode=native
17
+
18
+ See: docs/frameworks/crewai.md
package/bin/lib/error.sh CHANGED
@@ -182,13 +182,13 @@ get_resolution() {
182
182
  echo "Reduce context data size by removing unnecessary fields or summarizing large data. Default limit: 100KB."
183
183
  ;;
184
184
  "$ERROR_PATH_NOT_ALLOWED")
185
- echo "Use standard APort directories: ~/.openclaw/, ~/.aport/, or /tmp/aport-*. Contact administrator to add custom allowed directories."
185
+ echo "Use standard framework config directories such as ~/.openclaw/, ~/.aport/, ~/.cursor/, ~/.claude/, ~/.n8n/, or /tmp/aport-*. Contact administrator to add custom allowed directories."
186
186
  ;;
187
187
  "$ERROR_PATH_TRAVERSAL")
188
188
  echo "Use absolute paths without parent directory references (../ or /..). This is a security feature to prevent path traversal attacks."
189
189
  ;;
190
190
  "$ERROR_PASSPORT_NOT_FOUND")
191
- echo "Create a passport by running: npx @aporthq/aport-agent-guardrails openclaw. See: https://github.com/aporthq/agent-guardrails#passport-setup"
191
+ echo "Create a passport by running: npx @aporthq/aport-agent-guardrails <framework>. See: https://github.com/aporthq/aport-agent-guardrails/tree/main/docs/frameworks"
192
192
  ;;
193
193
  "$ERROR_PASSPORT_MISSING_CAP")
194
194
  echo "Request capability be added to passport or generate new passport with required capabilities."
@@ -206,7 +206,7 @@ get_resolution() {
206
206
  echo "Wait for rate limit to reset, reduce request frequency, or use local evaluation mode instead of API mode."
207
207
  ;;
208
208
  "$ERROR_MISCONFIGURED")
209
- echo "Run setup: npx @aporthq/aport-agent-guardrails <framework>. Check passport exists at ~/.openclaw/passport.json"
209
+ echo "Run setup: npx @aporthq/aport-agent-guardrails <framework>. Check your framework config directory for aport/passport.json and aport/runtime/bin/aport-guardrail.sh."
210
210
  ;;
211
211
  *)
212
212
  echo ""
@@ -0,0 +1,17 @@
1
+ file bin/aport-create-passport.sh
2
+ file bin/aport-guardrail.sh
3
+ file bin/aport-guardrail-bash.sh
4
+ file bin/aport-guardrail-api.sh
5
+ file bin/aport-guardrail-v2.sh
6
+ file bin/aport-resolve-paths.sh
7
+ file bin/aport-status.sh
8
+ file bin/lib/common.sh
9
+ file bin/lib/config.sh
10
+ file bin/lib/passport.sh
11
+ file bin/lib/tool-mapping.sh
12
+ file bin/lib/validation.sh
13
+ file python/aport_guardrails/core/tool-pack-mapping.json
14
+ file src/evaluator.js
15
+ tree external/aport-policies
16
+ file external/aport-spec/oap/passport-schema.json
17
+ mkdir local-overrides/policies