Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 1.0.13-beta.1 — 2026-10-28

### Fixes

- **OpenClaw enforcement works again on OpenClaw 2026.9.6 and later.** Those releases import plugins from a copy under `~/.openclaw/tmp/plugin-captures/` that holds only the plugin's own directory, so the plugin's CLI path, worked out relative to its own file (`../dist/cli.mjs`), pointed at nothing; it read the failed spawn as an allow, so every OpenClaw tool call ran unchecked and no hook activity was recorded, while session telemetry kept flowing. The plugin now asks `failproofaid` for each verdict over its Unix socket, as the Hermes plugin does, sending the request `failproofai --hook … --cli openclaw` sends, so policies and verdicts are unchanged. Like Hermes it now requires a running daemon: install refuses without one, and when no verdict comes back tool calls and prompts are blocked with the reason logged instead of being allowed. Restart the OpenClaw gateway after updating so it loads the new plugin (#956)

### Dependencies

- Bump `nltk` 3.10.3 → 3.10.4 in `sdk/python/uv.lock`, the SDK's dev/test lockfile (transitive via `llama-index-core`), for GHSA-j456-xh4h-cpf2, published 2026-10-28, which turned OSV-Scanner red on every pull request. The SDK itself ships with zero runtime dependencies (#956)

## 1.0.13-beta.0 — 2026-10-27

### Features
Expand Down
36 changes: 25 additions & 11 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,8 @@ OpenClaw has two extension surfaces: file-based **internal hooks**
**plugin hooks** (`api.on(name, handler, {priority, timeoutMs})`) which have
block/cancel semantics. So — like OpenCode — failproofai ships a **plugin**
(`openclaw-plugin/`, a static package like `pi-extension/`, shipped in the npm
tarball via `package.json` "files") that async-spawns the binary and translates
the verdict. The install registers it in `~/.openclaw/openclaw.json` (JSON):
tarball via `package.json` "files") that asks `failproofaid` for each verdict and
translates it. The install registers it in `~/.openclaw/openclaw.json` (JSON):

- `plugins.load.paths[]` ← the shipped `openclaw-plugin/` dir (absolute path)
- `plugins.entries.failproofai = { enabled: true, hooks: { allowConversationAccess: true } }`
Expand All @@ -450,15 +450,29 @@ the verdict. The install registers it in `~/.openclaw/openclaw.json` (JSON):
disabled by default), so `getSettingsPath` ignores scope/cwd. Uninstall only
edits `openclaw.json`; it **never deletes the shipped plugin dir**.

**Async spawn, never `spawnSync`.** OpenClaw is a long-running multi-channel
gateway; a sync spawn on every hook would stall every channel. The shim
(`openclaw-plugin/index.js`) uses async `spawn` + a 30s guard and **fails open**
(any spawn/parse/timeout error → allow). It carries **no inline tool maps**
(unlike the OpenCode/Pi shims) — it forwards raw event/tool names and a
Claude-shaped stdin (`params→tool_input`, `toolName→tool_name`,
`transcriptPath→transcript_path`, `stopHookActive→stop_hook_active`,
`sessionKey→session_id`), and the binary canonicalizes via the `OPENCLAW_*`
maps in `types.ts` (single source of truth).
**The plugin talks to the daemon's socket; it never spawns the CLI.** OpenClaw ≥
2026.9.6 imports plugins from a **captured copy**
(`~/.openclaw/tmp/plugin-captures/<id>/…/node_modules/openclaw-plugin/`) holding only
the plugin directory's files, so a CLI path derived from `import.meta.url`
(`../dist/cli.mjs`) points at nothing there. The old shim spawned exactly that and
read the spawn failure as allow — every tool call ran unchecked and no hook activity
was recorded (verified live on 2026.10.5). So `openclaw-plugin/daemon-client.js`
sends the same `hook` request `failproofai --hook <event> --cli openclaw` sends
(crates/PROTOCOL.md: `hookEvent` = the raw OpenClaw event, `cli: "openclaw"`, the
payload as `stdin`, the gateway profile's `openclaw.json` as `agentSettingsPath`)
over the owner-only socket, like the Hermes plugin, and reads the same flat stdout
verdict back. Every module in `openclaw-plugin/` imports only node built-ins and
its siblings (a test copies the directory and loads it from the copy). Like Hermes
it is **daemon-only** (`requiresHealthyDaemon`): install refuses without a daemon,
and when no verdict comes back `before_tool_call` and `before_agent_run` **fail
closed** (blocked, reason logged through `api.logger`) while `before_agent_finalize`
does not force a revise — that cannot bring the daemon back and would only loop the
turn. All I/O is async: the gateway is a long-running multi-channel process. It
carries **no inline tool maps** (unlike the OpenCode/Pi shims) — it forwards raw
event/tool names and a Claude-shaped stdin (`params→tool_input`,
`toolName→tool_name`, `transcriptPath→transcript_path`,
`stopHookActive→stop_hook_active`, `sessionKey→session_id`), and the worker
canonicalizes via the `OPENCLAW_*` maps in `types.ts` (single source of truth).

**Verdict mapping** (`policy-evaluator.ts` emits a flat Pi-style
`{permission, reason}`; the shim maps per-hook):
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "3"
members = ["crates/*"]

[workspace.package]
version = "1.0.13-beta.0"
version = "1.0.13-beta.1"
edition = "2024"
license-file = "LICENSE"
repository = "https://github.com/FailproofAI/failproofai"
1 change: 1 addition & 0 deletions __tests__/hooks/integrations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ describe("integrations registry", () => {
it("declares daemon-only installation requirements in registry metadata", () => {
expect(listIntegrations().filter((integration) => integration.requiresHealthyDaemon)).toEqual([
hermes,
openclaw,
]);
});

Expand Down
Loading
Loading