guard_status
Dashboard: session, diff delta, loop streaks, evidence count, contract size, last verify.
Takes no parameters.

io.github.XJTLUmediaPublished by GitHub account XJTLUmediav2.0.0Updated Nov 5, 2026
Agent guardrails: contract, evidence ledger, loop brake, diff-scope warnings, durable journal.
Local guardrail suite for AI coding agents: a persistent contract, an evidence ledger of verification runs, loop detection, diff-scope audits and a journal.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Follow the launch instructions in the repository.
Durable context, real evidence, and a brake pedal for AI coding agents.
protect-context keeps the rules, decisions, and proof-of-work an agent needs in
front of it — then makes scope creep, unverified claims, and repeated failures
visible before they cost you a session.
[license: MIT] [pi-package] [python 3.10+] [enforcement: advisory-first] [runtime dependencies: zero]
One project-local .agent-guard/ store · three interfaces backed by the same
logic:
The suite is advisory-first. It records evidence and makes unsafe or
repetitive behavior visible; it is not a sandbox and does not prove that a
change is correct. SPEC.md is the frozen v1.0 contract.
Long agent sessions fail in boring, expensive ways:
protect-context treats those as state problems, not intelligence problems.
A durable store plus three small habits — brief before work, evidence after
work, journal when something is decided — removes most of the failure surface
without turning the agent into a different agent.
Python 3.10+ is required for the CLI and MCP server. Node 22.19+ is required only to validate the TypeScript extension from a checkout — pi supplies the extension's runtime modules, and the extension has zero npm runtime dependencies.
Installed from npm. The package declares "pi": { "extensions": ["./extension/index.ts"], "image": "./preview.png" } and the pi-package
keyword required by the Pi package gallery:
Install it through pi, not through npm install. The extension declares pi's
host modules (@earendil-works/pi-ai, @earendil-works/pi-coding-agent,
typebox) as "*" peer dependencies, as Pi requires; Pi suppresses peer
installation for managed npm packages, while a plain npm install protect-context does not. The second path downloads the whole pi runtime and
surfaces advisories from that tree, which belong to the host and not to this
zero-runtime-dependency extension.
Remove any hand-installed copy first — a directory such as
~/.pi/agent/extensions/protect-context or .pi/extensions/protect-context,
or pi remove npm:protect-context if that copy came from npm. Two copies
register the same tool names, and pi drops the second one:
One error is reported per tool (guard_verify, guard_note,
guard_contract_check, guard_status), so nothing from the npm copy loads
until the other copy is gone.
Or copy/symlink the checkout into a trusted extension directory:
Start a session and you get a single-line status widget, a brief before the
first turn, and five slash commands. No configuration file is needed: the
extension seeds .agent-guard/config.json with the frozen defaults on first
session start.
For pi with pi-mcp-adapter, add .mcp.json at the project root:
The same stdio shape works in any generic MCP host. No checkout is needed
either: uvx protect-context mcp runs the same server straight from PyPI. Set
PC_SESSION_ID when several host sessions need distinct evidence and loop views:
The MCP surface exposes the nine frozen v1.0 tools plus two compatible read
surfaces added to close the original reachability gaps (guard_journal and
guard_orientation). Tools carry conservative MCP annotations; in particular
guard_verify is marked destructive and open-world because it can run any
shell command. Annotations are host hints, not enforcement — configure the host
to require human approval for that tool.
The CLI and MCP server are distributed through PyPI; the npm package ships only
the pi extension. Both read the same .agent-guard/ directory, so a project can
use the extension in pi and scripted verification in CI at the same time.
If the machine's default Node is older than 22.19, run the same checks with an
ephemeral Node 22 binary after npm ci:
Excerpts from a real session in this repository.
Ask for the current state:
Record proof instead of asserting it:
Re-run the same failing command a third time and the brake engages:
What pi injects before the agent starts working (sections elided):
And the always-on one-line widget:
Slash commands:
Model-callable tools:
Every command returns 0 for OK, 1 for a triggered warning or failed
verification, and 2 for an input/environment error.
The extension seeds .agent-guard/config.json on first session start; init
does the same for CLI/MCP-first use. Unknown, wrongly typed, fractional, or
out-of-range values fall back to the frozen default.
Numeric thresholds may be set to zero; the extension records an explicit first notification so a zero watermark does not silently disable alerts. Preflight checks at most 20 configured tool entries. If more are configured, it lists the omitted count and returns a warning instead of claiming the environment is fully checked. Version probes have a two-second per-tool timeout, share a 15-second overall deadline, and decode at most 4 KiB of output; a missing version is reported as an incomplete preflight.
The twelve seeds and the config defaults are the same for everyone, on purpose: they are the floor that makes the guardrail comparable across machines. What is yours goes in a personal layer that every project inherits:
The directory is $PC_USER_DIR, else $XDG_CONFIG_HOME/protect-context, else
~/.config/protect-context. Write it from the owner surfaces:
The same flag works in pi: /guard-contract add <text> --global and
/guard-contract remove <id> --global. Without it, writes go to the project
store as before, and guard_contract (the agent-callable tool) never touches
the personal layer.
Rules that make the layering predictable:
03-stay-in-scope) to
reword a seed for every repo.config.json with every
default value; a project value equal to the built-in default therefore does not
pin it, so your personal ctx_warn_pct still applies. Any different project
value is an explicit choice and wins.--global; a project-scope remove says so instead of silently doing nothing.--global write locks the personal
directory, not the project store, so two projects writing personal state cannot
race each other..agent-guard/ is runtime state and normally gitignored. If a team wants one
shared owner contract, track only that file:
Runtime files (events, evidence, journal, session state) stay local, while
contract.json becomes reviewable in pull requests. Personal constraints still
apply on top — each teammate's personal layer is weaker than the shared project
row.
The nine catastrophic patterns that trigger the pi confirmation are frozen and deliberately narrow, so benign lookalikes stay silent:
rm -rf / · rm -rf ~ · git push --force (not --force-with-lease) ·
git reset --hard · mkfs … /dev/… · dd … of=/dev/… ·
chmod -R 777 / · curl … | sh · sudo rm
Runtime files stay in the frozen .agent-guard/ layout and are gitignored:
config.json · contract.json · journal.jsonl · events.jsonl ·
verify_runs.jsonl · state.json
The optional personal layer lives outside the repository at
~/.config/protect-context/ (contract.json, config.json) and is never
written implicitly; see Making it yours above.
Ledgers bound themselves instead of growing forever: once events.jsonl,
verify_runs.jsonl or journal.jsonl passes 1 MiB it rotates to
<name>.1.jsonl, keeping at most three rotated segments and dropping the oldest.
Reads concatenate the segments oldest-first, so recent rows and the highest
evidence id always survive; budget and loop streaks describe the retained
window. Session baselines are keyed per session inside state.json, so parallel
pi sessions in one repo no longer overwrite each other's watermarks.
Python and TypeScript use the same atomic-directory lease at
.agent-guard/.lock.d, with an owner token, PID/hostname checks, bounded
waiting, and conservative stale recovery. Python also takes its legacy local
fcntl.flock inside that lease. Evidence-ID allocation, JSONL appends, and
contract read-modify-write operations hold the shared lease, preventing
duplicate IDs and lost contract updates across runtimes. JSON file replacement
uses unique temporary files.
The lease follows the atomic mkdir design used by mature lock libraries while
remaining dependency-free and interoperable with Python. A crashed same-host
owner is reclaimable after the stale interval; a stale foreign-host owner fails
closed because process liveness cannot be proved safely without an external
coordinator. Recovery retains a small identity-bound .lock.d.stale.*
directory as a durable race guard; it does not block new writers and lives
inside the already-ignored runtime store. Contract mutations also fail closed
when owner rows are malformed, preserving the original file for repair.
"Shipped" means the mitigation is reachable and tested. It does not turn an advisory signal into a security or correctness guarantee.
The extension is a thin event layer over the shared store. Nothing leaves your machine: there are no network calls and no LLM calls in the guardrail logic.
The brief is deliberately tiny — a persistent prompt that grows without bound is its own failure mode. Within the frozen 12-line constraint cap:
An agent-authored constraint therefore cannot crowd out a seed. Below the
contract it carries up to 3 recent trusted decisions, up to 3 trusted
corrections, up to 2 open loops, and the last evidence ID with its exit code,
duration, and a truncated command. brief_mode: "changed" (default) injects
only when the rendered digest actually changed, so a quiet turn costs nothing.
guard_verify runs your command with the caller's privileges, spools output to
temporary files to bound RAM, and records one append-only entry: ID, exit code,
duration, command, and a digest of the output. IDs are allocated inside the
shared lease, so parallel pi and CLI processes never collide.
Loop detection normalizes a command (whitespace collapsed, including inside
quoted strings) into a fingerprint, then counts consecutive failures of that
fingerprint across both bash and guard_verify. At loop_block_after the
attempt is refused, with the prior failure count and a change-strategy nudge —
the single cheapest intervention in the suite.
Diffs are computed with git porcelain/numstat over tracked, staged, and
bounded non-ignored untracked files, then compared against the configured line
and file thresholds to produce ok, warn, or hard. The pi extension freezes
its baseline at session start, so committing mid-session does not reset the
scope; CLI and MCP default to HEAD and accept an explicit baseline.
Untracked content reads are capped at 1,000 files, 1 MB per file, and 8 MB
total, and stop once the hard line threshold is known. A truncated scan is
reported as a bounded scan, promoted to at least a warning (CLI exit 1), and
its count treated as a lower bound — the widget renders it as Δ≥.
Direct CLI and pi slash-command contract writes are treated as owner input.
MCP/model journal writes are forcibly tagged agent: they never enter the
trusted decisions:/corrections: sections. They are not discarded either —
the brief shows the newest two under agent notes (unverified):, clearly
labelled and capped shorter than trusted text, and guard_journal returns the
full stored entry. That keeps the model's own reasoning recoverable after a
compaction without granting it authority. CLI and pi slash-command journal
entries are trusted. Untagged
legacy entries are treated as trusted for backward compatibility, because the
frozen journal schema has no dedicated author field.
This is a provenance convention, not authentication. A model that already has shell access can invoke the CLI and appear on that trusted path; use host-level tool permissions when the distinction must be a security boundary.
All newly stored contract, journal, and tag text is normalized to one bounded line, and legacy values are normalized again when rendered. This prevents terminal-line forging and unbounded persistent prompt growth; it does not make untrusted prose safe to follow.
Every injected and printed block has a cap: brief sections, journal and contract
text, evidence digests, orientation output, and previews. Context-watermark
notifications fire at ctx_warn_pct and above, and the widget always shows the
current percentage. The result is a guardrail layer whose own cost stays
constant while a session grows.
npm run check runs the typecheck and the extension tests together. The
definition-of-done checks are run twice consecutively before release. The dated
audit records the observed counts, integration probes, and package smoke test
instead of leaving stale numbers in this README.
guard_verify deliberately invokes a shell with the caller's privileges. It
can modify files, access the network, or disclose data. The pi layer asks for
confirmation only when one of the frozen regex patterns matches; the MCP layer
relies on host approval. Neither is a shell parser or a sandbox.guard_verify to an untrusted model without a real host sandbox/approval
policy.HEAD; pass them an explicit baseline for cross-commit scope. Binary files
count as files but zero lines.guard_journal, but hidden entries are
not enforced by context injection.addressed result as a review prompt, not proof.guard_journal returns the full entry.AgentStart, where before_agent_start does not run. The extension
arms a context hook on session_compact and re-asserts the block on every
request until a persisted one is back in the conversation. On a host that
does not emit context, the block returns at the next user prompt instead..agent-guard/state.json. That is conversation content: it stays local and
gitignored, but review it before sharing a store, and clear it with
/guard-task --clear.state.json is project-global. Atomic replacement prevents torn JSON, but
simultaneous sessions can still overwrite notification/session state; use
PC_SESSION_ID to separate ledger views, not state files.The implementation was checked against primary or professional sources:
proper-lockfile design, which uses atomic mkdir rather than
O_EXCL: moxystudio/node-proper-lockfile.
The package itself was not added because the frozen extension requirement is
zero runtime dependencies and the lock must interoperate with Python.No network or LLM calls are made by the guardrail logic itself. It is not a sandbox, policy engine, auto-fixer, secret manager, or replacement for pi/MCP host permissions. It complements those controls with durable context, evidence, telemetry, and narrowly scoped blocking.
MIT — see LICENSE. Copyright (c) 2026 XJTLUmedia.
Source: README.md at commit 9f1bf1a
Dashboard: session, diff delta, loop streaks, evidence count, contract size, last verify.
Takes no parameters.
Run a command and RECORD the outcome as evidence (id, exit, digest) in verify_runs.jsonl. shell=True is by design: the command runs verbatim with the caller's own privileges — this is an evidence ledger, not a sandbox. Never raises on nonzero exit; failures are recorded (that is the point). timeout_s is clamped to [1, 900] so a runaway value cannot wedge the host.
commandrequiredtimeout_sRecent verification runs from the evidence ledger.
limitContract store: action="list" | "add" (with text) | "remove" (with id). Seed constraints (source="seed") cannot be removed by the model — the owner removes them via the CLI or /guard-contract, so a model cannot delete its own injected guardrails (e.g. 02-evidence-before-success).
idtextactionrequiredJournal an agent-authored entry with a durable ``agent`` provenance tag.
tagstextrequiredtyperequiredRead recent journal entries, optionally filtered by journal type. limit is clamped to a compact, finite range at the journal boundary.
typelimitFailing-command streaks for this session + change-strategy advice.
Takes no parameters.
Diff audit vs HEAD (or a baseline ref): numstat stats + verdict vs thresholds.
baselineTool presence/versions for configured tools + git-repo check.
Takes no parameters.
Session tool-output chars, estimated tokens (chars/4), top-5 tools.
Takes no parameters.
Bounded repository tree, language counts, and conventions files.
Takes no parameters.
90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
pi install npm:protect-context
# Try it for one run without adding it to settingspi -e npm:protect-contextError: Failed to load extension "…/.pi/npm/node_modules/protect-context/extension/index.ts":Tool "guard_verify" conflicts with /home/you/.pi/agent/extensions/protect-context/index.ts# Personalmkdir -p ~/.pi/agent/extensionscp -r extension ~/.pi/agent/extensions/protect-context
# Project-local (read after the project is trusted)mkdir -p .pi/extensionscp -r extension .pi/extensions/protect-context{ "mcpServers": { "protect-context": { "command": "/absolute/path/to/protect-context/.venv/bin/python", "args": ["-m", "protect_context.mcp_server"] } }}{ "mcpServers": { "protect-context": { "command": "/absolute/path/to/protect-context/.venv/bin/python", "args": ["-m", "protect_context.mcp_server"], "env": { "PC_SESSION_ID": "my-session" } } }}pip install protect-context # or: pipx install protect-contextprotect-context init # seed .agent-guard/ in the current projectprotect-context statusprotect-context verify "pytest -q"protect-context mcp # serve the MCP tools on stdio# Python package, CLI, MCP server, and Python testspython3 -m venv .venv.venv/bin/python -m pip install -e ".[dev]"
# Reproducible extension development checks (dev dependencies only)npm ci --ignore-scriptsnpm run checknpx -y node@22 node_modules/typescript/bin/tsc --noEmit -p extension/tsconfig.jsonnpx -y node@22 --experimental-strip-types --test extension/test/*.test.ts$ protect-context statusPC status · session default · git 91fad4dΔdiff +88/-8 · 6 files · verdict okloops: nonecontract 12 constraintsconventions: README.mdNudge: verify changes with guard_verify and cite the evidence id before claiming success.$ protect-context verify "npm run check"PC evidence ev-0005 · exit 0 · 1440ms…output digest…Nudge: cite evidence ids, not memory, when claiming success.$ protect-context verify "npm test"PC verify blocked · npm test · 3 prior failuresNudge: change approach or gather new information — do not re-run unchanged.contract:01-obey-instructions: Obey the owner's explicit instructions and stated constraints exactly; when instructions conflict, ask instead of guessing.02-evidence-before-success: Never claim success without citing a fresh verification run (evidence id or pasted output) for the exact claim.…decisions:- Use the atomic-mkdir lease, not fcntl alone; Python must interoperate with the extension. [locking,spec]last verify: ev-0005 exit=0 'npm run check' 1.4sPC Δ+88L/6f 0loop ev5 ctx8%protect-context initprotect-context statusprotect-context verify "pytest -q" [--timeout 180]protect-context evidence [--limit 5]protect-context contract listprotect-context contract add "Keep the public API stable" [--global]protect-context contract remove ID [--global]protect-context note correction "use rg, not grep" --tags searchprotect-context journal [--limit 5] [--type decision]protect-context orientationprotect-context loopsprotect-context diff [--baseline REF]protect-context preflightprotect-context budgetprotect-context mcpbuilt-in defaults < ~/.config/protect-context/ < .agent-guard/ contract.json contract.json config.json config.json$ protect-context contract add "Prefer stdlib over new dependencies" --globaladded 13-prefer-stdlib-over-new-dependencies to personal layer…
$ protect-context contract list # every project sees itpersonal layer: 1 from /home/you/.config/protect-context/contract.json (applies to every project; a project row with the same id or text wins).agent-guard/*!.agent-guard/contract.json.venv/bin/pytest -q # Python suitesnpm run typecheck # extension types (tsc --noEmit)npm test # extension unit tests (node --test).venv/bin/python -m compileall -q src # byte-compile check.venv/bin/python -m build --outdir /tmp/protect-context-dist