@asqav/pi
Asqav extension for the pi coding agent - sign and gate tool calls before they execute
Package details
Install @asqav/pi from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@asqav/pi- Package
@asqav/pi- Version
0.1.0- Published
- Jun 29, 2026
- Downloads
- 148/mo · 16/wk
- Author
- jagmarques
- License
- MIT
- Types
- extension
- Size
- 15.1 KB
- Dependencies
- 1 dependency · 0 peers
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@asqav/pi
Stop a rogue agent before it acts, and prove what it tried. This package is an extension for the pi coding agent that guards pi's tool calls with Asqav. It signs the intended tool call before the tool runs, and blocks the call when Asqav refuses. Every bash, write, and edit your coding agent attempts becomes a tamper-evident receipt, signed server-side with NIST FIPS 204 ML-DSA-65. The agent never holds the signing key, so it cannot forge the record.
Asqav governs the agents you wire through it. An agent that never routes through the governed path produces no receipt and is not detected.
This is a pre-execution gate. The extension subscribes to pi's tool_call event, which fires before the tool executes and can block, signs tool:start, and returns a block when a call is refused so the tool never runs. After execution it signs a matching tool:end receipt with the outcome.
How it hooks in
Pi extensions are TypeScript modules that subscribe to lifecycle events. The tool_call event fires after tool_execution_start and before the tool executes, and a handler may return { block: true, reason } to stop the tool. The tool_result event fires after execution. This extension uses exactly those two events and nothing else, so it stays out of the way of your other extensions.
References, cold-verified:
- Extensions, covering
tool_call("Can block") andtool_result - Pi packages, covering the
pimanifest and install sources
asqav Pi (locked distribution)
Want Pi governed by default, in every process including sub-agents? Run the locked
distribution: upstream Pi + @asqav/pi installed globally + fail-closed. It is not a
fork. See docs/asqav-pi-distribution.md and run:
scripts/install-asqav-pi.sh --help
Install
pi install npm:@asqav/pi
Pi can also install straight from GitHub:
pi install git:github.com/jagmarques/asqav-pi
Pi runs npm install for the package, which pulls in the @asqav/sdk dependency automatically.
Setup
Set your Asqav API key and run pi as usual:
export ASQAV_API_KEY="sk_..."
pi
Every tool call pi makes now produces signed tool:start and tool:end receipts through the Asqav API. Governance is intended once the extension loads, so if it cannot initialize (no ASQAV_API_KEY, or the signer is unreachable at startup) it fails closed and blocks every tool call rather than letting pi run ungoverned. Opt out deliberately with ASQAV_FAIL_OPEN=true.
Environment options:
ASQAV_AGENT_NAME: the agent name on receipts. Defaults topi.ASQAV_OBSERVE_ONLY=true: sign everything, never block. This only takes effect once init succeeds; if init fails the fail-closed default still blocks every tool (useASQAV_FAIL_OPEN=truefor audit-only setups that must keep running through a startup signer outage).ASQAV_FAIL_CLOSED=true: block tools when Asqav is unreachable mid-session (a signing transport error). The default here is fail-open so a transient outage never breaks a working coding agent. A real deny always blocks regardless.ASQAV_FAIL_OPEN=true(orASQAV_FAIL_CLOSED=false): deliberate dev opt-out that restores the old inactive/allow behavior when init fails. Pi runs ungoverned, so use it only when you know that is what you want.
Programmatic use
When embedding pi via its SDK, or when you want full control over the agent identity and options, register the extension yourself:
import { init, Agent } from "@asqav/sdk";
import { registerAsqav } from "@asqav/pi/extensions/asqav.ts";
init({ apiKey: process.env.ASQAV_API_KEY! });
const agent = await Agent.create({ name: "ci-coding-agent" });
registerAsqav(pi, {
agent,
tools: ["bash", "write", "edit"],
failClosed: true,
});
registerAsqav(pi, options) accepts:
agent, required: a pre-built AsqavAgentfrom@asqav/sdk.block, defaulting totrue: when a preflight is refused, block the tool. Setfalsefor observe-only signing.tools: only sign these tool names. Defaults to all tools.signResults, defaulting totrue: sign atool:endreceipt after each tool runs.preflight: a custom(actionType, input) => { allowed, reason }check. Defaults toagent.preflight, which checks revocation, suspension, and active policies.failClosed, defaulting tofalse: when a signing transport error occurs, block the tool.onError: sink for signing transport errors. Defaults toconsole.warn.
How blocking works
When the extension blocks, it returns { block: true, reason } from the tool_call handler. Pi surfaces the block to the model as a failed tool call, so the model sees why and can react. The receipt for the refused call records policy_decision: "deny", giving you proof of what the agent tried.
License
MIT