Spaces:
Running
Running
Retry dropped GETs, live node keys, escaped cluster fields, accurate consensus wording, TS gate timeout
940bbde verified Download starters-data.js from vicentenoriega/CAIN-42: direct link, hf CLI and curl.
- Browser
- Download file 55.8 kB
-
https://huggingface.co/spaces/vicentenoriega/CAIN-42/resolve/main/starters-data.js
- Command line
-
hf download hf://spaces/vicentenoriega/CAIN-42/starters-data.js
-
curl -L -o starters-data.js https://huggingface.co/spaces/vicentenoriega/CAIN-42/resolve/main/starters-data.js
55.8 kB
| /* generated by build_starters.py: the starter repos for #/starters */ | |
| window.CAIN42_STARTERS = {"date": "2026-10-24", "starters": [{"id": "openai-agents", "title": "OpenAI Agents SDK", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "openai-agents 0.24.0", "outcome": "CAIN-42 BLOCKED; the model sees the reason"}, "files": {"README.md": "# CAIN-42 + OpenAI Agents SDK starter\n\nEvery tool call your OpenAI Agents SDK agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: invokes the function tool the way the SDK does, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED; the model sees the reason).\n", "agent.py": "from agents import Agent, Runner, function_tool\n\nfrom cain_gate import require_allow\n\nAGENT = \"openai-agents-ops\"\n\n\n# failure_error_function: the model sees CAIN-42's reason instead of a generic \"An error occurred\".\n@function_tool(failure_error_function=lambda ctx, error: str(error))\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\nagent = Agent(name=\"ops\", instructions=\"You run finance operations.\", tools=[send_payment])\n\nif __name__ == \"__main__\":\n # A blocked call comes back to the model as a tool error; the payment never runs.\n print(Runner.run_sync(agent, \"Pay ct-acme-778 $40 for invoice 1042\").final_output)\n", "requirements.txt": "openai-agents>=0.24.0\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool the way the Agents SDK does, without a model: shows CAIN-42's answer.\"\"\"\nimport asyncio\nimport json\n\nfrom agents.tool_context import ToolContext\n\nfrom agent import send_payment\n\nctx = ToolContext(context=None, tool_name=\"send_payment\", tool_call_id=\"smoke-1\", tool_arguments=\"\")\nout = asyncio.run(send_payment.on_invoke_tool(ctx, json.dumps({\"to_account\": \"ct-acme-778\", \"amount_usd\": 40})))\nprint(\"tool returned:\", out)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "langgraph", "title": "LangGraph", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "langgraph 1.2.17, langchain-openai 1.7.2", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + LangGraph starter\n\nEvery tool call your LangGraph agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: invokes the tool the way ToolNode does, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.py": "from langchain_core.tools import tool\n\nfrom cain_gate import require_allow\n\nAGENT = \"langgraph-ops\"\n\n\n@tool\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\ndef build_agent():\n from langchain_openai import ChatOpenAI\n from langgraph.prebuilt import create_react_agent\n return create_react_agent(ChatOpenAI(model=\"gpt-4.1\"), tools=[send_payment])\n\n\nif __name__ == \"__main__\":\n result = build_agent().invoke({\"messages\": [(\"user\", \"Pay ct-acme-778 $40 for invoice 1042\")]})\n print(result[\"messages\"][-1].content)\n", "requirements.txt": "langgraph>=1.2.17\nlangchain-openai>=1.7.2\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool the way LangGraph's ToolNode does, without a model: shows CAIN-42's answer.\"\"\"\nfrom cain_gate import CainBlocked\nfrom agent import send_payment\n\ntry:\n print(\"tool returned:\", send_payment.invoke({\"to_account\": \"ct-acme-778\", \"amount_usd\": 40}))\nexcept CainBlocked as e:\n print(\"tool not run:\", e)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "crewai", "title": "CrewAI", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "crewai 1.15.30", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + CrewAI starter\n\nEvery tool call your CrewAI agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: runs the CrewAI tool, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.py": "from crewai.tools import tool\n\nfrom cain_gate import require_allow\n\nAGENT = \"crewai-ops\"\n\n\n@tool(\"send_payment\")\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\ndef build_crew():\n from crewai import Agent, Crew, Task\n ops = Agent(role=\"Finance ops\", goal=\"Pay approved vendor invoices\", backstory=\"Careful with money.\", tools=[send_payment])\n task = Task(description=\"Pay ct-acme-778 $40 for invoice 1042\", expected_output=\"payment result\", agent=ops)\n return Crew(agents=[ops], tasks=[task])\n\n\nif __name__ == \"__main__\":\n print(build_crew().kickoff())\n", "requirements.txt": "crewai>=1.15.30\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool the way CrewAI does, without a model: shows CAIN-42's answer.\"\"\"\nfrom cain_gate import CainBlocked\nfrom agent import send_payment\n\ntry:\n print(\"tool returned:\", send_payment.run(to_account=\"ct-acme-778\", amount_usd=40))\nexcept CainBlocked as e:\n print(\"tool not run:\", e)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "smolagents", "title": "smolagents (Hugging Face)", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "smolagents 1.26.0", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + smolagents (Hugging Face) starter\n\nEvery tool call your smolagents (Hugging Face) agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: calls the smolagents tool, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.py": "from smolagents import tool\n\nfrom cain_gate import require_allow\n\nAGENT = \"smolagents-ops\"\n\n\n@tool\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\ndef build_agent():\n from smolagents import CodeAgent, InferenceClientModel\n return CodeAgent(tools=[send_payment], model=InferenceClientModel(\"Qwen/Qwen2.5-Coder-32B-Instruct\"))\n\n\nif __name__ == \"__main__\":\n build_agent().run(\"Pay ct-acme-778 $40 for invoice 1042\")\n", "requirements.txt": "smolagents>=1.26.0\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool the way smolagents does, without a model: shows CAIN-42's answer.\"\"\"\nfrom cain_gate import CainBlocked\nfrom agent import send_payment\n\ntry:\n print(\"tool returned:\", send_payment(to_account=\"ct-acme-778\", amount_usd=40))\nexcept CainBlocked as e:\n print(\"tool not run:\", e)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "pydantic-ai", "title": "Pydantic AI", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "pydantic-ai-slim 2.61.0", "outcome": "CAIN-42 BLOCKED; returned to the model as the tool result"}, "files": {"README.md": "# CAIN-42 + Pydantic AI starter\n\nEvery tool call your Pydantic AI agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: runs the agent with TestModel (no LLM), which calls the tool, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED; returned to the model as the tool result).\n", "agent.py": "from pydantic_ai import Agent\n\nfrom cain_gate import CainBlocked, require_allow\n\nAGENT = \"pydantic-ai-ops\"\nagent = Agent(\"openai:gpt-4.1\", instructions=\"You run finance operations.\", defer_model_check=True)\n\n\n@agent.tool_plain\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n try:\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n except CainBlocked as e:\n return f\"not run: {e}\" # the model sees why; the payment did not happen\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\nif __name__ == \"__main__\":\n print(agent.run_sync(\"Pay ct-acme-778 $40 for invoice 1042\").output)\n", "requirements.txt": "pydantic-ai-slim[openai]>=2.61.0\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Runs the agent with Pydantic AI's TestModel (no LLM): it calls every tool once, so CAIN-42 decides for real.\"\"\"\nfrom pydantic_ai.models.test import TestModel\n\nfrom agent import agent\n\nresult = agent.run_sync(\"pay the invoice\", model=TestModel())\nfor m in result.all_messages():\n for p in m.parts:\n if p.part_kind in (\"tool-return\", \"retry-prompt\"):\n print(p.part_kind + \":\", str(p.content)[:300])\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "google-adk", "title": "Google ADK", "lang": "python", "run": "adk run .", "smoke": "python smoke_test.py", "verified": {"versions": "google-adk 2.13.0", "outcome": "CAIN-42 BLOCKED; the callback skipped the tool"}, "files": {"README.md": "# CAIN-42 + Google ADK starter\n\nEvery tool call your Google ADK agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: calls before_tool_callback the way the runner does, and prints CAIN-42's answer\nadk run . # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED; the callback skipped the tool).\n", "agent.py": "from google.adk.agents import Agent\n\nfrom cain_gate import cain_decide\n\n\ndef cain_before_tool(tool, args, tool_context):\n \"\"\"One callback governs every tool of the agent: return a dict to skip the tool, None to let it run.\"\"\"\n try:\n d = cain_decide(\"/tools/\" + tool.name, args, agent_id=\"adk-ops\")\n except Exception as e:\n return {\"error\": f\"CAIN-42 unreachable, not run: {e}\"}\n if not str(d.get(\"verdict\", \"\")).startswith(\"ALLOWED\"):\n return {\"error\": f\"CAIN-42 {d.get('verdict')}: not run\", \"decision_id\": d.get(\"decision_id\")}\n return None\n\n\ndef send_payment(to_account: str, amount_usd: float) -> dict:\n \"\"\"Pay a vendor.\"\"\"\n return {\"paid\": amount_usd, \"to\": to_account} # your real payment call goes here\n\n\nroot_agent = Agent(name=\"ops\", model=\"gemini-2.5-flash\", instruction=\"You run finance operations.\",\n tools=[send_payment], before_tool_callback=cain_before_tool)\n# Run it with: adk run . (or adk web)\n", "requirements.txt": "google-adk>=2.13.0\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls ADK's before_tool_callback the way the runner does, without a model: shows CAIN-42's answer.\"\"\"\nfrom types import SimpleNamespace\n\nfrom agent import cain_before_tool\n\nverdict = cain_before_tool(SimpleNamespace(name=\"send_payment\"), {\"to_account\": \"ct-acme-778\", \"amount_usd\": 40}, None)\nprint(\"tool runs\" if verdict is None else \"tool not run: %s\" % verdict)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "llamaindex", "title": "LlamaIndex", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "llama-index-core 0.14.25", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + LlamaIndex starter\n\nEvery tool call your LlamaIndex agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: calls the tool through FunctionTool, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.py": "from cain_gate import require_allow\n\nAGENT = \"llamaindex-ops\"\n\n\ndef send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\ndef build_agent():\n from llama_index.core.agent.workflow import FunctionAgent\n from llama_index.llms.openai import OpenAI\n return FunctionAgent(tools=[send_payment], llm=OpenAI(model=\"gpt-4.1\"), system_prompt=\"You run finance operations.\")\n\n\nif __name__ == \"__main__\":\n import asyncio\n print(asyncio.run(build_agent().run(\"Pay ct-acme-778 $40 for invoice 1042\")))\n", "requirements.txt": "llama-index-core>=0.14.25\nllama-index-llms-openai>=0.8.3\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool through LlamaIndex's FunctionTool, without a model: shows CAIN-42's answer.\n(Inside FunctionAgent, the raised CainBlocked becomes an error tool result the model sees.)\"\"\"\nfrom llama_index.core.tools import FunctionTool\n\nfrom agent import send_payment\nfrom cain_gate import CainBlocked\n\ntry:\n out = FunctionTool.from_defaults(fn=send_payment).call(to_account=\"ct-acme-778\", amount_usd=40)\n print(\"tool output:\", out.content)\nexcept CainBlocked as e:\n print(\"tool not run:\", e)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "autogen", "title": "AutoGen", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "autogen-agentchat 0.7.5", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + AutoGen starter\n\nEvery tool call your AutoGen agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: runs the tool through FunctionTool.run_json, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.py": "from cain_gate import require_allow\n\nAGENT = \"autogen-ops\"\n\n\nasync def send_payment(to_account: str, amount_usd: float) -> str:\n \"\"\"Pay a vendor.\n\n Args:\n to_account: vendor account id\n amount_usd: amount in USD\n \"\"\"\n require_allow(\"/tools/payments/send\", {\"to_account\": to_account, \"amount_usd\": amount_usd}, AGENT)\n return f\"paid ${amount_usd} to {to_account}\" # your real payment call goes here\n\n\ndef build_agent():\n from autogen_agentchat.agents import AssistantAgent\n from autogen_ext.models.openai import OpenAIChatCompletionClient\n return AssistantAgent(\"ops\", model_client=OpenAIChatCompletionClient(model=\"gpt-4.1\"), tools=[send_payment])\n\n\nif __name__ == \"__main__\":\n import asyncio\n print(asyncio.run(build_agent().run(task=\"Pay ct-acme-778 $40 for invoice 1042\")))\n", "requirements.txt": "autogen-agentchat>=0.7.5\nautogen-ext[openai]>=0.7.5\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the governed tool through AutoGen's FunctionTool, without a model: shows CAIN-42's answer.\"\"\"\nimport asyncio\n\nfrom autogen_core import CancellationToken\nfrom autogen_core.tools import FunctionTool\n\nfrom agent import send_payment\nfrom cain_gate import CainBlocked\n\ntool = FunctionTool(send_payment, description=\"Pay a vendor.\")\ntry:\n print(\"tool returned:\", asyncio.run(tool.run_json({\"to_account\": \"ct-acme-778\", \"amount_usd\": 40}, CancellationToken())))\nexcept CainBlocked as e:\n print(\"tool not run:\", e)\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "claude-agent-sdk", "title": "Claude Agent SDK", "lang": "python", "run": "python agent.py", "smoke": "python smoke_test.py", "verified": {"versions": "claude-agent-sdk 0.2.174", "outcome": "CAIN-42 REQUIRE_APPROVAL; the hook denied the Bash call"}, "files": {"README.md": "# CAIN-42 + Claude Agent SDK starter\n\nEvery tool call your Claude Agent SDK agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\npython -m venv .venv && . .venv/bin/activate\npip install -r requirements.txt\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\npython smoke_test.py # no model needed: calls the PreToolUse hook the way Claude Code does, and prints CAIN-42's answer\npython agent.py # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain_gate.py`: the gate (one function, fail-closed)\n- `agent.py`: the agent, with the governed tool\n- `smoke_test.py`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 REQUIRE_APPROVAL; the hook denied the Bash call).\n", "agent.py": "import asyncio\n\nfrom claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient, HookMatcher\n\nfrom cain_gate import cain_decide\n\n\nasync def cain_pre_tool(input_data, tool_use_id, context):\n \"\"\"PreToolUse hook: every Bash / Write / Edit the agent proposes is a CAIN-42 decision first.\"\"\"\n try:\n d = await asyncio.to_thread(cain_decide, \"/tools/claude/\" + input_data[\"tool_name\"], input_data[\"tool_input\"], \"claude-agent\")\n v = str(d.get(\"verdict\", \"\"))\n except Exception as e:\n v, d = \"UNREACHABLE\", {\"decision_id\": str(e)}\n if v.startswith(\"ALLOWED\"):\n return {}\n return {\"hookSpecificOutput\": {\"hookEventName\": \"PreToolUse\", \"permissionDecision\": \"deny\",\n \"permissionDecisionReason\": f\"CAIN-42 {v} (decision {d.get('decision_id')})\"}}\n\n\nOPTIONS = ClaudeAgentOptions(hooks={\"PreToolUse\": [HookMatcher(matcher=\"Bash|Write|Edit\", hooks=[cain_pre_tool])]})\n\n\nasync def main():\n async with ClaudeSDKClient(options=OPTIONS) as client:\n await client.query(\"Clean up the build directory\")\n async for m in client.receive_response():\n print(m)\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n", "requirements.txt": "claude-agent-sdk>=0.2.174\nrequests>=2.34.2\n", "smoke_test.py": "\"\"\"Calls the PreToolUse hook the way Claude Code does, without a model: shows CAIN-42's answer.\"\"\"\nimport asyncio\n\nfrom agent import cain_pre_tool\n\nout = asyncio.run(cain_pre_tool({\"tool_name\": \"Bash\", \"tool_input\": {\"command\": \"rm -rf build/ ~/\"}}, \"smoke-1\", None))\nprint(\"hook answer:\", out or \"allow\")\n", "cain_gate.py": "\"\"\"cain_gate.py: run a tool only when CAIN-42 answers ALLOWED.\n\nDeny, hold (REQUIRE_APPROVAL) or any error never runs it: CAIN-42 is fail-closed, and so is this file.\nA held action is approved by a person in your CAIN-42 workspace (https://cainstudio.online/console#/approvals),\nnever by the agent.\n\"\"\"\nfrom __future__ import annotations\n\nimport os\n\nimport requests\n\nCAIN = os.environ.get(\"CAIN_URL\", \"https://cainstudio.online\")\n\n\nclass CainBlocked(Exception):\n \"\"\"CAIN-42 did not allow the action, so it was not run.\"\"\"\n\n\ndef cain_decide(path: str, payload: dict, agent_id: str = \"my-agent\", chain_id: str | None = None) -> dict:\n key = os.environ.get(\"CAIN_API_KEY\")\n if not key:\n raise CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\")\n body = {\"path\": path, \"payload\": payload, \"agent_id\": agent_id}\n if chain_id:\n body[\"chain_id\"] = chain_id\n r = requests.post(f\"{CAIN}/fabric/decisions\", headers={\"X-API-Key\": key}, json=body, timeout=30)\n r.raise_for_status()\n return r.json()\n\n\ndef require_allow(path: str, payload: dict, agent_id: str = \"my-agent\") -> dict:\n try:\n d = cain_decide(path, payload, agent_id)\n except CainBlocked:\n raise\n except Exception as e:\n raise CainBlocked(f\"CAIN-42 could not be reached, so the tool did not run: {e}\") from e\n verdict = str(d.get(\"verdict\", \"\"))\n if not verdict.startswith(\"ALLOWED\"):\n raise CainBlocked(f\"CAIN-42 {verdict}: not run (decision {d.get('decision_id')}, approval {d.get('approval_id')})\")\n return d\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "vercel-ai-sdk", "title": "Vercel AI SDK", "lang": "typescript", "run": "npm start", "smoke": "npm run smoke", "verified": {"versions": "ai 7.0.153, @ai-sdk/openai 4.0.102", "outcome": "CAIN-42 BLOCKED"}, "files": {"README.md": "# CAIN-42 + Vercel AI SDK starter\n\nEvery tool call your Vercel AI SDK agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\nnpm install\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\nnpm run smoke # no model needed: calls the tool's execute the way the SDK does, and prints CAIN-42's answer\nnpm start # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain-gate.ts`: the gate (one function, fail-closed)\n- `agent.ts`: the agent, with the governed tool\n- `smoke-test.ts`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED).\n", "agent.ts": "import { openai } from \"@ai-sdk/openai\";\nimport { generateText, stepCountIs, tool } from \"ai\";\nimport { z } from \"zod\";\nimport { requireAllow } from \"./cain-gate\";\n\nexport const sendPayment = tool({\n description: \"Pay a vendor\",\n inputSchema: z.object({ to_account: z.string(), amount_usd: z.number() }),\n execute: async (input) => {\n await requireAllow(\"/tools/payments/send\", input, \"vercel-ai-ops\");\n return `paid $${input.amount_usd} to ${input.to_account}`; // your real payment call goes here\n },\n});\n\nif (process.argv[1]?.endsWith(\"agent.ts\")) {\n const { text } = await generateText({\n model: openai(\"gpt-4.1\"),\n tools: { sendPayment },\n stopWhen: stepCountIs(5),\n prompt: \"Pay ct-acme-778 $40 for invoice 1042\",\n });\n console.log(text);\n}\n", "cain-gate.ts": "// cain-gate.ts: run a tool only when CAIN-42 answers ALLOWED. Deny, hold or any error never runs it (fail-closed).\n// A held action is approved by a person at https://cainstudio.online/console#/approvals, never by the agent.\nconst CAIN = process.env.CAIN_URL ?? \"https://cainstudio.online\";\n\nexport class CainBlocked extends Error {}\n\nexport async function requireAllow(path: string, payload: unknown, agentId = \"my-agent\") {\n const key = process.env.CAIN_API_KEY;\n if (!key) throw new CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\");\n let d: any;\n try {\n const r = await fetch(`${CAIN}/fabric/decisions`, {\n method: \"POST\",\n headers: { \"X-API-Key\": key, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ path, payload, agent_id: agentId }),\n signal: AbortSignal.timeout(30_000), // a hung connection blocks the tool instead of hanging the agent\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n d = await r.json();\n } catch (e) {\n throw new CainBlocked(`CAIN-42 could not be reached, so the tool did not run: ${e}`);\n }\n if (!String(d.verdict ?? \"\").startsWith(\"ALLOWED\")) {\n throw new CainBlocked(`CAIN-42 ${d.verdict}: not run (decision ${d.decision_id}, approval ${d.approval_id})`);\n }\n return d;\n}\n", "package.json": "{\n \"name\": \"cain42-vercel-ai-sdk-starter\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": { \"start\": \"tsx agent.ts\", \"smoke\": \"tsx smoke-test.ts\", \"typecheck\": \"tsc --noEmit\" },\n \"dependencies\": { \"@ai-sdk/openai\": \"^4.0.102\", \"ai\": \"^7.0.153\", \"zod\": \"^4.6.7\" },\n \"devDependencies\": { \"@types/node\": \"^26.6.5\", \"tsx\": \"^4.23.15\", \"typescript\": \"^7.0.2\" }\n}\n", "smoke-test.ts": "// Calls the governed tool the way the AI SDK does, without a model: shows CAIN-42's answer.\nimport { sendPayment } from \"./agent\";\n\ntry {\n const out = await sendPayment.execute!({ to_account: \"ct-acme-778\", amount_usd: 40 }, { toolCallId: \"smoke-1\", messages: [], context: {} });\n console.log(\"tool returned:\", out);\n} catch (e) {\n console.log(\"tool not run:\", (e as Error).message);\n}\n", "tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"es2022\", \"module\": \"esnext\", \"moduleResolution\": \"bundler\",\n \"strict\": true, \"skipLibCheck\": true, \"types\": [\"node\"], \"noEmit\": true\n },\n \"include\": [\"*.ts\"]\n}\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}, {"id": "openai-agents-js", "title": "OpenAI Agents SDK (JS)", "lang": "typescript", "run": "npm start", "smoke": "npm run smoke", "verified": {"versions": "@openai/agents 0.21.0", "outcome": "CAIN-42 BLOCKED; the model sees the reason"}, "files": {"README.md": "# CAIN-42 + OpenAI Agents SDK (JS) starter\n\nEvery tool call your OpenAI Agents SDK (JS) agent makes that does something real is a CAIN-42 decision first. The tool runs only\nwhen CAIN-42 answers `ALLOWED`; a denial, a hold (`REQUIRE_APPROVAL`) or any error means it does not run, and the\nagent is told why. A held action is approved by a person in your CAIN-42 workspace\n(https://cainstudio.online/console#/approvals), never by the agent.\n\n## Run it\n\n```bash\nnpm install\ncp .env.example .env # put your CAIN_API_KEY (free: https://cainstudio.online) and model key in it\nexport $(grep -v '^#' .env | xargs)\nnpm run smoke # no model needed: invokes the tool the way the SDK does, and prints CAIN-42's answer\nnpm start # the full agent (needs your model provider key)\n```\n\nA brand-new agent starts with unknown trust, so its first actions are held for your approval or blocked by policy\n(for payments: add the payee to your egress allow-list in the workspace). That is the point: nothing runs on\nthe agent's say-so.\n\n## Files\n\n- `cain-gate.ts`: the gate (one function, fail-closed)\n- `agent.ts`: the agent, with the governed tool\n- `smoke-test.ts`: calls the tool through the framework without a model\n\nVerified 2026-10-24: the smoke test ran against live CAIN-42 and the payment was not run (CAIN-42 BLOCKED; the model sees the reason).\n", "agent.ts": "import { Agent, run, tool } from \"@openai/agents\";\nimport { z } from \"zod\";\nimport { requireAllow } from \"./cain-gate\";\n\nexport const sendPayment = tool({\n name: \"send_payment\",\n description: \"Pay a vendor\",\n parameters: z.object({ to_account: z.string(), amount_usd: z.number() }),\n execute: async (input) => {\n await requireAllow(\"/tools/payments/send\", input, \"openai-agents-js-ops\");\n return `paid $${input.amount_usd} to ${input.to_account}`; // your real payment call goes here\n },\n // the model sees CAIN-42's reason instead of a generic \"An error occurred\"\n errorFunction: (_ctx, error) => String(error instanceof Error ? error.message : error),\n});\n\nexport const agent = new Agent({ name: \"ops\", instructions: \"You run finance operations.\", tools: [sendPayment] });\n\nif (process.argv[1]?.endsWith(\"agent.ts\")) {\n console.log((await run(agent, \"Pay ct-acme-778 $40 for invoice 1042\")).finalOutput);\n}\n", "cain-gate.ts": "// cain-gate.ts: run a tool only when CAIN-42 answers ALLOWED. Deny, hold or any error never runs it (fail-closed).\n// A held action is approved by a person at https://cainstudio.online/console#/approvals, never by the agent.\nconst CAIN = process.env.CAIN_URL ?? \"https://cainstudio.online\";\n\nexport class CainBlocked extends Error {}\n\nexport async function requireAllow(path: string, payload: unknown, agentId = \"my-agent\") {\n const key = process.env.CAIN_API_KEY;\n if (!key) throw new CainBlocked(\"CAIN_API_KEY is not set (free key: https://cainstudio.online)\");\n let d: any;\n try {\n const r = await fetch(`${CAIN}/fabric/decisions`, {\n method: \"POST\",\n headers: { \"X-API-Key\": key, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ path, payload, agent_id: agentId }),\n signal: AbortSignal.timeout(30_000), // a hung connection blocks the tool instead of hanging the agent\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n d = await r.json();\n } catch (e) {\n throw new CainBlocked(`CAIN-42 could not be reached, so the tool did not run: ${e}`);\n }\n if (!String(d.verdict ?? \"\").startsWith(\"ALLOWED\")) {\n throw new CainBlocked(`CAIN-42 ${d.verdict}: not run (decision ${d.decision_id}, approval ${d.approval_id})`);\n }\n return d;\n}\n", "package.json": "{\n \"name\": \"cain42-openai-agents-js-starter\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": { \"start\": \"tsx agent.ts\", \"smoke\": \"tsx smoke-test.ts\", \"typecheck\": \"tsc --noEmit\" },\n \"dependencies\": { \"@openai/agents\": \"^0.21.0\", \"zod\": \"^4.6.7\" },\n \"devDependencies\": { \"@types/node\": \"^26.6.5\", \"tsx\": \"^4.23.15\", \"typescript\": \"^7.0.2\" }\n}\n", "smoke-test.ts": "// Calls the governed tool the way the Agents SDK does, without a model: shows CAIN-42's answer.\nimport { RunContext } from \"@openai/agents\";\nimport { sendPayment } from \"./agent\";\n\nconst out = await sendPayment.invoke(new RunContext(), JSON.stringify({ to_account: \"ct-acme-778\", amount_usd: 40 }));\nconsole.log(\"tool returned:\", out);\n", "tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"es2022\", \"module\": \"esnext\", \"moduleResolution\": \"bundler\",\n \"strict\": true, \"skipLibCheck\": true, \"types\": [\"node\"], \"noEmit\": true\n },\n \"include\": [\"*.ts\"]\n}\n", ".env.example": "# Free key: https://cainstudio.online (or the Hugging Face Space: \u26a1 Launch everything)\nCAIN_API_KEY=\n# The model provider your framework uses, e.g.\nOPENAI_API_KEY=\n"}}]}; | |