{"_id":"@avee1234/constraintguard","_rev":"3-fe852fa14caf2ddd8ed7d1e5b95ed336","name":"@avee1234/constraintguard","dist-tags":{"latest":"0.3.1"},"versions":{"0.2.0":{"name":"@avee1234/constraintguard","version":"0.2.0","license":"MIT","_id":"@avee1234/constraintguard@0.2.0","maintainers":[{"name":"avee1234","email":"das.abhijit34@gmail.com"}],"bin":{"cg":"bin/cg.js"},"dist":{"shasum":"cb27445fb3a7c9fd839d4c32692bbb89d4b17c16","tarball":"https://registry.npmjs.org/@avee1234/constraintguard/-/constraintguard-0.2.0.tgz","fileCount":16,"integrity":"sha512-Wvz3+kyFRz+WEHGhSToIM/9pF18WPXdsoy8CnUQo4oxrdsXdJYqTWfI32zolLD/BgWbnqMvazJCMcVn+FmpOnw==","signatures":[{"sig":"MEQCIHeIOPtmeFt97ex/o+SzN00ZbJyKFRLEXu02oWsAecX/AiA1Fwho2i3lAG3pp9DaZQtPuxHFRakv9lB3T22JRr8Ykg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":59321},"main":"src/index.js","type":"module","engines":{"node":">=18"},"gitHead":"6487c846dbaf426fbddde302043a884da560d511","scripts":{"test":"node --test","bench":"node bench/bench.js"},"_npmUser":{"name":"avee1234","email":"das.abhijit34@gmail.com"},"_npmVersion":"10.9.4","description":"Keep an AI agent's declared constraints alive across context compaction. Zero dependencies.","directories":{},"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/constraintguard_0.2.0_1783397021624_0.01124140010271324","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@avee1234/constraintguard","version":"0.3.0","keywords":["ai","agent","llm","constraints","context-compaction","context-window","context","token-budget","cost","guardrails","governance","claude-code","codex","cursor","antigravity","conformance","constraintrot","opentelemetry","zero-dependency"],"license":"MIT","_id":"@avee1234/constraintguard@0.3.0","maintainers":[{"name":"avee1234","email":"das.abhijit34@gmail.com"}],"homepage":"https://constraintguard.vercel.app","bugs":{"url":"https://github.com/abhid1234/constraintguard/issues"},"bin":{"cg":"bin/cg.js"},"dist":{"shasum":"94e5811d77114faed3ae30092ef27c090eded606","tarball":"https://registry.npmjs.org/@avee1234/constraintguard/-/constraintguard-0.3.0.tgz","fileCount":18,"integrity":"sha512-/y+HfdqxWXWl79lIQWt6xMHJ2EvC2paEZSds4tzs8LtnyGh/m8rFKYLAxxh1qsnaZBYwSFh8OjQsDE3YE6y3Aw==","signatures":[{"sig":"MEUCIQCCeUvqmXtLpA9h0O/GtGgMASF92fKRVKkluqRzuzflAgIgMLn25vBGV4HdeveRconE1OKlokh6nDaAIJ0wHpkS+rw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":79797},"main":"src/index.js","type":"module","engines":{"node":">=18"},"gitHead":"f8db753f8c1f3651b71730ce65c1c162a55809d0","scripts":{"test":"node --test","bench":"node bench/bench.js"},"_npmUser":{"name":"avee1234","email":"das.abhijit34@gmail.com"},"repository":{"url":"git+https://github.com/abhid1234/constraintguard.git","type":"git"},"_npmVersion":"10.9.8","description":"Keep an AI agent's declared constraints alive across context compaction. Zero dependencies.","directories":{},"_nodeVersion":"22.22.3","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/constraintguard_0.3.0_1784073108102_0.8677284097777003","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@avee1234/constraintguard","version":"0.3.1","description":"Keep an AI agent's declared constraints alive across context compaction. Zero dependencies.","type":"module","bin":{"cg":"bin/cg.js"},"scripts":{"test":"node --test","bench":"node bench/bench.js"},"license":"MIT","main":"src/index.js","engines":{"node":">=18"},"homepage":"https://constraintguard.vercel.app","repository":{"type":"git","url":"git+https://github.com/abhid1234/constraintguard.git"},"bugs":{"url":"https://github.com/abhid1234/constraintguard/issues"},"keywords":["ai","agent","llm","constraints","context-compaction","context-window","context","token-budget","cost","guardrails","governance","claude-code","codex","cursor","antigravity","conformance","constraintrot","opentelemetry","zero-dependency"],"_id":"@avee1234/constraintguard@0.3.1","gitHead":"44152e6c0c0d82cdf5d7d437588d5300d4460324","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-xIVNWY8V2my5nb2Sea2mXWFJ07xTnVXp40G1P1DXk7P3Z9JvkK83cU4U3A4JxIgeQOJiDEAlcfYkMlfGNFO0rQ==","shasum":"09873b8d4c8f793ac581ae836dc3dcbc69f7feac","tarball":"https://registry.npmjs.org/@avee1234/constraintguard/-/constraintguard-0.3.1.tgz","fileCount":18,"unpackedSize":79797,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCePCLzLEYdQy7qtvmEmk0KCFlczsAS52zgT/b6KZPB6wIhAJ/jec0fD5LGVFZjzAp3RhXt1YQsuBvYZ8CnNpzqAO1f"}]},"_npmUser":{"name":"avee1234","email":"das.abhijit34@gmail.com"},"directories":{},"maintainers":[{"name":"avee1234","email":"das.abhijit34@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/constraintguard_0.3.1_1784080825786_0.7454864803897441"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-07T04:03:41.464Z","modified":"2026-07-15T02:00:26.052Z","0.2.0":"2026-07-07T04:03:41.784Z","0.3.0":"2026-07-14T23:51:48.254Z","0.3.1":"2026-07-15T02:00:25.956Z"},"bugs":{"url":"https://github.com/abhid1234/constraintguard/issues"},"license":"MIT","homepage":"https://constraintguard.vercel.app","keywords":["ai","agent","llm","constraints","context-compaction","context-window","context","token-budget","cost","guardrails","governance","claude-code","codex","cursor","antigravity","conformance","constraintrot","opentelemetry","zero-dependency"],"repository":{"type":"git","url":"git+https://github.com/abhid1234/constraintguard.git"},"description":"Keep an AI agent's declared constraints alive across context compaction. Zero dependencies.","maintainers":[{"name":"avee1234","email":"das.abhijit34@gmail.com"}],"readme":"# ConstraintGuard\n\n[![CI](https://github.com/abhid1234/constraintguard/actions/workflows/ci.yml/badge.svg)](https://github.com/abhid1234/constraintguard/actions/workflows/ci.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) ![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)\n\n**[▶ Try the live playground →](https://constraintguard.vercel.app)** · demos run the real library in your browser.\n\n![constraintguard demo](site/constraintguard-demo.gif)\n\n\n**Keep an AI agent's declared constraints alive across context compaction.** Zero dependencies.\n\nWhen a long-running agent summarizes its history to fit the context window, its safety and policy constraints get silently dropped — measured violation rates jump from 0% to as high as 59% ([arXiv:2606.22528](https://arxiv.org/abs/2606.22528), *Governance Decay*; \"ConstraintRot\" is its benchmark). ConstraintGuard extracts declared constraints *before* compaction and re-pins them *after*, and ships a conformance suite so you can measure the risk on any harness.\n\n## Install\n\n```bash\nnpm i -g @avee1234/constraintguard   # or: npx @avee1234/constraintguard\n```\n\n## Commands\n\n```bash\ncg validate constraints.json   # is this a well-formed constraint set?\ncg extract session.md          # pull declared constraints out of a context\ncg extract --harness claude-code session.jsonl  # …or straight from a Claude Code transcript\ncg extract --harness codex rollout.jsonl        # …or from an OpenAI Codex CLI rollout\ncg extract --harness antigravity AGENTS.md      # …or a Google Antigravity rules file\ncg pin constraints.json ctx.md # re-inject constraints into a (compacted) context\ncg conformance orig.md new.md  # score how well constraints survive compaction\ncg budget reads.json --max-tokens 8000  # audit what was loaded vs. what was needed\ncg otel constraints ctx.md     # map constraints to OpenTelemetry span attributes\n```\n\nThe `otel` command emits a flat attribute object under the stable `constraintguard.*`\nnamespace (no OpenTelemetry SDK) — attach it to any span so \"which constraints were\ndeclared / dropped\" shows up in your agent's trace.\n\n## Context budget — keep the context lean\n\nConstraintGuard owns **context integrity**, and integrity has two halves: keep the\n*rules* alive across compaction (above), and keep the *context* itself lean. An\nagent that loads a whole repo into context to answer one question is burning tokens\non ballast — the \"99% fewer tokens\" waste everyone is chasing. `cg budget` audits\nit: given a **read-log** of what the agent loaded, it reports what was actually used\nvs. dropped-in-and-never-touched, and whether any budget cap was blown.\n\nTwo plain, open JSON shapes mirror the constraint set — no service, no tokenizer:\n\n```jsonc\n// budget (all caps optional)\n{ \"max_tokens\": 8000, \"max_files\": 20, \"max_tokens_per_file\": 2000 }\n\n// read-log — one entry per thing the agent loaded.\n// `tokens` is the caller's own count; `used` marks whether it was actually needed.\n[\n  { \"path\": \"src/auth.js\",  \"tokens\": 400, \"used\": true  },\n  { \"path\": \"vendor/big.js\", \"tokens\": 6000, \"used\": false },\n  { \"path\": \"README.md\",     \"tokens\": 200 }\n]\n```\n\n```bash\ncg budget reads.json --max-tokens 8000 --max-files 20   # human report; exit 2 if over budget\ncg budget reads.json --json                             # machine-readable report\ncat reads.json | cg budget -                            # read-log from stdin\ncg otel budget reads.json --max-tokens 8000             # …as OpenTelemetry span attributes\n```\n\n`budgetReport(reads, budget?)` returns `{ total_tokens, file_count, over_budget,\noverages, unused, unused_tokens, utilization, waste_ratio }`. The heart of it is\n`unused` / `waste_ratio` — the loaded-but-never-used context you can cut — and\n`overages`, one entry per blown cap so `cg budget` can gate CI (it exits `2` when\nover budget, just like `cg conformance --threshold`). ConstraintGuard does not\ntokenize: token counts come from the caller (a real model count is best; the\ndocumented `estimateTokens(text)` ≈ chars/4 heuristic is a fallback, not a\ntokenizer). A read is only ever counted as waste when it is explicitly\n`used: false`, so the audit never over-accuses.\n\n## Dogfood: auto-pin across Claude Code compaction\n\nConstraintGuard exists for one failure — an agent's declared rules silently\nvanish when a long session compacts its context. The `cg hook` commands close\nthat loop **automatically inside Claude Code**: on compaction they extract the\nsession's declared constraints; immediately after, they re-inject them into the\nfreshly compacted context. No glue script, no file to maintain — just two hooks.\n\nDrop this into `~/.claude/settings.json` (all projects) or `.claude/settings.json`\n(one project) — it is [`hooks/claude-code/settings.json`](hooks/claude-code/settings.json)\nverbatim:\n\n```json\n{\n  \"hooks\": {\n    \"PreCompact\": [\n      { \"hooks\": [ { \"type\": \"command\", \"command\": \"cg hook pre-compact\" } ] }\n    ],\n    \"SessionStart\": [\n      {\n        \"matcher\": \"compact\",\n        \"hooks\": [ { \"type\": \"command\", \"command\": \"cg hook session-start\" } ]\n      }\n    ]\n  }\n}\n```\n\n- **`cg hook pre-compact`** (event `PreCompact`, no matcher) reads the hook JSON\n  on stdin, extracts the session's declared constraints from its transcript, and\n  caches them keyed by `session_id`. Repeated compactions **union** into the\n  cache, so a constraint declared early survives every later compaction.\n- **`cg hook session-start`** (event `SessionStart`, matcher `compact`) reads the\n  cached set and returns it as `hookSpecificOutput.additionalContext` — a single\n  `cg pin`-rendered ```` ```constraints ```` block — which Claude Code appends to\n  the compacted context. It prints nothing when the session declared no\n  constraints.\n\nBoth hooks are fail-safe: any error (unreadable transcript, malformed JSON,\nmissing cache) exits `0` with no output, so a hook can never block compaction or\na session start. The cache lives under the OS temp dir — nothing is written to\nyour repo.\n\nIf `cg` is not on `PATH` in the hook environment, use\n`npx --no-install constraintguard hook …` or an absolute path in place of `cg`.\n\n**Verify it:** declare a constraint in a `constraints` fence, force a compaction\nwith `/compact`, and confirm the block reappears in the next turn.\n\n> Note: `SessionStart`'s `source` string after compaction (and whether it fires\n> after *automatic* window-full compaction, not just `/compact`) depends on your\n> Claude Code version. If re-injection doesn't fire, adjust the `matcher` — no\n> code change is needed.\n\nOpen format, dependency-free, cross-harness. Run the tests: `npm test`.\n\nReproduce the ConstraintRot drop on committed sample sessions: `npm run bench`. It\nscores each session's original context against its compacted version with\n`cg conformance` and prints a retention table plus the aggregate drop.\n\n> Built by the Foundry software factory. Issues here are triaged, specced, implemented, reviewed and shipped by agents, with human approval at the gates.\n","readmeFilename":"README.md"}