failproofai 0.0.2-beta.2 → 0.0.2-beta.4
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +17 -17
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +16 -16
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +8 -8
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__02nt~6d._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_0z7w.hh._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0103jwf._.js → [root-of-the-server]__05ib_c3._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__092s1ta._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__09icjsf._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0g.lg8b._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0h..k-e._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ovwjau._.js → [root-of-the-server]__0od~yp1._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0okos0k._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0w6l33k._.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__11pa2ra._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12t-wym._.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_10lm7or._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_0xerkr6._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_0q-m0y-._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +9 -9
- package/.next/standalone/.next/static/chunks/{0sm1iqi3m~xiz.js → 03qghe4e2_.ul.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0jrzwsyo7wo26.js → 0_eej2~ju.yds.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0tl2f-3yc.rqc.js → 0bc69j4t8njpq.js} +1 -1
- package/.next/standalone/.next/static/chunks/0nnxt7uoz_cvj.css +1 -0
- package/.next/standalone/.next/static/chunks/{0uftmw5od9kdz.js → 0qaojcc.nvqd8.js} +1 -1
- package/.next/standalone/.next/static/chunks/{001k0zayn2o.s.js → 0xg4wy053mmhs.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0pdd7~yp8ytu6.js → 12mgwr8gh_kqo.js} +2 -2
- package/.next/standalone/.next/static/chunks/{0tbr0o7vwc~-s.js → 134~t05vpu75e.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0wtcha31~i7rm.js → 17veghz_js0u3.js} +1 -1
- package/.next/standalone/Dockerfile.docs +12 -0
- package/.next/standalone/README.md +59 -46
- package/.next/standalone/app/components/session-hooks-panel.tsx +31 -6
- package/.next/standalone/app/policies/hooks-client.tsx +31 -6
- package/.next/standalone/dist/cli.mjs +234 -27
- package/.next/standalone/dist/index.js +2 -2
- package/.next/standalone/docs/{architecture.md → architecture.mdx} +40 -23
- package/.next/standalone/docs/{built-in-policies.md → built-in-policies.mdx} +134 -12
- package/.next/standalone/docs/cli/dashboard.mdx +28 -0
- package/.next/standalone/docs/cli/environment-variables.mdx +34 -0
- package/.next/standalone/docs/cli/hook.mdx +30 -0
- package/.next/standalone/docs/cli/install-policies.mdx +48 -0
- package/.next/standalone/docs/cli/list-policies.mdx +31 -0
- package/.next/standalone/docs/cli/remove-policies.mdx +44 -0
- package/.next/standalone/docs/cli/version.mdx +12 -0
- package/.next/standalone/docs/{configuration.md → configuration.mdx} +16 -16
- package/.next/standalone/docs/{custom-hooks.md → custom-policies.mdx} +80 -42
- package/.next/standalone/docs/{dashboard.md → dashboard.mdx} +26 -29
- package/.next/standalone/docs/docs.json +31 -4
- package/.next/standalone/docs/examples.mdx +253 -0
- package/.next/standalone/docs/for-agents.mdx +38 -0
- package/.next/standalone/docs/getting-started.mdx +134 -0
- package/.next/standalone/docs/introduction.mdx +57 -0
- package/.next/standalone/docs/logo/dark.svg +21 -0
- package/.next/standalone/docs/logo/light.svg +21 -0
- package/.next/standalone/docs/{package-aliases.md → package-aliases.mdx} +5 -5
- package/.next/standalone/docs/{testing.md → testing.mdx} +11 -11
- package/.next/standalone/package.json +6 -9
- package/.next/standalone/scripts/publish-aliases.mjs +4 -2
- package/.next/standalone/skills-lock.json +10 -0
- package/.next/standalone/src/hooks/builtin-policies.ts +271 -25
- package/.next/standalone/src/hooks/handler.ts +1 -0
- package/.next/standalone/src/hooks/hook-activity-store.ts +6 -1
- package/.next/standalone/src/hooks/policy-evaluator.ts +23 -2
- package/.next/standalone/src/hooks/policy-helpers.ts +2 -2
- package/.next/standalone/vitest.config.e2e.mts +3 -0
- package/.next/standalone/vitest.config.mts +3 -0
- package/README.md +59 -46
- package/dist/cli.mjs +234 -27
- package/dist/index.js +2 -2
- package/package.json +6 -9
- package/scripts/publish-aliases.mjs +4 -2
- package/src/hooks/builtin-policies.ts +271 -25
- package/src/hooks/handler.ts +1 -0
- package/src/hooks/hook-activity-store.ts +6 -1
- package/src/hooks/policy-evaluator.ts +23 -2
- package/src/hooks/policy-helpers.ts +2 -2
- package/.next/standalone/.next/static/chunks/15jpradyu_531.css +0 -1
- package/.next/standalone/docs/cli-reference.md +0 -175
- package/.next/standalone/docs/getting-started.md +0 -128
- package/.next/standalone/docs/introduction.md +0 -47
- /package/.next/standalone/.next/static/{JksWDLwDoPy6bcczVWlff → N7CmoNJD3b7hE1pCaP_Gs}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{JksWDLwDoPy6bcczVWlff → N7CmoNJD3b7hE1pCaP_Gs}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{JksWDLwDoPy6bcczVWlff → N7CmoNJD3b7hE1pCaP_Gs}/_ssgManifest.js +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Custom
|
|
3
|
-
description: "Write your own policies in JavaScript
|
|
2
|
+
title: Custom Policies
|
|
3
|
+
description: "Write your own policies in JavaScript - enforce conventions, prevent drift, detect failures, integrate with external systems"
|
|
4
4
|
icon: code
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Custom
|
|
7
|
+
Custom policies let you write rules for any agent behavior: enforce project conventions, prevent drift, gate destructive operations, detect stuck agents, or integrate with Slack, approval workflows, and more. They use the same hook event system and `allow`, `deny`, `instruct` decisions as built-in policies.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -32,7 +32,7 @@ customPolicies.add({
|
|
|
32
32
|
Install it:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
failproofai --install
|
|
35
|
+
failproofai policies --install --custom ./my-policies.js
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
---
|
|
@@ -40,17 +40,17 @@ failproofai --install-policies --custom ./my-policies.js
|
|
|
40
40
|
## Installing and updating
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
# Install with a custom
|
|
44
|
-
failproofai --install
|
|
43
|
+
# Install with a custom policies file
|
|
44
|
+
failproofai policies --install --custom ./my-policies.js
|
|
45
45
|
|
|
46
|
-
# Replace the
|
|
47
|
-
failproofai --install
|
|
46
|
+
# Replace the policies file path
|
|
47
|
+
failproofai policies --install --custom ./new-policies.js
|
|
48
48
|
|
|
49
|
-
# Remove the custom
|
|
50
|
-
failproofai
|
|
49
|
+
# Remove the custom policies path from config
|
|
50
|
+
failproofai policies --uninstall --custom
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The resolved absolute path is stored in `policies-config.json` as `customPoliciesPath`. The file is loaded fresh on every hook event
|
|
53
|
+
The resolved absolute path is stored in `policies-config.json` as `customPoliciesPath`. The file is loaded fresh on every hook event - there is no caching between events.
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
@@ -64,12 +64,12 @@ import { customPolicies, allow, deny, instruct } from "failproofai";
|
|
|
64
64
|
|
|
65
65
|
### `customPolicies.add(hook)`
|
|
66
66
|
|
|
67
|
-
Registers a
|
|
67
|
+
Registers a policy. Call this as many times as needed for multiple policies in the same file.
|
|
68
68
|
|
|
69
69
|
```ts
|
|
70
70
|
customPolicies.add({
|
|
71
|
-
name: string; // required
|
|
72
|
-
description?: string; // shown in
|
|
71
|
+
name: string; // required - unique identifier
|
|
72
|
+
description?: string; // shown in `failproofai policies` output
|
|
73
73
|
match?: { events?: HookEventType[] }; // filter by event type; omit to match all
|
|
74
74
|
fn: (ctx: PolicyContext) => PolicyResult | Promise<PolicyResult>;
|
|
75
75
|
});
|
|
@@ -79,13 +79,47 @@ customPolicies.add({
|
|
|
79
79
|
|
|
80
80
|
| Function | Effect | Use when |
|
|
81
81
|
|----------|--------|----------|
|
|
82
|
-
| `allow()` | Permit the
|
|
83
|
-
| `deny(message)` | Block the
|
|
84
|
-
| `instruct(message)` | Add context
|
|
82
|
+
| `allow()` | Permit the operation silently | The action is safe, no message needed |
|
|
83
|
+
| `deny(message)` | Block the operation | The agent should not take this action |
|
|
84
|
+
| `instruct(message)` | Add context without blocking | Give the agent extra context to stay on track |
|
|
85
85
|
|
|
86
|
-
`deny(message)`
|
|
86
|
+
`deny(message)` - the message appears to Claude prefixed with `"Blocked by failproofai:"`. A single `deny` short-circuits all further evaluation.
|
|
87
87
|
|
|
88
|
-
`instruct(message)`
|
|
88
|
+
`instruct(message)` - the message is appended to Claude's context for the current tool call. The first `instruct` wins — subsequent `instruct` returns from other policies are ignored.
|
|
89
|
+
|
|
90
|
+
### Informational allow messages (beta)
|
|
91
|
+
|
|
92
|
+
<Note>
|
|
93
|
+
`allow(message)` is a beta feature available since v0.0.2-beta.3. The API may change in future releases. Earlier versions only support `allow()` without arguments.
|
|
94
|
+
</Note>
|
|
95
|
+
|
|
96
|
+
`allow(message)` permits the operation **and** sends an informational message back to Claude. The message is delivered as `additionalContext` in the hook handler's stdout response — the same mechanism used by `instruct`, but semantically different: it's a status update, not a warning.
|
|
97
|
+
|
|
98
|
+
| Function | Effect | Use when |
|
|
99
|
+
|----------|--------|----------|
|
|
100
|
+
| `allow(message)` | Permit and send context to Claude | Confirm a check passed, or explain why a check was skipped |
|
|
101
|
+
|
|
102
|
+
Use cases:
|
|
103
|
+
- **Status confirmations:** `allow("All CI checks passed.")` — tells Claude everything is green
|
|
104
|
+
- **Fail-open explanations:** `allow("GitHub CLI not installed, skipping CI check.")` — tells Claude why a check was skipped so it has full context
|
|
105
|
+
- **Multiple messages accumulate:** if several policies each return `allow(message)`, all messages are joined with newlines and delivered together
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
customPolicies.add({
|
|
109
|
+
name: "confirm-branch-status",
|
|
110
|
+
match: { events: ["Stop"] },
|
|
111
|
+
fn: async (ctx) => {
|
|
112
|
+
const cwd = ctx.session?.cwd;
|
|
113
|
+
if (!cwd) return allow("No working directory, skipping branch check.");
|
|
114
|
+
|
|
115
|
+
// ... check branch status ...
|
|
116
|
+
if (allPushed) {
|
|
117
|
+
return allow("Branch is up to date with remote.");
|
|
118
|
+
}
|
|
119
|
+
return deny("Unpushed changes detected.");
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
89
123
|
|
|
90
124
|
### `PolicyContext` fields
|
|
91
125
|
|
|
@@ -111,25 +145,27 @@ customPolicies.add({
|
|
|
111
145
|
|-------|--------------|----------------------|
|
|
112
146
|
| `PreToolUse` | Before Claude runs a tool | The tool's input (e.g. `{ command: "..." }` for Bash) |
|
|
113
147
|
| `PostToolUse` | After a tool completes | The tool's input + `tool_result` (the output) |
|
|
114
|
-
| `Notification` | When Claude sends a notification | `{ message: "...", notification_type: "idle" \| "permission_prompt" \| ... }`
|
|
148
|
+
| `Notification` | When Claude sends a notification | `{ message: "...", notification_type: "idle" \| "permission_prompt" \| ... }` - hooks must always return `allow()`, they cannot block notifications |
|
|
115
149
|
| `Stop` | When the Claude session ends | Empty |
|
|
116
150
|
|
|
117
151
|
---
|
|
118
152
|
|
|
119
153
|
## Evaluation order
|
|
120
154
|
|
|
121
|
-
|
|
155
|
+
Policies are evaluated in this order:
|
|
122
156
|
|
|
123
157
|
1. Built-in policies (in definition order)
|
|
124
|
-
2. Custom
|
|
158
|
+
2. Custom policies (in `.add()` order)
|
|
125
159
|
|
|
126
|
-
|
|
160
|
+
<Note>
|
|
161
|
+
The first `deny` short-circuits all subsequent policies. The first `instruct` wins — subsequent `instruct` returns are ignored.
|
|
162
|
+
</Note>
|
|
127
163
|
|
|
128
164
|
---
|
|
129
165
|
|
|
130
166
|
## Transitive imports
|
|
131
167
|
|
|
132
|
-
Custom
|
|
168
|
+
Custom policy files can import local modules using relative paths:
|
|
133
169
|
|
|
134
170
|
```js
|
|
135
171
|
// my-policies.js
|
|
@@ -150,9 +186,9 @@ All relative imports reachable from the entry file are resolved. This is impleme
|
|
|
150
186
|
|
|
151
187
|
---
|
|
152
188
|
|
|
153
|
-
##
|
|
189
|
+
## Event type filtering
|
|
154
190
|
|
|
155
|
-
Use `match.events` to limit when a
|
|
191
|
+
Use `match.events` to limit when a policy fires:
|
|
156
192
|
|
|
157
193
|
```js
|
|
158
194
|
customPolicies.add({
|
|
@@ -172,34 +208,36 @@ Omit `match` entirely to fire on every event type.
|
|
|
172
208
|
|
|
173
209
|
## Error handling and failure modes
|
|
174
210
|
|
|
175
|
-
Custom
|
|
211
|
+
Custom policies are **fail-open**: errors never block built-in policies or crash the hook handler.
|
|
176
212
|
|
|
177
213
|
| Failure | Behavior |
|
|
178
214
|
|---------|----------|
|
|
179
|
-
| `customPoliciesPath` not set | No custom
|
|
215
|
+
| `customPoliciesPath` not set | No custom policies run; built-ins continue normally |
|
|
180
216
|
| File not found | Warning logged to `~/.failproofai/hook.log`; built-ins continue |
|
|
181
|
-
| Syntax/import error | Error logged to `~/.failproofai/hook.log`; all custom
|
|
217
|
+
| Syntax/import error | Error logged to `~/.failproofai/hook.log`; all custom policies skipped |
|
|
182
218
|
| `fn` throws at runtime | Error logged; that hook treated as `allow`; other hooks continue |
|
|
183
219
|
| `fn` takes longer than 10s | Timeout logged; treated as `allow` |
|
|
184
220
|
|
|
185
|
-
|
|
221
|
+
<Tip>
|
|
222
|
+
To debug custom policy errors, watch the log file:
|
|
186
223
|
|
|
187
224
|
```bash
|
|
188
225
|
tail -f ~/.failproofai/hook.log
|
|
189
226
|
```
|
|
227
|
+
</Tip>
|
|
190
228
|
|
|
191
229
|
---
|
|
192
230
|
|
|
193
|
-
## Full example: multiple
|
|
231
|
+
## Full example: multiple policies
|
|
194
232
|
|
|
195
233
|
```js
|
|
196
234
|
// my-policies.js
|
|
197
235
|
import { customPolicies, allow, deny, instruct } from "failproofai";
|
|
198
236
|
|
|
199
|
-
//
|
|
237
|
+
// Prevent agent from writing to secrets/ directory
|
|
200
238
|
customPolicies.add({
|
|
201
239
|
name: "block-secrets-dir",
|
|
202
|
-
description: "
|
|
240
|
+
description: "Prevent agent from writing to secrets/ directory",
|
|
203
241
|
match: { events: ["PreToolUse"] },
|
|
204
242
|
fn: async (ctx) => {
|
|
205
243
|
if (!["Write", "Edit"].includes(ctx.toolName ?? "")) return allow();
|
|
@@ -209,10 +247,10 @@ customPolicies.add({
|
|
|
209
247
|
},
|
|
210
248
|
});
|
|
211
249
|
|
|
212
|
-
//
|
|
250
|
+
// Keep the agent on track: verify tests before committing
|
|
213
251
|
customPolicies.add({
|
|
214
252
|
name: "remind-test-before-commit",
|
|
215
|
-
description: "
|
|
253
|
+
description: "Keep the agent on track: verify tests pass before committing",
|
|
216
254
|
match: { events: ["PreToolUse"] },
|
|
217
255
|
fn: async (ctx) => {
|
|
218
256
|
if (ctx.toolName !== "Bash") return allow();
|
|
@@ -224,10 +262,10 @@ customPolicies.add({
|
|
|
224
262
|
},
|
|
225
263
|
});
|
|
226
264
|
|
|
227
|
-
//
|
|
265
|
+
// Prevent unplanned dependency changes during freeze
|
|
228
266
|
customPolicies.add({
|
|
229
267
|
name: "dependency-freeze",
|
|
230
|
-
description: "
|
|
268
|
+
description: "Prevent unplanned dependency changes during freeze period",
|
|
231
269
|
match: { events: ["PreToolUse"] },
|
|
232
270
|
fn: async (ctx) => {
|
|
233
271
|
if (ctx.toolName !== "Bash") return allow();
|
|
@@ -245,17 +283,17 @@ export { customPolicies };
|
|
|
245
283
|
|
|
246
284
|
---
|
|
247
285
|
|
|
248
|
-
## Examples
|
|
286
|
+
## Examples
|
|
249
287
|
|
|
250
|
-
The `examples/` directory contains ready-to-run
|
|
288
|
+
The `examples/` directory contains ready-to-run policy files:
|
|
251
289
|
|
|
252
290
|
| File | Contents |
|
|
253
291
|
|------|----------|
|
|
254
|
-
| `examples/policies-basic.js` | Five starter policies covering common
|
|
255
|
-
| `examples/policies-advanced/index.js` | Advanced patterns
|
|
292
|
+
| `examples/policies-basic.js` | Five starter policies covering common agent failure modes |
|
|
293
|
+
| `examples/policies-advanced/index.js` | Advanced patterns: transitive imports, async calls, output scrubbing, and session-end hooks |
|
|
256
294
|
|
|
257
295
|
Install the basic examples:
|
|
258
296
|
|
|
259
297
|
```bash
|
|
260
|
-
failproofai --install
|
|
298
|
+
failproofai policies --install --custom ./examples/policies-basic.js
|
|
261
299
|
```
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Dashboard
|
|
3
|
-
description: "
|
|
3
|
+
description: "Monitor agent sessions, review tool calls, and manage policies"
|
|
4
4
|
icon: chart-line
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The failproofai dashboard is a local web application for
|
|
7
|
+
The failproofai dashboard is a local web application for monitoring your AI agent sessions and managing policies. See what your agents did while you were away.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -16,13 +16,7 @@ failproofai
|
|
|
16
16
|
|
|
17
17
|
Opens at `http://localhost:8020`.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
failproofai --start
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
The dashboard reads directly from the filesystem — your Claude Code project folders and the failproofai config files. Nothing is written to a remote service.
|
|
19
|
+
The dashboard reads directly from the filesystem - your Claude Code project folders and the failproofai config files. Nothing is written to a remote service.
|
|
26
20
|
|
|
27
21
|
---
|
|
28
22
|
|
|
@@ -53,11 +47,11 @@ Click a session to open the session viewer.
|
|
|
53
47
|
|
|
54
48
|
### Session viewer
|
|
55
49
|
|
|
56
|
-
|
|
50
|
+
The session viewer answers the key question for autonomous agents: what did the agent do, and did it stay on track? It shows a timeline of everything that happened in a session:
|
|
57
51
|
|
|
58
|
-
- **Messages**
|
|
59
|
-
- **Tool calls**
|
|
60
|
-
- **
|
|
52
|
+
- **Messages** - Claude's text responses and user prompts
|
|
53
|
+
- **Tool calls** - Every tool Claude invoked, with its input and output
|
|
54
|
+
- **Policy activity** - For each tool call, which policies fired and what decision they returned
|
|
61
55
|
|
|
62
56
|
The stats bar at the top shows session duration, total tool calls, and a summary of hook decisions (allow / deny / instruct counts).
|
|
63
57
|
|
|
@@ -67,24 +61,25 @@ You can export the session as a ZIP or JSONL file using the download button.
|
|
|
67
61
|
|
|
68
62
|
A two-tab page for managing policies and reviewing activity.
|
|
69
63
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- Toggle individual policies on or off with a single click (writes to `~/.failproofai/policies-config.json`)
|
|
73
|
-
- Expand a policy to configure its parameters (for policies that support `policyParams`)
|
|
74
|
-
- Install or remove hooks for a given scope
|
|
75
|
-
- Set a custom
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
|
|
64
|
+
<Tabs>
|
|
65
|
+
<Tab title="Policies tab">
|
|
66
|
+
- Toggle individual policies on or off with a single click (writes to `~/.failproofai/policies-config.json`)
|
|
67
|
+
- Expand a policy to configure its parameters (for policies that support `policyParams`)
|
|
68
|
+
- Install or remove hooks for a given scope
|
|
69
|
+
- Set a custom policies file path
|
|
70
|
+
</Tab>
|
|
71
|
+
<Tab title="Activity tab">
|
|
72
|
+
- Full paginated history of every hook event that has fired across all sessions
|
|
73
|
+
- Search by policy name, session ID, tool name, or decision
|
|
74
|
+
- Each row shows: timestamp, policy name, decision, tool name, session ID, and the reason for deny/instruct decisions
|
|
75
|
+
</Tab>
|
|
76
|
+
</Tabs>
|
|
82
77
|
|
|
83
78
|
---
|
|
84
79
|
|
|
85
80
|
## Auto-refresh
|
|
86
81
|
|
|
87
|
-
The dashboard has an auto-refresh toggle in the top navigation. When enabled, the current page refreshes periodically to show new sessions and
|
|
82
|
+
The dashboard has an auto-refresh toggle in the top navigation. When enabled, the current page refreshes periodically to show new sessions and policy activity as they appear. Essential for monitoring long-running autonomous agent sessions.
|
|
88
83
|
|
|
89
84
|
---
|
|
90
85
|
|
|
@@ -118,9 +113,9 @@ CLAUDE_PROJECTS_PATH=/custom/path/to/projects failproofai
|
|
|
118
113
|
|
|
119
114
|
## Accessing from a non-localhost host
|
|
120
115
|
|
|
121
|
-
When running the dashboard in **dev mode** (`npm run dev`) and accessing it from a hostname other than `localhost`
|
|
116
|
+
When running the dashboard in **dev mode** (`npm run dev`) and accessing it from a hostname other than `localhost` - for example, a custom domain, a remote IP, or a tunneled URL - you may see a warning like:
|
|
122
117
|
|
|
123
|
-
```
|
|
118
|
+
```text
|
|
124
119
|
⚠ Blocked cross-origin request to Next.js dev resource /_next/webpack-hmr from "dashboard.example.com".
|
|
125
120
|
```
|
|
126
121
|
|
|
@@ -142,4 +137,6 @@ You can also set the `FAILPROOFAI_ALLOWED_DEV_ORIGINS` environment variable inst
|
|
|
142
137
|
FAILPROOFAI_ALLOWED_DEV_ORIGINS=dashboard.example.com npm run dev
|
|
143
138
|
```
|
|
144
139
|
|
|
145
|
-
>
|
|
140
|
+
<Note>
|
|
141
|
+
This only applies to dev mode. When running `failproofai` (production mode), there is no HMR websocket and no cross-origin dev resource issue.
|
|
142
|
+
</Note>
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
"light": "#e4587d",
|
|
8
8
|
"dark": "#002CA7"
|
|
9
9
|
},
|
|
10
|
+
"logo": {
|
|
11
|
+
"light": "/logo/exosphere-light.png",
|
|
12
|
+
"dark": "/logo/exosphere-dark.png"
|
|
13
|
+
},
|
|
10
14
|
"favicon": "/favicon.ico",
|
|
11
15
|
"navigation": {
|
|
12
16
|
"tabs": [
|
|
@@ -23,15 +27,26 @@
|
|
|
23
27
|
{
|
|
24
28
|
"group": "Core Concepts",
|
|
25
29
|
"pages": [
|
|
26
|
-
"configuration",
|
|
27
30
|
"built-in-policies",
|
|
28
|
-
"custom-
|
|
31
|
+
"custom-policies",
|
|
32
|
+
"configuration"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"group": "CLI",
|
|
37
|
+
"pages": [
|
|
38
|
+
"cli/dashboard",
|
|
39
|
+
"cli/install-policies",
|
|
40
|
+
"cli/remove-policies",
|
|
41
|
+
"cli/list-policies",
|
|
42
|
+
"cli/hook",
|
|
43
|
+
"cli/version",
|
|
44
|
+
"cli/environment-variables"
|
|
29
45
|
]
|
|
30
46
|
},
|
|
31
47
|
{
|
|
32
48
|
"group": "Tools",
|
|
33
49
|
"pages": [
|
|
34
|
-
"cli-reference",
|
|
35
50
|
"dashboard"
|
|
36
51
|
]
|
|
37
52
|
},
|
|
@@ -40,7 +55,19 @@
|
|
|
40
55
|
"pages": [
|
|
41
56
|
"architecture",
|
|
42
57
|
"testing",
|
|
43
|
-
"package-aliases"
|
|
58
|
+
"package-aliases",
|
|
59
|
+
"for-agents"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"tab": "Examples",
|
|
66
|
+
"groups": [
|
|
67
|
+
{
|
|
68
|
+
"group": "Examples",
|
|
69
|
+
"pages": [
|
|
70
|
+
"examples"
|
|
44
71
|
]
|
|
45
72
|
}
|
|
46
73
|
]
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Examples
|
|
3
|
+
description: "How to set up hooks for Claude Code and the Agents SDK"
|
|
4
|
+
icon: book-open
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Ready-to-use examples for common scenarios. Each one shows how to install and what to expect.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Setting up hooks for Claude Code
|
|
12
|
+
|
|
13
|
+
Failproof AI integrates with Claude Code via its [hooks system](https://docs.anthropic.com/en/docs/claude-code/hooks). When you run `failproofai policies --install`, it registers hook commands in Claude Code's `settings.json` that fire on every tool call.
|
|
14
|
+
|
|
15
|
+
<Steps>
|
|
16
|
+
<Step title="Install failproofai">
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g failproofai
|
|
19
|
+
```
|
|
20
|
+
</Step>
|
|
21
|
+
<Step title="Enable all built-in policies">
|
|
22
|
+
```bash
|
|
23
|
+
failproofai policies --install
|
|
24
|
+
```
|
|
25
|
+
</Step>
|
|
26
|
+
<Step title="Verify hooks are registered">
|
|
27
|
+
```bash
|
|
28
|
+
cat ~/.claude/settings.json | grep failproofai
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
You should see hook entries for `PreToolUse`, `PostToolUse`, `Notification`, and `Stop` events.
|
|
32
|
+
</Step>
|
|
33
|
+
<Step title="Run Claude Code">
|
|
34
|
+
```bash
|
|
35
|
+
claude
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Policies now run automatically on every tool call. Try asking Claude to run `sudo rm -rf /` - it will be blocked.
|
|
39
|
+
</Step>
|
|
40
|
+
</Steps>
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Setting up hooks for the Agents SDK
|
|
45
|
+
|
|
46
|
+
If you're building with the [Agents SDK](https://docs.anthropic.com/en/docs/agents-sdk), you can use the same hook system programmatically.
|
|
47
|
+
|
|
48
|
+
<Steps>
|
|
49
|
+
<Step title="Install failproofai in your project">
|
|
50
|
+
```bash
|
|
51
|
+
npm install failproofai
|
|
52
|
+
```
|
|
53
|
+
</Step>
|
|
54
|
+
<Step title="Configure hooks in your agent">
|
|
55
|
+
Pass hook commands when creating your agent process. The hooks fire the same way as in Claude Code - via stdin/stdout JSON:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
failproofai --hook PreToolUse # called before each tool
|
|
59
|
+
failproofai --hook PostToolUse # called after each tool
|
|
60
|
+
```
|
|
61
|
+
</Step>
|
|
62
|
+
<Step title="Write a custom policy for your agent">
|
|
63
|
+
```javascript
|
|
64
|
+
import { customPolicies, allow, deny } from "failproofai";
|
|
65
|
+
|
|
66
|
+
customPolicies.add({
|
|
67
|
+
name: "limit-to-project-dir",
|
|
68
|
+
description: "Keep the agent inside the project directory",
|
|
69
|
+
match: { events: ["PreToolUse"] },
|
|
70
|
+
fn: async (ctx) => {
|
|
71
|
+
const path = String(ctx.toolInput?.file_path ?? "");
|
|
72
|
+
if (path.startsWith("/") && !path.startsWith(ctx.session?.cwd ?? "")) {
|
|
73
|
+
return deny("Agent is restricted to the project directory");
|
|
74
|
+
}
|
|
75
|
+
return allow();
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
</Step>
|
|
80
|
+
<Step title="Install the custom policy">
|
|
81
|
+
```bash
|
|
82
|
+
failproofai policies --install --custom ./my-agent-policies.js
|
|
83
|
+
```
|
|
84
|
+
</Step>
|
|
85
|
+
</Steps>
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Block destructive commands
|
|
90
|
+
|
|
91
|
+
The most common setup - prevent agents from doing irreversible damage.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
failproofai policies --install block-sudo block-rm-rf block-force-push block-curl-pipe-sh
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
What this does:
|
|
98
|
+
- `block-sudo` - blocks all `sudo` commands
|
|
99
|
+
- `block-rm-rf` - blocks recursive file deletion
|
|
100
|
+
- `block-force-push` - blocks `git push --force`
|
|
101
|
+
- `block-curl-pipe-sh` - blocks piping remote scripts to shell
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Prevent secret leakage
|
|
106
|
+
|
|
107
|
+
Stop agents from seeing or leaking credentials in tool output.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
failproofai policies --install sanitize-api-keys sanitize-jwt sanitize-connection-strings sanitize-bearer-tokens
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
These fire on `PostToolUse` - after a tool runs, they scrub the output before the agent sees it.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Get Slack alerts when agents need attention
|
|
118
|
+
|
|
119
|
+
Use the notification hook to forward idle alerts to Slack.
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
import { customPolicies, allow, instruct } from "failproofai";
|
|
123
|
+
|
|
124
|
+
customPolicies.add({
|
|
125
|
+
name: "slack-on-idle",
|
|
126
|
+
description: "Alert Slack when the agent is waiting for input",
|
|
127
|
+
match: { events: ["Notification"] },
|
|
128
|
+
fn: async (ctx) => {
|
|
129
|
+
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
|
|
130
|
+
if (!webhookUrl) return allow();
|
|
131
|
+
|
|
132
|
+
const message = String(ctx.payload?.message ?? "Agent is waiting");
|
|
133
|
+
const project = ctx.session?.cwd ?? "unknown";
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
await fetch(webhookUrl, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: { "Content-Type": "application/json" },
|
|
139
|
+
body: JSON.stringify({
|
|
140
|
+
text: `*${message}*\nProject: \`${project}\``,
|
|
141
|
+
}),
|
|
142
|
+
signal: AbortSignal.timeout(5000),
|
|
143
|
+
});
|
|
144
|
+
} catch {
|
|
145
|
+
// never block the agent if Slack is unreachable
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return allow();
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Install it:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
SLACK_WEBHOOK_URL=https://hooks.slack.com/... failproofai policies --install --custom ./slack-alerts.js
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Keep agents on a branch
|
|
162
|
+
|
|
163
|
+
Prevent agents from switching branches or pushing to protected ones.
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
import { customPolicies, allow, deny } from "failproofai";
|
|
167
|
+
|
|
168
|
+
customPolicies.add({
|
|
169
|
+
name: "stay-on-branch",
|
|
170
|
+
description: "Prevent the agent from checking out other branches",
|
|
171
|
+
match: { events: ["PreToolUse"] },
|
|
172
|
+
fn: async (ctx) => {
|
|
173
|
+
if (ctx.toolName !== "Bash") return allow();
|
|
174
|
+
const cmd = String(ctx.toolInput?.command ?? "");
|
|
175
|
+
if (/git\s+checkout\s+(?!-b)/.test(cmd)) {
|
|
176
|
+
return deny("Stay on the current branch. Create a new branch with -b if needed.");
|
|
177
|
+
}
|
|
178
|
+
return allow();
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Require tests before commits
|
|
186
|
+
|
|
187
|
+
Remind agents to run tests before committing.
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
import { customPolicies, allow, instruct } from "failproofai";
|
|
191
|
+
|
|
192
|
+
customPolicies.add({
|
|
193
|
+
name: "test-before-commit",
|
|
194
|
+
description: "Remind the agent to run tests before committing",
|
|
195
|
+
match: { events: ["PreToolUse"] },
|
|
196
|
+
fn: async (ctx) => {
|
|
197
|
+
if (ctx.toolName !== "Bash") return allow();
|
|
198
|
+
const cmd = String(ctx.toolInput?.command ?? "");
|
|
199
|
+
if (/git\s+commit/.test(cmd)) {
|
|
200
|
+
return instruct("Run tests before committing. Use `npm test` or `bun test` first.");
|
|
201
|
+
}
|
|
202
|
+
return allow();
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Lock down a production repo
|
|
210
|
+
|
|
211
|
+
Commit a project-level config so every developer on your team gets the same policies.
|
|
212
|
+
|
|
213
|
+
Create `.failproofai/policies-config.json` in your repo:
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"enabledPolicies": [
|
|
218
|
+
"block-sudo",
|
|
219
|
+
"block-rm-rf",
|
|
220
|
+
"block-force-push",
|
|
221
|
+
"block-push-master",
|
|
222
|
+
"block-env-files",
|
|
223
|
+
"sanitize-api-keys",
|
|
224
|
+
"sanitize-jwt"
|
|
225
|
+
],
|
|
226
|
+
"policyParams": {
|
|
227
|
+
"block-push-master": {
|
|
228
|
+
"protectedBranches": ["main", "release", "production"]
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Then commit it:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
git add .failproofai/policies-config.json
|
|
238
|
+
git commit -m "Add failproofai team policies"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Every team member who has failproofai installed will automatically pick up these rules.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## More examples
|
|
246
|
+
|
|
247
|
+
The [`examples/`](https://github.com/exospherehost/failproofai/tree/main/examples) directory in the repo contains:
|
|
248
|
+
|
|
249
|
+
| File | What it shows |
|
|
250
|
+
|------|---------------|
|
|
251
|
+
| `policies-basic.js` | Starter policies - block production writes, force-push, piped scripts |
|
|
252
|
+
| `policies-notification.js` | Slack alerts for idle notifications and session end |
|
|
253
|
+
| `policies-advanced/index.js` | Transitive imports, async hooks, PostToolUse output scrubbing, Stop event handling |
|