{"id":50611399,"url":"https://github.com/rusty4444/agentcontract","last_synced_at":"2026-06-06T04:01:43.613Z","repository":{"id":359932185,"uuid":"1247414422","full_name":"rusty4444/agentcontract","owner":"rusty4444","description":"JSON-native agent capability contracts and pre-execution gate","archived":false,"fork":false,"pushed_at":"2026-05-24T06:16:32.000Z","size":239,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-24T08:16:36.550Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rusty4444.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-23T09:30:07.000Z","updated_at":"2026-05-24T06:41:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rusty4444/agentcontract","commit_stats":null,"previous_names":["rusty4444/agentcontract"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/rusty4444/agentcontract","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rusty4444%2Fagentcontract","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rusty4444%2Fagentcontract/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rusty4444%2Fagentcontract/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rusty4444%2Fagentcontract/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rusty4444","download_url":"https://codeload.github.com/rusty4444/agentcontract/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rusty4444%2Fagentcontract/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33968711,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-06T02:00:07.033Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-06T04:01:42.081Z","updated_at":"2026-06-06T04:01:43.605Z","avatar_url":"https://github.com/rusty4444.png","language":"Python","funding_links":["https://buymeacoffee.com/rusty4"],"categories":[],"sub_categories":[],"readme":"# agentcontract\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://buymeacoffee.com/rusty4\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" alt=\"Buy Me A Coffee\" height=\"50\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\n\n**Agent contracts that don't lie.**\n\nA JSON-native, framework-agnostic capability contract for LLM-powered agents. Declare\nwhat an agent *can* and *cannot* do — which tools, which files, which network calls,\nwhich cost caps — and get a single pre-execution gate that any runtime (Hermes, LangChain,\nraw OpenAI function-calling) calls before every action.\n\n```\nagc init default-contract        # scaffold a contract\nagc validate contracts/default.json\nagc gate contracts/default.json --action '{\"tool_name\":\"shell\",\"estimated_cost_cents\":0}'\nagc gui                          # local browser GUI for setup + dry-runs\n```\n\n![agentcontract local setup GUI](docs/assets/agentcontract-gui.png)\n\nPhilosophy: contracts live in version control, can be audited in CI, and work over JavaScript and every LLM framework today.\n\n---\n\n## Quick-start (5 minutes)\n\n### 1 — Install\n\n```bash\npip install agentcontract\n```\n\n### 2 — Scaffold a contract\n\n```bash\n# Interactive wizard\nagc init my-agent --hermes\n\n# Result: ─ ~/.hermes/contracts/my-agent.json ─\n# {\n#   \"version\": \"1.0\",\n#   \"agent_id\": \"my-agent\",\n#   \"description\": \"...\",\n#   \"allow_tools\": [\"shell\", \"read_file\", \"write_file\"],\n#   \"deny_tools\": [\"execute_code\"],\n#   \"allow_paths\": [\"~/dev/\"],\n#   \"deny_paths\": [\"~/.ssh/\", \"~/.hermes/.env\"],\n#   \"allow_network\": false,\n#   \"max_cost_per_action_cents\": 100,\n#   \"require_approval\": [],\n#   \"max_iterations\": 90\n# }\n```\n\n### 3 — Set up the enforcement interface\n\n```python\n# interactive-cli\nfrom agentcontract import ContractEnforcer, ContractViolation, Contract\n\n# load from disk, path, or raw JSON string\nenforcer = ContractEnforcer(\n    \"~/.hermes/contracts/my-agent.json\",   # ← accepts str\n    # OR: Contract.parse_file(...),\n    # OR: '{\"agent_id\":\"my-agent\", ...}',    # raw JSON string\n)\n\n# after meta[let's define the actual API call first], get to the distinction between `check()` and `enforce()`\ntry:\n    result = enforcer.enforce(\"shell\", estimates_cost_cents=0)\n    # ALLOW → result returned, call proceeds\nexcept ContractViolation as exc:\n    # BLOCK / REQUIRE_APPROVAL → agent stopped here\n    log(exc)                               # or return, or prompt user\n    ...\n\n# return-value pattern (no exception)\nresult = enforcer.check(\"shell\", estimated_cost_cents=0)\nif result.decision == \"ALLOW\":\n    ... # proceed\nelse:\n    reason = \"; \".join(v.message for v in result.violations)\n    return f\"Blocked: {reason}\"\n```\n\n### 4 — Optional: enforce every tool call in Hermes\n\n```python\n# in your Hermes agent loop or HermesMCPContext pre-call hook:\nfrom agentcontract import ContractEnforcer\n\nenforcer = ContractEnforcer(active_contract_path)\n\ndef before_tool_call(tool_name, **kwargs):\n    try:\n        enforcer.enforce(tool_name, **kwargs)\n    except ContractViolation as exc:\n        # enforce() already raised → return/abort are your call\n        return str(exc)\n    return OK\n```\n\n---\n\n## Contract schema\n\n```json\n{\n  \"version\": \"1.0\",\n  \"agent_id\": \"my-agent\",\n  \"description\": \"Development sandbox contract\",\n  \"allow_tools\": [\"shell\", \"read_file\", \"write_file\"],\n  \"deny_tools\": [\"execute_code\"],\n  \"allow_paths\": [\"~/dev/\"],\n  \"deny_paths\": [\"~/.ssh/\", \"~/.hermes/.env\"],\n  \"allow_network\": false,\n  \"max_cost_per_action_cents\": 100,\n  \"require_approval\": [\"execute_code\"],\n  \"max_iterations\": 90\n}\n```\n\n### Fields\n\n| Field | Type | Default | Meaning |\n|---|---|---|---|\n| `version` | string | `\"1.0\"` | Schema version |\n| `agent_id` | string | *required* | Stable identity for this contract |\n| `description` | string | `\"\"` | Human-readable purpose |\n| `allow_tools` | `[string] | null` | `null` = all tools allowed; list = explicit allowlist |\n| `deny_tools` | `[string]` | `[]` | Always blocked, even if in `allow_tools` |\n| `allow_paths` | `[string] | null` | `null` = all paths; list = filesystem glob allowlist |\n| `deny_paths` | `[string]` | `[]` | Always denied filesystem paths (globs supported) |\n| `allow_network` | bool | `true` | Whether this agent may make outbound network calls |\n| `max_cost_per_action_cents` | `int | null` | `null` | Cap per-action cost in USD cents |\n| `require_approval` | `[string]` | `[]` | Tool names that need phase gate / sign-off |\n| `max_iterations` | `int | null` | `null` | Cap recursive loop depth |\n\n---\n\n## Enforcement interface\n\n### `ContractEnforcer`\n\nLoads a contract from disk, path, or JSON string and keeps it in interpreter memory.\n\n```python\nfrom agentcontract import ContractEnforcer, Contract, AuditTrail\n\n# Three constructor forms:\nenforcer1 = ContractEnforcer(\"contracts/default.json\")                 # path / URL\nenforcer2 = ContractEnforcer('{\"agent_id\":\"dev\", ...}')               # raw JSON string\nenforcer3 = ContractEnforcer(Contract(agent_id=\"dev\", ...))           # Pydantic object\n```\n\n**`check(tool_name, ...) -\u003e GateResult`**  \nReturn-value pattern. Non-throwing.\n\n```python\nresult = enforcer.check(\"shell\", estimated_cost_cents=0)\n# GateResult.decision  → GateDecision.ALLOW | BLOCK | REQUIRE_APPROVAL\n# result.violations    → list[GateViolation]\n# result.metadata      → dict[str, Any]\n```\n\n**`enforce(tool_name, ...) -\u003e GateResult`**  \nException-based pattern. BLOCK / REQUIRE_APPROVAL raise `ContractViolation`.\n\n```python\ntry:\n    enforcer.enforce(\"executability_code\", arguments={\"code\": \"x=1\"})\n    # ALLOW → returns GateResult, call proceeds\nexcept ContractViolation as exc:\n    # BLOCK or REQUIRE_APPROVAL → call is aborted here\n    reason = str(exc)\n    log(reason)\n```\n\n**`check_hermes(mcp_ctx: HermesMCPContext) -\u003e GateResult`**  \nHermes MCP pre-call adapter:\n\n```python\nfrom agentcontract.sdk import HermesMCPContext, HermesMCPFormatter\nfrom agentcontract.core import ActionContext\n\nctx = HermesMCPContext(\n    context=ActionContext(\n        tool_name=\"http_get\",\n        requires_network=True,\n        paths_touched=[],\n        arguments={},\n        estimated_cost_cents=0,\n    )\n)\nresult = enforcer.check_hermes(ctx)\n# Non-ALLOW decisions land in both ring buffer AND persistent JSONL log\n```\n\n**`replace_contract(new, source=None)`**  \nAtomic hot-swap at runtime. Audit trail flushes automatically. Returns `True` on success.\nRejects bad JSON / invalid schema on stderr and keeps the old contract intact.\n\n---\n\n## Audit and dry-runs\n\n```python\n# tail the in-memory ring buffer\ntail = enforcer.tail_audit(limit=10)\n# [{\"tool_name\": \"shell\", \"decision\": \"ALLOW\", ...}, ...]\n\n# capacity is configurable; default 128\nenforcer = ContractEnforcer(path, audit_capacity=10)\nlen(enforcer.audit_tail())  # ≤ 10 at any time\n```\n\n### Hermes MCP formatting\n\n```python\nfrom agentcontract.sdk import HermesMCPFormatter\n\nfmtr = HermesMCPFormatter(enforcer.contract)\nrejected_record = fmtr.format_gate_rejection(result)\nif mussand_deletion_flag called):\n    result = enforcer.enforce(\"docker_build\", estimated_cost_cents=5)   # $ 0$.05\n    result = enforcer.enforce(\"embed_memory\")                             # local/read-only\n```\n\n---\n\n## CLI reference\n\n```\nagc init \u003cagent-id\u003e [--hermes]       # scaffold a contract\nagc validate \u003ccontract.json\u003e          # schema + cross-field rules\nagc gate \u003ccontract.json\u003e --action \u003cjson-string\u003e   # dry-run one tool call\nagc gui                               # localhost browser GUI\nagc schema                            # print JSON schema to stdout\nagc schema \u003e ~/.vscode/schemas/agentcontract.json   # tooling integration\n```\n\n### GUI — contract editor \u0026 dry-run studio\n\n`agc gui` starts a localhost-only setup app at `http://127.0.0.1:8765/`.\n\n**Panel 1 — contract editor**\n- JSON editor with a production-safe starter template pre-loaded\n- `Save \u0026 exit` → writes `~/.hermes/contracts/\u003cagent-id\u003e.json`\n\n**Panel 2 — validate**\n- One-click schema validation: shows Pydantic error messages in real time\n- Covers all cross-field rules (`allow_tools` vs `deny_tools` overlap, etc.)\n\n**Panel 3 — dry-run**\n- Paste an `ActionContext` JSON payload\n- `Run gate` → shows `ALLOW`, `BLOCK`, or `REQUIRE_APPROVAL` with full `GateResult`\n- Great for previewing a new contract's behaviour before saving\n\n---\n\n## Engine (Python SDK)\n\n```python\nfrom agentcontract.core import Contract, ActionContext, GateDecision, gate\n\ncontract = Contract.model_validate_json(Path(\".agentcontract.json\").read_text())\nresult  = gate(\n    contract,\n    ActionContext(\n        tool_name=\"shell\",\n        arguments={\"command\": \"rm -rf /\"},\n        estimated_cost_cents=0,\n        paths_touched=[\"/\"],\n        requires_network=False,\n    ),\n)\n\n# result.decision == GateDecision.BLOCK\n# result.violations → list[GateViolation]\n```\n\n---\n\n## Schema (open)\n\n`agent_to_json_schema()` returns the JSON Schema draft 2020-12 object. Wire it into VS Code, your form builder, or your CI linter:\n\n```bash\nagc schema | jq '.' \u003e ~/.vscode/schemas/agentcontract.json\n```\n\n---\n\n## CI usage\n\n```yaml\n# .github/workflows/contract-check.yml\n- name: Validate agent contracts\n  run: |\n    install agentcontract\n    agc validate contracts/prod-agent.json\n    agc gate contracts/prod-agent.json \\\n        --action '{\"tool_name\":\"execute_code\",\"arguments\":{}}'\n```\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frusty4444%2Fagentcontract","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frusty4444%2Fagentcontract","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frusty4444%2Fagentcontract/lists"}