Skip to content

feat: after_tool_call → POST /audit (success-only evidence) + config resolution#12

Merged
olivrg merged 1 commit into
mainfrom
feat/after-tool-call
Jun 18, 2026
Merged

feat: after_tool_call → POST /audit (success-only evidence) + config resolution#12
olivrg merged 1 commit into
mainfrom
feat/after-tool-call

Conversation

@olivrg

@olivrg olivrg commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Implements the after_tool_call → POST /audit hook (issue #11 Part B) and wires up operator config resolution so the evidence path is reachable in production.

after_tool_call/audit

  • Claims the evaluation_id from the correlation registry (claim miss → no POST; the pending evaluation expires server-side as designed).
  • Maps status from event.error (error/success), plus duration_ms and result.
  • Success-only, config-driven evidence: per-tool rules with explicit array-segment paths (["url","host"], not dotted) extracted from event.result; a missing path skips the entry.
  • Observational/best-effort — never throws across the hook. The payload is hardened so a call that ran always finalizes its audit + counters rather than decaying into a false evaluation_expired:
    • serialization-safe via a single JSON.parse(JSON.stringify(...)) snapshot (no probe-vs-send TOCTOU; tolerates throwing getters / BigInt / circular refs),
    • per-entry evidence filtering (one bad entry doesn't drop the rest),
    • size budgeting under the 1 MiB sideband limit: shed result → trim evidence → truncate (UTF-8-safe) then omit an oversized error, keeping the always-serializable core.
  • 200 already_finalized (and any 2xx) is treated as success by the client; we audit unconditionally.

Config resolution

  • parseConfig validates api.pluginConfig with zod (defaults mirror openclaw.plugin.json); the evidence config is now consumed at runtime.
  • Fail-closed on misconfig: invalid config or a missing adapter token registers blocking before_tool_call + before_install gates and logs loudly — it never skips hook registration, which would leave the tool surface ungoverned. Host-independent (does not rely on the gateway aborting on a register() throw).

Tests

  • TDD throughout. after-tool-call.test.ts (claim/status/evidence/serialization/budget/error-truncation), config.test.ts (zod validation + defaults), and an end-to-end index.test.ts proving configured evidence rules reach the /audit body through real plugin registration.
  • pnpm verify green: 162 tests (vitest runtime + typecheck projects), typecheck clean, build success.

Not in this PR

before_install → /install-scan remains a stub (next task); Part C (live e2e + npm publish) follows.

…resolution

after_tool_call → /audit:
- Claim evaluation_id from the correlation registry (miss → no POST; the pending
  evaluation expires server-side). Map status from event.error, plus duration_ms
  and result.
- Success-only, config-driven evidence: per-tool rules with explicit array-segment
  paths extracted from event.result (a missing path skips the entry).
- Observational/best-effort — never throws across the hook. Payload is
  serialization-safe (snapshot once → TOCTOU-free) and size-budgeted under the
  1 MiB sideband limit (shed result → trim evidence → truncate, then omit, an
  oversized error), so a call that ran always finalizes its audit + counters.

Config resolution:
- parseConfig validates api.pluginConfig with zod; defaults mirror the manifest.
  Operator-configured evidence rules are now reachable in production.
- Fail-closed on misconfig: invalid config or a missing adapter token registers
  blocking before_tool_call/before_install gates (and logs), rather than skipping
  registration — which would leave the tool surface ungoverned.

162 tests; typecheck and build clean.
@olivrg olivrg merged commit 6bcbbf9 into main Jun 18, 2026
1 check passed
@olivrg olivrg deleted the feat/after-tool-call branch June 18, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant