{"_id":"@ch4r10teer41/inai-cli","name":"@ch4r10teer41/inai-cli","dist-tags":{"latest":"0.3.1"},"versions":{"0.3.1":{"name":"@ch4r10teer41/inai-cli","version":"0.3.1","description":"Scaffold P2P-discoverable, DID-native AI agents that interoperate across any framework","bin":{"inai":"bin/inai.js"},"main":"./dist/cli.js","scripts":{"build":"tsc","dev":"ts-node src/cli.ts","prepublishOnly":"npm run build"},"dependencies":{"chalk":"^4.1.2","commander":"^12.0.0","fs-extra":"^11.2.0","inquirer":"^8.2.7","ora":"^5.4.1"},"devDependencies":{"@semantic-release/changelog":"^6.0.3","@semantic-release/commit-analyzer":"^13.0.1","@semantic-release/exec":"^7.1.0","@semantic-release/git":"^10.0.1","@semantic-release/github":"^10.3.5","@semantic-release/npm":"^12.0.2","@semantic-release/release-notes-generator":"^14.1.0","@types/fs-extra":"^11.0.4","@types/inquirer":"^9.0.7","@types/node":"^20.11.0","conventional-changelog-conventionalcommits":"^8.0.0","semantic-release":"^24.2.9","ts-node":"^10.9.2","typescript":"^5.3.3"},"engines":{"node":">=18.0.0"},"keywords":["inai","agent","agent-mesh","interoperability","p2p","libp2p","did","discovery","ai-agents","langgraph","crewai","cli","scaffold"],"repository":{"type":"git","url":"git+https://github.com/ch4r10t33r/inai.git"},"bugs":{"url":"https://github.com/ch4r10t33r/inai/issues"},"homepage":"https://github.com/ch4r10t33r/inai#readme","license":"Apache-2.0","publishConfig":{"access":"public"},"_id":"@ch4r10teer41/inai-cli@0.3.1","gitHead":"2c1f626328e7c00f83b6183ff24e58e823882716","types":"./dist/cli.d.ts","_nodeVersion":"22.22.1","_npmVersion":"10.9.7","dist":{"integrity":"sha512-XovBr1TdlcqCKRVQfui8gOd323lDZc7K20UAHUFQOCjHRurXspRrPGRVMP5EjG6ZK+Aq43cLKTfzlhb/f10big==","shasum":"9b607e5f4c282ed5181a3f281a1fff9b8658af66","tarball":"https://registry.npmjs.org/@ch4r10teer41/inai-cli/-/inai-cli-0.3.1.tgz","fileCount":203,"unpackedSize":1549780,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICf2NpPmWa4Gqnl7l04WjOkgfnLYfujjY6TlBpI0j+GQAiBgy3HRVUGk44Xf+KY12s1PGxius5+OawDTr7s/JiVq1A=="}]},"_npmUser":{"name":"ch4r01teer41","email":"partha@charioteerconsulting.com"},"directories":{},"maintainers":[{"name":"ch4r01teer41","email":"partha@charioteerconsulting.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/inai-cli_0.3.1_1774655238346_0.5316208242492428"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-27T23:47:18.247Z","0.3.1":"2026-03-27T23:47:18.533Z","modified":"2026-03-27T23:47:18.775Z"},"maintainers":[{"name":"ch4r01teer41","email":"partha@charioteerconsulting.com"}],"description":"Scaffold P2P-discoverable, DID-native AI agents that interoperate across any framework","homepage":"https://github.com/ch4r10t33r/inai#readme","keywords":["inai","agent","agent-mesh","interoperability","p2p","libp2p","did","discovery","ai-agents","langgraph","crewai","cli","scaffold"],"repository":{"type":"git","url":"git+https://github.com/ch4r10t33r/inai.git"},"bugs":{"url":"https://github.com/ch4r10t33r/inai/issues"},"license":"Apache-2.0","readme":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/ch4r10t33r/inai/main/docs/logo.svg\" alt=\"Inai\" width=\"220\"/>\n</p>\n\n# Inai\n\n> **Autonomous Agentic Coordination Middleware** — scaffold P2P-discoverable, DID-native AI agents that interoperate across any framework, with optional ERC-8004 on-chain compliance.\n\n*இணை (inai) — Tamil for \"join\" or \"connect\". Like TCP/IP connects heterogeneous computers, Inai connects heterogeneous agents.*\n\n---\n\n## Why Inai?\n\nMost AI frameworks help you **build** agents. Inai helps them **find and talk to each other** — across frameworks, runtimes, and clouds.\n\n| What exists today | The gap Inai fills |\n|---|---|\n| Orchestration frameworks (CrewAI, AutoGen) | Agents locked inside one runtime, no external discovery |\n| Framework-bound agents (LangGraph, AutoGPT) | No standardised interface for cross-framework calls |\n| Closed ecosystems (Fetch.ai, SingularityNET) | Ecosystem lock-in, heavyweight infrastructure |\n\nInai is **not a platform** — it is a protocol layer others build on.\n\n---\n\n## Architecture\n\n| Layer | Role | Technologies |\n|---|---|---|\n| **L4** Execution | Agent frameworks | LangGraph · Google ADK · CrewAI · Agno · LlamaIndex · smolagents · OpenAI Agents |\n| **L3** Interaction | Request / response | `AgentRequest` / `AgentResponse` · AMP-2 |\n| **L2** Discovery | Capability lookup | Local · HTTP · libp2p + Kademlia DHT · AMP-1 |\n| **L1** Identity | DID + trust | `did:key` W3C (default) · ERC-8004 on-chain (optional) |\n\nInai operates primarily at **L2** and **L3**, bridging L1 identity to L4 framework execution.\n\n---\n\n## Features\n\n- **Framework-agnostic** — wrap LangGraph, Google ADK, CrewAI, Agno, LlamaIndex, smolagents, or OpenAI Agents with one function call\n- **Built-in HTTP server** — `inai run MyAgent --port 6174` starts a real HTTP server; no extra setup\n- **MCP bridge** — any MCP server becomes a Inai agent; any Inai agent becomes an MCP server (Claude Desktop, Cursor, Continue)\n- **Dynamic discovery** — agents register capabilities; callers query at runtime, no hardcoded URLs\n- **Mesh protocols** — heartbeat, capability exchange (as part of handshake), and gossip fan-out built in\n- **P2P mesh** — libp2p + QUIC + Kademlia DHT; mDNS for LAN; circuit relay for NAT traversal\n- **DID identity** — `did:key` W3C standard out of the box; no wallet, no gas, no tokens required\n- **x402 payments** — opt-in micropayment layer; charge per capability in USDC / ETH on Base\n- **MPP payments** — [Machine Payments Protocol](https://mpp.dev) plugin; HTTP 402 challenge–credential–receipt flow with Tempo stablecoin, Stripe SPT, and Lightning support across TypeScript, Rust, and Zig\n- **Multi-language** — TypeScript, Python, Rust, Zig\n- **One CLI** — scaffold, create, run, discover\n\n---\n\n## Language Coverage\n\n| Feature | Python | TypeScript | Rust | Zig |\n|---|:---:|:---:|:---:|:---:|\n| **IAgent interface** | ✅ | ✅ | ✅ | ✅ |\n| **AgentRequest / AgentResponse** | ✅ | ✅ | ✅ | ✅ |\n| **ANR (Agent Network Record)** | ✅ | ✅ | ✅ | ✅ |\n| **DID identity (`did:key`)** | ✅ | ✅ | ✅ | ✅ |\n| **HTTP server (`inai run`)** | ✅ | ✅ | ✅ | ✅ |\n| **Discovery — local (in-memory)** | ✅ | ✅ | ✅ | ✅ |\n| **Discovery — HTTP** | ✅ | ✅ | ✅ | ✅ |\n| **Discovery — libp2p + Kademlia DHT** | ✅ | ✅ | ✅ | ✅ |\n| **Discovery — gossip fan-out** | ✅ | ✅ | ✅ | ✅ |\n| **AgentClient (mesh protocols)** | ✅ | ✅ | ✅ | ✅ |\n| **Example agent** | ✅ | ✅ | ✅ | ✅ |\n| **Plugin system (framework adapters)** | ✅ | ✅ | ✅ | ✅ |\n| **LangGraph plugin** | ✅ | ✅ | ✅ | ✅ |\n| **Google ADK plugin** | ✅ | ✅ | ✅ | ✅ |\n| **CrewAI plugin** | ✅ | ✅ | ✅ | ✅ |\n| **OpenAI Agents SDK plugin** | ✅ | ✅ | ✅ | ✅ |\n| **Agno plugin** | ✅ | ✅ | ✅ | ✅ |\n| **LlamaIndex plugin** | ✅ | ✅ | ✅ | ✅ |\n| **smolagents plugin** | ✅ | ✅ | ✅ | ✅ |\n| **MCP bridge (wrap MCP servers)** | ✅ | ✅ | ✅ | ✅ |\n| **MCP bridge (expose as MCP server)** | ✅ | ✅ | ✅ | ✅ |\n| **x402 micropayments** | ✅ | ✅ | ✅ | ✅ |\n| **MPP plugin ([Machine Payments Protocol](https://mpp.dev))** | 🔜 | ✅ | ✅ | ✅ |\n| **Streaming (SSE via /invoke/stream)** | ✅ | ✅ | ✅ | ✅ |\n\n**Legend:** ✅ implemented · 🔜 on roadmap · — not applicable for this language\n\n**MPP:** HTTP 402 payment gating using the MPP challenge–credential–receipt model (`MppPlugin` / `mpp` modules). Python template support is on the roadmap; use x402 or bridge via a TS/Rust/Zig agent until then.\n\n**Rust / Zig — framework plugins:** LangGraph, Google ADK, and CrewAI are Python/JS frameworks with no native Rust or Zig SDKs. The Rust and Zig plugins are **HTTP bridge adapters** that call a running service endpoint (e.g. `adk web`, a LangServe app, or a FastAPI-wrapped CrewAI crew) so the agent participates in the Inai mesh without embedding a Python interpreter.\n\n**Zig — discovery:** `HttpDiscovery` (`discovery_http.zig`) is a REST client for the discovery service. `Libp2pDiscovery` (`discovery_libp2p.zig`) is a full Kademlia DHT implementation in pure Zig (UDP/JSON transport, 256-bucket XOR routing table, k=20). Key derivation matches the Rust implementation (`SHA-256(\"inai:cap:<cap>\")` / `SHA-256(\"inai:anr:<agentId>\")`). Note: uses a Inai-native JSON wire format rather than libp2p protobuf — interoperates with other Zig Inai nodes.\n\n---\n\n## Installation\n\n### npm (recommended)\n\nWorks on macOS, Linux, and Windows. npm downloads the correct pre-built\nbinary for your platform automatically.\n\n```bash\nnpm install -g @ch4r10teer41/inai-cli\n```\n\n### curl installer (macOS / Linux)\n\nAuto-detects your OS and architecture, installs to `/usr/local/bin`:\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/ch4r10t33r/inai/main/install.sh | sh\n```\n\nTo install to a custom directory set `INAI_INSTALL_DIR` before piping:\n\n```bash\nINAI_INSTALL_DIR=~/.local/bin curl -fsSL https://raw.githubusercontent.com/ch4r10t33r/inai/main/install.sh | sh\n```\n\n> **Windows** — use npm above, or download `inai-win32-x64.exe` directly from the\n> [Releases page](https://github.com/ch4r10t33r/inai/releases/latest).\n\n### Build from source\n\nRequires [Rust](https://rustup.rs) 1.75+.\n\n```bash\ngit clone https://github.com/ch4r10t33r/inai.git\ncd inai\ncargo build --release --package inai-cli\n# Binary is at ./target/release/inai\n```\n\n---\n\n## Quick Start\n\n```bash\n# Scaffold a new project (TypeScript default)\ninai init my-agent\ncd my-agent && npm install\ninai run ExampleAgent --port 6174\n\n# Python\ninai init my-agent --lang python\ncd my-agent\ninai run ExampleAgent --port 6174\n\n# Rust\ninai init my-agent --lang rust\ncd my-agent\ncargo run --example did_key_identity        # optional: did:key from secp256k1 secret\ncargo run --example gossip_fanout_discovery # optional: in-memory gossip fan-out demo\n\n# Zig\ninai init my-agent --lang zig\ncd my-agent\nzig build examples   # optional: builds did:key + gossip fan-out demo binaries\n```\n\nOnce running, the agent prints its full startup banner. **libp2p is the default** — a Peer ID is shown out of the box:\n\n**libp2p mode (default — no config needed):**\n```\n────────────────────────────────────────────────────────────\n  Inai Agent Online  v0.1.0\n────────────────────────────────────────────────────────────\n  Name         ExampleAgent\n  Agent ID     inai://agent/example\n  Peer ID      12D3KooWQmK3p2pFBFVxpuBkJCxZ1HsK...\n  Endpoint     http://0.0.0.0:6174\n  Multiaddr    /ip4/0.0.0.0/tcp/6174/p2p/12D3KooW...\n  Discovery    libp2p (Kademlia DHT)\n  ANR          {\"agentId\":\"inai://agent/example\",\"name\":\"ExampleAgent\",...}\n  ANR JSON     curl -s http://localhost:6174/anr\n  Capabilities (2)\n           • echo\n           • ping\n────────────────────────────────────────────────────────────\n```\n\n**Local mode (`INAI_DISCOVERY_TYPE=local` — dev/test, zero ports opened):**\n```\n────────────────────────────────────────────────────────────\n  Inai Agent Online  v0.1.0\n────────────────────────────────────────────────────────────\n  Name         ExampleAgent\n  Agent ID     inai://agent/example\n  Peer ID      (none — local discovery mode)\n  Endpoint     http://0.0.0.0:6174\n  Discovery    local\n  ANR          {\"agentId\":\"inai://agent/example\",\"name\":\"ExampleAgent\",...}\n  ANR JSON     curl -s http://localhost:6174/anr\n  Capabilities (2)\n           • echo\n           • ping\n────────────────────────────────────────────────────────────\n```\n\n> **Default port: 6174** ([Kaprekar's constant](https://en.wikipedia.org/wiki/6174)). Override with `INAI_PORT=<n>` or `--port <n>`.\n\n---\n\n## Common Questions\n\n### Why is Peer ID missing from the banner?\n\n```\nPeer ID      (none — local discovery mode)\n```\n\nThis means `INAI_DISCOVERY_TYPE=local` is set (or the libp2p host failed to bind and fell back to local). In local mode there is no libp2p host, so no Peer ID exists. The agent is fully functional — it just uses in-process discovery.\n\nlibp2p is the **default**. If you see this line unexpectedly, check that your `.env` does not have `INAI_DISCOVERY_TYPE=local` set, or that no other process is occupying the libp2p port.\n\nTo ensure a stable Peer ID across restarts, set a persistent key:\n\n```env\nINAI_AGENT_KEY=<your 64-hex-char secp256k1 private key>\n```\n\nGenerate a fresh key:\n\n```bash\n# Node.js one-liner\nnode -e \"const {secp256k1} = require('ethereum-cryptography/secp256k1'); \\\n  console.log(Buffer.from(secp256k1.utils.randomPrivateKey()).toString('hex'));\"\n\n# or openssl\nopenssl rand -hex 32\n```\n\nIf `INAI_AGENT_KEY` is omitted a random ephemeral key is generated at startup — the Peer ID will change every time the process restarts.\n\n---\n\n### Why did it feel like there was no ANR?\n\nThe startup banner previously only printed the URL to the `/anr` endpoint, which made it easy to miss the fact that an ANR was live. The banner now always shows:\n\n| Line | What it means |\n|---|---|\n| `ANR` | One-line JSON preview: `agentId`, `name`, `capabilities`, `network` |\n| `ANR JSON` | The exact `curl` command to fetch the full record |\n\nFetch the full ANR at any time:\n\n```bash\ncurl -s http://localhost:6174/anr | jq .\n```\n\nThe ANR is **always present** regardless of discovery mode. It is not dependent on libp2p being enabled.\n\n---\n\n### What's the difference between `local`, `http`, and `libp2p` discovery?\n\n| Mode | When to use | PeerId | Central server |\n|---|---|---|---|\n| `libp2p` **(default)** | All environments — production P2P mesh | ✓ | ✗ |\n| `local` | Unit tests / single-process demos | ✗ | ✗ |\n| `http` | Staging / enterprise / managed registry | ✗ | ✓ |\n\nSwitch modes with a single env var — no code changes required:\n\n```env\n# libp2p (default — nothing to set, or explicitly):\nINAI_DISCOVERY_TYPE=libp2p\nINAI_AGENT_KEY=<64-hex secp256k1 private key>   # optional but recommended\n\n# local (dev/test — zero ports):\nINAI_DISCOVERY_TYPE=local\n\n# http (centralised registry):\nINAI_DISCOVERY_TYPE=http\nINAI_DISCOVERY_URL=https://registry.example.com\n```\n\n---\n\n## CLI Reference\n\n| Command | Description |\n|---|---|\n| `inai scaffold <name> [OPTIONS]` | Generate a minimal, targeted agent project (see below) |\n| `inai init <name> [--lang ts\\|python\\|rust\\|zig]` | Copy the full template library into a new project (see below) |\n| `inai create agent <name> [-c cap1,cap2] [--framework X]` | Add an agent to an existing project |\n| `inai run <AgentName> [--port 6174]` | Start an agent's HTTP server |\n| `inai discover [-c capability] [--host h] [--port p]` | Query the discovery layer |\n| `inai version` | Show CLI version and build info |\n\n### `inai scaffold` vs `inai init`\n\nBoth create a new agent project, but they serve different workflows:\n\n| | `scaffold` | `init` |\n|---|---|---|\n| **Approach** | Generates files programmatically from flags | Copies the full embedded template library |\n| **Output** | Minimal — only what you asked for | Full kitchen sink — all discovery adapters, example agents, every template file |\n| **Customisation** | `--plugins`, `--stream`, `--x402`, `--did`, `--discovery` flags wire things together for you | Raw templates with `{{AGENT_NAME}}` token substitution — you wire things yourself |\n| **Also generates** | `.env.example`, `README.md` | `inai.config.json` |\n| **Best for** | Starting a focused, production-ready agent quickly | Exploring the full template library or building something custom |\n| **Languages** | TypeScript, Rust, Zig | TypeScript, Python, Rust, Zig |\n\n> **Rule of thumb:** use `scaffold` when you know what you want; use `init` when you want to browse all available patterns and pick your own path.\n\n### `inai scaffold` — targeted project generator\n\n```bash\ninai scaffold <name> [OPTIONS]\n\nOptions:\n  -l, --lang <LANG>           typescript | rust | zig          [default: typescript]\n  -p, --plugins <PLUGINS>     openai,agno,langgraph,google_adk,crewai,\n                              llamaindex,smolagents,mcp        [default: none]\n  -o, --output <DIR>          output directory                 [default: cwd]\n  -d, --did                   include DID key generation example\n  -s, --stream                include SSE streaming endpoint\n  -x, --x402                  include x402 micropayments middleware\n      --discovery <BACKEND>   http | libp2p                    [default: http]\n      --dry-run               print file tree without writing\n```\n\n**Examples:**\n\n```bash\n# TypeScript agent with LangGraph + OpenAI, SSE streaming, libp2p discovery\ninai scaffold my-agent --lang typescript --plugins langgraph,openai --stream --discovery libp2p\n\n# Rust agent with MCP bridge and x402 micropayments\ninai scaffold payments-agent --lang rust --plugins mcp --x402\n\n# Zig agent with DID examples — preview first, then generate\ninai scaffold did-agent --lang zig --did --dry-run\ninai scaffold did-agent --lang zig --did\n```\n\nGenerated structure (TypeScript example):\n```\nmy-agent/\n├── package.json\n├── tsconfig.json\n├── src/\n│   ├── agent.ts        ← discovery registration, /invoke handler, selected plugins\n│   ├── index.ts        ← entry point\n│   └── plugins/        ← only created when --plugins is set\n│       └── LangGraphPlugin.ts\n├── .env.example\n└── README.md\n```\n\n### `inai init` — full template copy\n\n```bash\ninai init <name> [OPTIONS]\n\nOptions:\n  -l, --lang <LANG>     typescript | python | rust | zig   [default: typescript]\n      --no-discovery    skip copying discovery adapter files\n      --no-example      skip copying example agent files\n```\n\nCopies the **entire template library** for the chosen language into `<name>/`, applies token substitution (`{{AGENT_NAME}}`, `{{PROJECT_NAME}}`, etc.), and writes a `inai.config.json`. The result is a fully-populated project containing every discovery adapter, all plugin stubs, and complete example agents — ready to explore and trim down.\n\n```bash\n# Full TypeScript project with everything included\ninai init my-project --lang typescript\n\n# Rust project without the example agent files\ninai init my-project --lang rust --no-example\n```\n\n### `inai run` — HTTP endpoints\n\nWhen you run an agent, these endpoints are live automatically:\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/invoke` | `POST` | Call any capability — `{ capability, payload, from }` → `AgentResponse` |\n| `/invoke/stream` | `POST` | Same as `/invoke` but responds with `text/event-stream` SSE frames |\n| `/health` | `GET` | Heartbeat — returns health status, capability count, version |\n| `/anr` | `GET` | Full Agent Network Record (ANR) as JSON |\n| `/capabilities` | `GET` | List of capability names |\n| `/gossip` | `POST` | Receive gossip messages from mesh peers |\n\n---\n\n## Framework Plugins\n\n### Python — all supported frameworks\n\n```python\n# Google ADK\nfrom plugins.google_adk_plugin import wrap_google_adk\nagent = wrap_google_adk(adk_agent, name=\"SupportBot\", agent_id=\"inai://agent/support\", owner=\"0x...\")\n\n# CrewAI\nfrom plugins.crewai_plugin import wrap_crewai\nagent = wrap_crewai(crew_agent, name=\"ResearchBot\", agent_id=\"inai://agent/research\", owner=\"0x...\")\n\n# LangGraph\nfrom plugins.langgraph_plugin import wrap_langgraph\nagent = wrap_langgraph(compiled_graph, config)\n\n# OpenAI Agents SDK\nfrom plugins.openai_plugin import wrap_openai\nagent = wrap_openai(oai_agent, name=\"WeatherBot\", agent_id=\"inai://agent/weather\", owner=\"0x...\")\n\n# Agno / LlamaIndex / smolagents\nfrom plugins.agno_plugin       import wrap_agno\nfrom plugins.llamaindex_plugin import wrap_llamaindex\nfrom plugins.smolagents_plugin import wrap_smolagents\n\n# Serve over HTTP (all plugins share the same interface)\nimport asyncio\nasyncio.run(agent.serve(port=6174))\n```\n\n### TypeScript\n\n```typescript\n// LangGraph (in-process — wraps a compiled CompiledGraph)\nimport { wrapLangGraph } from './plugins/LangGraphPlugin';\nconst agent = wrapLangGraph(compiledGraph, { agentId: 'inai://agent/researcher', name: 'Researcher', ... });\n\n// Google ADK (in-process — wraps a BaseAgent / LlmAgent)\nimport { wrapGoogleADK } from './plugins/GoogleADKPlugin';\nconst agent = wrapGoogleADK(adkAgent, { agentId: 'inai://agent/support', name: 'Support', ... });\n\n// CrewAI (HTTP bridge — calls a running CrewAI service)\nimport { wrapCrewAI } from './plugins/CrewAIPlugin';\nconst agent = await wrapCrewAI({\n  agentId:    'inai://agent/writer',\n  name:       'WriterCrew',\n  version:    '1.0.0',\n  owner:      '0xYourWallet',\n  serviceUrl: 'http://localhost:8000',  // FastAPI-wrapped CrewAI crew\n});\n\n// OpenAI Agents SDK\nimport { wrapOpenAI }     from './plugins/OpenAIPlugin';\nimport { AgnoPlugin }     from './plugins/AgnoPlugin';\nimport { LlamaIndexPlugin } from './plugins/LlamaIndexPlugin';\nimport { SmolagentsPlugin } from './plugins/SmolagentsPlugin';\n\nconst agent = wrapOpenAI(oaiAgent, { agentId: 'inai://agent/weather', name: 'WeatherBot', ... });\n\n// Agno, LlamaIndex, smolagents — same one-liner pattern\nconst agnoAgent     = new AgnoPlugin({ agentId: 'inai://agent/agno', ... }).wrap(myAgnoAgent);\nconst llamaAgent    = new LlamaIndexPlugin({ agentId: 'inai://agent/llama', ... }).wrap(myIndex);\nconst smolaAgent    = new SmolagentsPlugin({ agentId: 'inai://agent/smol', ... }).wrap(mySmolAgent);\n\nawait agent.serve({ port: 6174 });\n```\n\n### Rust\n\n```rust\nuse inai::plugins::{\n    langgraph::{LangGraphPlugin, LangGraphService},\n    google_adk::{GoogleADKPlugin, GoogleADKService},\n    crewai::{CrewAIPlugin, CrewAIService},\n    openai::{OpenAIPlugin, OpenAIService},\n    agno::{AgnoPlugin, AgnoService},\n    llamaindex::{LlamaIndexPlugin, LlamaIndexService},\n    smolagents::{SmolagentsPlugin, SmolagentsService},\n    base::PluginConfig,\n};\n\n// LangGraph — HTTP bridge to a LangServe endpoint\nlet service = LangGraphService { base_url: \"http://localhost:8000\".into(), ..Default::default() };\nlet agent = LangGraphPlugin::new().wrap(service, PluginConfig {\n    agent_id: \"inai://agent/researcher\".into(), owner: \"0xYourWallet\".into(), ..Default::default()\n});\n\n// OpenAI-compatible API (OpenAI, vLLM, Ollama, …)\nlet service = OpenAIService {\n    base_url: \"https://api.openai.com\".into(),\n    model:    \"gpt-4o-mini\".into(),\n    api_key:  Some(std::env::var(\"OPENAI_API_KEY\").unwrap()),\n    ..Default::default()\n};\nlet agent = OpenAIPlugin::new().wrap(service, PluginConfig { .. });\n\n// Agno — HTTP bridge to a deployed Agno FastAPI server\nlet service = AgnoService { base_url: \"http://localhost:7777\".into(), ..Default::default() };\nlet agent = AgnoPlugin::new().wrap(service, PluginConfig { .. });\n\n// LlamaIndex — HTTP bridge to a LlamaIndex server\nlet service = LlamaIndexService { base_url: \"http://localhost:8080\".into(), ..Default::default() };\nlet agent = LlamaIndexPlugin::new().wrap(service, PluginConfig { .. });\n\n// smolagents — Gradio or custom API bridge\nlet service = SmolagentsService { base_url: \"http://localhost:7860\".into(), ..Default::default() };\nlet agent = SmolagentsPlugin::new().wrap(service, PluginConfig { .. });\n\n// CrewAI — HTTP bridge to a FastAPI-wrapped crew\nlet mut service = CrewAIService { base_url: \"http://localhost:8000\".into(), ..Default::default() };\nlet plugin = CrewAIPlugin::new();\nplugin.fetch_capabilities(&mut service).await?;\nlet agent = plugin.wrap(service, PluginConfig { .. });\n```\n\n### Zig\n\n```zig\nconst lg   = @import(\"plugins/langgraph.zig\");\nconst adk  = @import(\"plugins/google_adk.zig\");\nconst ca   = @import(\"plugins/crewai.zig\");\nconst oai  = @import(\"plugins/openai.zig\");\nconst agno = @import(\"plugins/agno.zig\");\nconst lli  = @import(\"plugins/llamaindex.zig\");\nconst sma  = @import(\"plugins/smolagents.zig\");\nconst Wrapped = @import(\"plugins/wrapped_agent.zig\").WrappedAgent;\n\n// LangGraph — HTTP bridge to a LangServe endpoint\nvar lg_service = lg.LangGraphService{ .base_url = \"http://localhost:8000\" };\nvar lg_plugin  = lg.LangGraphPlugin.init(allocator);\ndefer lg_plugin.deinit();\nvar agent = Wrapped(lg.LangGraphService, lg.LangGraphPlugin).init(\n    &lg_service, &lg_plugin, .{ .agent_id = \"inai://agent/researcher\", .owner = \"0x...\" }, allocator,\n);\n\n// OpenAI-compatible API\nvar oai_service = oai.OpenAIService{ .base_url = \"https://api.openai.com\", .api_key = \"sk-...\" };\nvar oai_plugin  = oai.OpenAIPlugin.init(allocator);\ndefer oai_plugin.deinit();\n\n// Agno — deployed Agno server\nvar agno_service = agno.AgnoService{ .base_url = \"http://localhost:7777\" };\nvar agno_plugin  = agno.AgnoPlugin.init(allocator);\ndefer agno_plugin.deinit();\n\n// LlamaIndex — deployed LlamaIndex server\nvar lli_service = lli.LlamaIndexService{ .base_url = \"http://localhost:8080\" };\n\n// smolagents — Gradio or custom API\nvar sma_service = sma.SmolagentsService{ .base_url = \"http://localhost:7860\" };\n```\n\n---\n\n## MCP Bridge\n\nInai has a two-way bridge with the [Model Context Protocol](https://modelcontextprotocol.io):\n\n```python\n# Any MCP server → Inai agent (GitHub, filesystem, Slack, databases…)\nfrom plugins.mcp_plugin import MCPPlugin\nplugin = await MCPPlugin.from_command(\n    [\"npx\", \"-y\", \"@modelcontextprotocol/server-github\"],\n    config, env={\"GITHUB_TOKEN\": \"ghp_...\"}\n)\nagent = plugin.wrap()\nawait agent.serve(port=8081)\n\n# Any Inai agent → MCP server (Claude Desktop, Cursor, Continue…)\nfrom adapters.mcp_server import serve_as_mcp\nawait serve_as_mcp(my_agent)                           # stdio (Claude Desktop)\nawait serve_as_mcp(my_agent, transport=\"sse\", port=3000)  # SSE (remote)\n```\n\n```typescript\n// TypeScript — same bridge\nimport { MCPPlugin }    from './plugins/MCPPlugin';\nimport { serveAsMcp }  from './adapters/MCPServer';\n\nconst plugin = await MCPPlugin.fromCommand(['npx', '-y', '@modelcontextprotocol/server-github'], config);\nawait plugin.wrap().serve({ port: 8081 });\n\nawait serveAsMcp(myAgent);                              // stdio\nawait serveAsMcp(myAgent, { transport: 'sse', port: 3000 });\n```\n\n```rust\n// Rust — stdio subprocess or HTTP endpoint\nuse inai::mcp::{McpPlugin, serve_as_mcp, ServeMcpOptions, Transport};\nuse inai::plugins::base::PluginConfig;\n\n// Wrap an MCP server (subprocess) → Inai agent\nlet agent = McpPlugin::from_command(\n    &[\"npx\", \"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\"],\n    PluginConfig { agent_id: \"inai://agent/fs\".into(), .. },\n    None,\n).await?;\ninai::server::serve(agent, 8081).await?;\n\n// Expose a Inai agent → MCP server (stdio for Claude Desktop)\nserve_as_mcp(my_agent, ServeMcpOptions::default()).await?;\n\n// Expose over SSE (remote clients)\nserve_as_mcp(my_agent, ServeMcpOptions {\n    transport: Transport::Sse, port: 3000, ..Default::default()\n}).await?;\n```\n\n```zig\n// Zig — stdio subprocess or HTTP endpoint\nconst mcp_plugin = @import(\"mcp_plugin.zig\");\nconst mcp_server = @import(\"mcp_server.zig\");\n\n// Wrap an MCP server (subprocess) → Inai agent\nvar plugin = mcp_plugin.McpPlugin.initStdio(allocator);\ndefer plugin.deinit();\ntry plugin.fromCommand(&.{ \"npx\", \"-y\", \"@modelcontextprotocol/server-filesystem\", \"/tmp\" }, null);\n\n// Expose a Inai agent → MCP server (stdio for Claude Desktop)\ntry mcp_server.serveAsMcp(MyAgent, &my_agent, .{}, allocator);\n\n// Expose over HTTP (POST /mcp)\ntry mcp_server.serveAsMcp(MyAgent, &my_agent, .{ .transport = .http, .port = 3000 }, allocator);\n```\n\n→ Full guide: **[docs/mcp.md](docs/mcp.md)**\n\n---\n\n## Agent-to-Agent Communication\n\n```python\nfrom interfaces.iagent_client import AgentClient\nfrom discovery.local_discovery import LocalDiscovery\n\nclient = AgentClient(discovery=LocalDiscovery.get_instance())\n\n# 1. Find an agent by capability\nentry = await client.find(\"web_search\")\n\n# 2. Connect (handshake: heartbeat + capability exchange) → AgentSession\nsession = await client.connect(entry)\nprint(session.capabilities)    # ['web_search', 'summarise']\nprint(session.is_healthy)      # True\n\n# 3. Call via the session\nresp = await session.call(\"web_search\", {\"query\": \"latest AI news\"})\nprint(resp.result[\"content\"])\n\n# Or skip the handshake for one-off calls\nresp = await client.call_capability(\"web_search\", {\"query\": \"latest AI news\"})\n```\n\n→ Full guide: **[docs/interfaces.md](docs/interfaces.md)**\n\n---\n\n## DIDComm v2 — Encrypted Agent Messaging\n\nInai includes a full DIDComm v2 implementation for end-to-end encrypted, authenticated messages between agents. No external key infrastructure required — each agent's `did:key` is derived from its identity keypair.\n\n**Crypto stack:** X25519 ECDH key agreement + ChaCha20-Poly1305 AEAD.\n**Wire format:** JWE JSON serialization with per-recipient key wrapping.\n**Modes:** `authcrypt` (sender-authenticated) and `anoncrypt` (anonymous).\n\n### TypeScript\n\n```typescript\nimport { DidcommClient, MessageTypes } from './didcomm';\n\nconst alice = DidcommClient.generateKeyPair();\nconst bob   = DidcommClient.generateKeyPair();\n\nconst aliceClient = new DidcommClient(alice);\nconst bobClient   = new DidcommClient(bob);\n\n// Alice encrypts an INVOKE message to Bob (authcrypt)\nconst encrypted = await aliceClient.invoke(bob.did, 'translate', { text: 'hello' });\n\n// Bob decrypts it\nconst { message, senderDid } = await bobClient.unpack(encrypted);\nconsole.log(message.body);   // { text: 'hello' }\nconsole.log(senderDid);      // alice's did:key\n```\n\n### Rust\n\n```rust\nuse crate::didcomm::DidcommClient;\n\nlet alice = DidcommClient::generate()?;\nlet bob   = DidcommClient::generate()?;\n\n// Encrypt (authcrypt)\nlet packed = alice.invoke(&bob.did, \"translate\", json!({\"text\": \"hello\"}), false)?;\n\n// Decrypt\nlet (msg, sender_did) = bob.unpack(&packed)?;\nprintln!(\"{}\", msg.body);           // {\"text\":\"hello\"}\nprintln!(\"{:?}\", sender_did);       // Some(\"did:key:z6Mk...\")\n\n// Anonymous — sender is not revealed\nlet anon = alice.invoke(&bob.did, \"translate\", json!({\"text\": \"hi\"}), true)?;\nlet (msg2, none_sender) = bob.unpack(&anon)?;\nassert!(none_sender.is_none());\n```\n\n### Zig\n\n```zig\nconst didcomm = @import(\"didcomm.zig\");\n\nvar alice = try didcomm.DidcommClient.generate(allocator);\ndefer alice.deinit();\nvar bob = try didcomm.DidcommClient.generate(allocator);\ndefer bob.deinit();\n\n// Encrypt\nconst packed = try alice.invoke(allocator, bob.did, \"translate\", \"{\\\"text\\\":\\\"hello\\\"}\", false);\ndefer allocator.free(packed);\n\n// Decrypt\nconst result = try bob.unpack(allocator, packed);\ndefer result.deinit(allocator);\nstd.debug.print(\"body: {s}\\n\", .{result.message.body_json});\nstd.debug.print(\"from: {?s}\\n\", .{result.sender_did});\n```\n\nSource: [`templates/typescript/didcomm.ts`](templates/typescript/didcomm.ts) · [`templates/rust/src/didcomm.rs`](templates/rust/src/didcomm.rs) · [`templates/zig/src/didcomm.zig`](templates/zig/src/didcomm.zig)\n\n---\n\n## Protocol Reference\n\n### Ports and Addresses\n\n| Layer | Default address | Env override |\n|---|---|---|\n| HTTP server (`/invoke`, `/health`, …) | `0.0.0.0:6174` | `INAI_PORT` |\n| libp2p TCP (GossipSub + request-response) | `/ip4/0.0.0.0/tcp/6174` | `INAI_P2P_ADDR` |\n| libp2p QUIC (Rust/Zig DHT) | `/ip4/0.0.0.0/udp/6174/quic-v1` | `INAI_P2P_PORT` |\n| Bootstrap peers | _(none — mDNS only on LAN)_ | `INAI_BOOTSTRAP_PEERS` (comma-separated multiaddrs) |\n\nAll discovery traffic — DHT announces, gossip fan-out, and capability queries — travels **over the libp2p transport on the same port**. There is no separate discovery port.\n\nEvery agent's ANR always carries the full `multiaddr` when running in libp2p mode:\n\n```\n/ip4/<host>/tcp/<port>/p2p/<PeerId>          # TCP (TypeScript)\n/ip4/<host>/udp/<port>/quic-v1/p2p/<PeerId>  # QUIC (Rust)\n```\n\nWhen only HTTP transport is active, `network.multiaddr` is empty and `network.protocol` is `\"http\"`.\n\n---\n\n### Agent DID and Identity\n\nEvery agent has a **DID** derived from its secp256k1 keypair — no wallet or gas required.\n\n| Mode | DID format | `agentId` |\n|---|---|---|\n| `local` (default) | `did:key:zQ3sh…` | `inai://agent/<eth-addr>` |\n| `env` | `did:key:zQ3sh…` | same, driven by `INAI_AGENT_KEY` |\n| `erc8004` | `did:pkh:eip155:<chainId>:0x…` | on-chain verified owner |\n\nThe DID is a multicodec-prefixed, base58btc-encoded secp256k1 compressed public key:\n\n```\ndid:key:z  <base58btc( 0xe701 || compressed-secp256k1-pubkey )>\n            ↑ secp256k1 multicodec varint\n```\n\n### Agent Network Record (ANR)\n\nThe ANR is the canonical self-description of an agent. It is returned by `GET /anr`, stored in the Kademlia DHT, and broadcast via gossip. Its shape is `DiscoveryEntry`:\n\n```typescript\ninterface DiscoveryEntry {\n  agentId:      string;           // \"inai://agent/0xABC…\" or a DID\n  name:         string;           // human-readable label\n  owner:        string;           // Ethereum address or DID of the keyholder\n  capabilities: string[];         // [\"echo\", \"web_search\", \"generate_image\"]\n  network: {\n    protocol:  'http' | 'websocket' | 'grpc' | 'tcp' | 'libp2p';\n    host:      string;            // \"192.168.1.5\" or \"agent.example.com\"\n    port:      number;\n    tls:       boolean;\n    peerId?:   string;            // libp2p PeerId (when protocol === 'libp2p')\n    multiaddr?: string;           // full multiaddr, e.g. \"/ip4/…/udp/…/quic-v1/p2p/…\"\n  };\n  health: {\n    status:         'healthy' | 'degraded' | 'unhealthy';\n    lastHeartbeat:  string;       // ISO 8601\n    uptimeSeconds:  number;\n  };\n  registeredAt: string;           // ISO 8601\n  metadataUri?: string;           // IPFS / HTTPS link to extended metadata\n}\n```\n\nThe DHT stores a signed envelope around this record:\n\n```json\n{\n  \"v\": 1,\n  \"seq\": 42,\n  \"entry\": { /* StoredEntry fields */ },\n  \"sig\": \"<base64-compact-secp256k1-signature-over-sha256(unsigned-envelope)>\"\n}\n```\n\n### Capabilities\n\nCapabilities are **plain strings** declared by `getCapabilities()`. They form the unit of discovery and billing:\n\n```typescript\ngetCapabilities(): string[]   // e.g. [\"echo\", \"web_search\", \"generate_image\"]\n```\n\nFor plugin-wrapped agents a `CapabilityDescriptor` carries richer metadata:\n\n```typescript\ninterface CapabilityDescriptor {\n  name:          string;\n  description:   string;\n  inputSchema?:  Record<string, unknown>;   // JSON Schema\n  outputSchema?: Record<string, unknown>;   // JSON Schema\n  pricePerCall?: string;                    // \"0.05 USDC\" — triggers x402 gate\n}\n```\n\nWhen `pricePerCall` is set the HTTP server automatically returns HTTP 402 on calls that carry no payment proof.\n\n---\n\n### `POST /invoke` — AgentRequest / AgentResponse\n\n**Request**\n\n```typescript\ninterface AgentRequest {\n  requestId:  string;                        // UUID v4\n  from:       string;                        // caller agentId or wallet address\n  capability: string;                        // target capability name\n  payload:    Record<string, unknown>;       // capability-specific body\n  signature?: string;                        // EIP-712 signature over the envelope\n  timestamp?: number;                        // Unix ms — used to reject stale calls\n  sessionKey?: string;                       // delegated execution session\n  payment?:   { type, token, amount, txHash? };  // legacy payment field\n  x402?:      X402Payment;                   // x402 micropayment proof (auto-attached by X402Client)\n  stream?:    boolean;                       // true → use POST /invoke/stream (SSE)\n}\n```\n\n**Response**\n\n```typescript\ninterface AgentResponse {\n  requestId:  string;\n  status:     'success' | 'error' | 'payment_required';\n  result?:    Record<string, unknown>;       // present on success\n  errorMessage?: string;                     // present on error / payment_required\n  proof?:     string;                        // optional ZK proof or attestation\n  signature?: string;                        // EIP-712 response signature\n  timestamp?: number;                        // Unix ms\n  paymentRequirements?: X402PaymentRequirements[];  // present on payment_required\n}\n```\n\n**Wire example**\n\n```json\n// POST /invoke\n{ \"requestId\": \"a1b2\", \"from\": \"inai://agent/caller\", \"capability\": \"web_search\",\n  \"payload\": { \"query\": \"latest AI news\" }, \"timestamp\": 1711234567000 }\n\n// 200 OK\n{ \"requestId\": \"a1b2\", \"status\": \"success\",\n  \"result\": { \"content\": \"…\", \"sources\": [\"…\"] }, \"timestamp\": 1711234567120 }\n\n// 402 Payment Required\n{ \"requestId\": \"a1b2\", \"status\": \"payment_required\",\n  \"errorMessage\": \"Capability 'generate_image' requires payment.\",\n  \"paymentRequirements\": [{ \"network\": \"base\", \"asset\": \"0x833…\", \"maxAmountRequired\": \"50000\", \"payTo\": \"0xYour…\" }] }\n```\n\n---\n\n### Mesh Protocols — Heartbeat, Capability Exchange, Gossip\n\nThese are dispatched via the same `POST /invoke` endpoint using **reserved capability names**.\n\n#### Heartbeat — `__heartbeat`\n\n```typescript\n// AgentRequest.capability = \"__heartbeat\"\n// AgentRequest.payload cast to:\ninterface HeartbeatRequest {\n  senderId:  string;\n  timestamp: number;   // Unix ms\n  nonce?:    string;\n}\n\n// AgentResponse.result cast to:\ninterface HeartbeatResponse {\n  agentId:           string;\n  status:            'healthy' | 'degraded' | 'unhealthy';\n  timestamp:         number;\n  capabilitiesCount: number;\n  uptimeMs?:         number;\n  version?:          string;\n  nonce?:            string;   // echoed from request\n}\n```\n\n#### Capability Exchange — `__capabilities`\n\n```typescript\n// AgentRequest.payload cast to:\ninterface CapabilityExchangeRequest {\n  senderId:   string;\n  timestamp:  number;\n  includeAnr: boolean;   // true → response includes full DiscoveryEntry\n}\n\n// AgentResponse.result cast to:\ninterface CapabilityExchangeResponse {\n  agentId:      string;\n  capabilities: string[];\n  timestamp:    number;\n  anr?:         DiscoveryEntry;   // present when includeAnr was true\n}\n```\n\n#### Gossip — `POST /gossip`\n\nGossip is **fire-and-forget** — the server always returns `{ \"ok\": true }`. Messages propagate hop by hop; each hop decrements `ttl` and appends its own ID to `seenBy` to prevent loops.\n\n```typescript\ninterface GossipMessage {\n  type:        'announce' | 'revoke' | 'heartbeat' | 'query';\n  senderId:    string;\n  timestamp:   number;           // Unix ms\n  ttl:         number;           // decremented each hop; dropped at 0\n  seenBy:      string[];         // agent IDs that have already forwarded this\n  entry?:      DiscoveryEntry;   // present for announce / revoke\n  capability?: string;           // present for query\n  nonce?:      string;\n}\n```\n\n**Wire example**\n\n```json\n// POST /gossip\n{ \"type\": \"announce\", \"senderId\": \"inai://agent/0xABC\",\n  \"timestamp\": 1711234567000, \"ttl\": 3, \"seenBy\": [],\n  \"entry\": { \"agentId\": \"inai://agent/0xABC\", \"capabilities\": [\"web_search\"], … } }\n\n// 200 OK\n{ \"ok\": true }\n```\n\n---\n\n### Streaming — `POST /invoke/stream`\n\nSet `AgentRequest.stream = true` (or call `POST /invoke/stream` directly). The server responds with `Content-Type: text/event-stream` and emits SSE frames until the terminal `StreamEnd` frame.\n\n```typescript\n// Each incremental frame:\ninterface StreamChunk {\n  requestId: string;\n  type:      'chunk';\n  delta:     string;       // LLM token text or incremental output\n  result?:   unknown;      // optional partial structured result\n  sequence:  number;       // monotonically increasing per request\n  timestamp: number;\n}\n\n// Terminal frame:\ninterface StreamEnd {\n  requestId:    string;\n  type:         'end';\n  finalResult?: unknown;   // fully assembled result\n  error?:       string;    // set on abnormal termination\n  sequence:     number;\n  timestamp:    number;\n}\n```\n\n**Wire example**\n\n```\ndata: {\"type\":\"chunk\",\"requestId\":\"a1b2\",\"delta\":\"The \",\"sequence\":1,\"timestamp\":1711234567100}\n\ndata: {\"type\":\"chunk\",\"requestId\":\"a1b2\",\"delta\":\"latest \",\"sequence\":2,\"timestamp\":1711234567110}\n\ndata: {\"type\":\"end\",\"requestId\":\"a1b2\",\"finalResult\":{\"text\":\"The latest AI news…\"},\"sequence\":47,\"timestamp\":1711234567890}\n```\n\n---\n\n## Discovery Adapters\n\n| Adapter | Backend | Use case |\n|---|---|---|\n| `LocalDiscovery` | In-memory | Dev & testing |\n| `HttpDiscovery` | REST API | Centralised staging |\n| `GossipDiscovery` | HTTP fan-out + TTL | Decentralised mesh (no DHT required) |\n| `Libp2pDiscovery` | P2P / Kademlia DHT | Production mesh |\n| `OnChainDiscovery` | ERC-8004 smart contract | On-chain registry (optional) |\n\n---\n\n## Identity — DID by default, no wallet needed\n\n| Mode | DID format | How |\n|------|-----------|-----|\n| `local` (default) | `did:key:z...` | Key auto-created in `~/.inai/keystore/` |\n| `env` | `did:key:z...` | `INAI_AGENT_KEY=0x...` env var |\n| `raw` | `did:key:z...` | Pass key directly (secret manager, HSM) |\n| `erc8004` (optional) | `did:pkh:eip155:<chainId>:0x...` | On-chain wallet — adds verifiable ownership |\n\n```python\nfrom identity.provider import LocalKeystoreIdentity\n\nidentity = LocalKeystoreIdentity(name=\"my-agent\")\nprint(identity.agent_id())  # did:key:zQ3shXXX...\n\nconfig = PluginConfig(**identity.to_plugin_config_fields(), port=6174)\n```\n\n→ Full guide: **[docs/identity.md](docs/identity.md)**\n\n---\n\n## x402 Payments (opt-in)\n\nCharge other agents per capability in USDC on Base. Agents without pricing serve all requests free.\n\n```python\nfrom addons.x402.types import CapabilityPricing\n\nconfig = PluginConfig(\n    ...\n    x402_pricing={\n        \"generate_image\": CapabilityPricing.usdc_base(0.05, \"0xYourWallet\"),  # $0.05 per call\n    }\n)\n```\n\nThe HTTP server gate is automatic — no code needed in the agent. Callers receive an HTTP 402 with the full payment challenge if they haven't included a proof.\n\n→ Full guide: **[docs/x402.md](docs/x402.md)**\n\n### Runnable example — Google ADK + CrewAI\n\n[`examples/cross-framework/`](examples/cross-framework/) is a working end-to-end demo you can run right now:\n\n```bash\ngit clone https://github.com/ch4r10t33r/inai\npython3 examples/cross-framework/run.py\n```\n\nA **ResearchAgent** (Google ADK) and a **WriterAgent** (CrewAI) register with `LocalDiscovery`, then the orchestrator uses `AgentClient` to discover and call them in sequence — research findings flow from ADK into CrewAI without either agent knowing the other's framework. Runs in **demo mode by default** (no API keys needed); set `GOOGLE_API_KEY` and `OPENAI_API_KEY` to enable real LLMs.\n\n---\n\n## Cross-Framework Example\n\nA Google ADK research agent and a CrewAI writer agent discovering and calling each other with zero framework coupling:\n\n```bash\ncd examples/cross-framework\npip install -r requirements.txt\npython run_example.py\n```\n\n→ **[examples/cross-framework/](examples/cross-framework/)**\n\n---\n\n## AMP Specification Modules\n\n| Module | Description | Status |\n|---|---|---|\n| AMP-1 | Discovery (capability indexing + queries) | ✅ Stable |\n| AMP-2 | Interaction (request/response + routing) | ✅ Stable |\n| AMP-3 | Payments (x402 micropayments) | ✅ Add-on available |\n| AMP-4 | Delegation & multi-agent workflows | 🔜 Roadmap |\n\n---\n\n## Documentation\n\n| Doc | Description |\n|---|---|\n| [docs/overview.md](docs/overview.md) | Architecture and core concepts |\n| [docs/interfaces.md](docs/interfaces.md) | IAgent, AgentSession, IAgentClient — full interface reference |\n| [docs/identity.md](docs/identity.md) | DID identity — all modes, ERC-8004 optional |\n| [docs/x402.md](docs/x402.md) | x402 payment add-on |\n| [docs/mcp.md](docs/mcp.md) | MCP bridge — wrap MCP servers, expose as MCP server |\n| [docs/discovery.md](docs/discovery.md) | Discovery adapters |\n| [docs/libp2p.md](docs/libp2p.md) | P2P networking with libp2p + QUIC |\n| [docs/plugins.md](docs/plugins.md) | Framework adapters — LangGraph, Google ADK, CrewAI, OpenAI, Agno, LlamaIndex, smolagents, MCP |\n| [docs/differentiation.md](docs/differentiation.md) | How Inai differs from other frameworks |\n| [docs/vs-a2a.md](docs/vs-a2a.md) | Inai vs A2A — detailed technical comparison |\n\n---\n\n## Inai vs A2A\n\n> **A2A defines *how* two agents talk. Inai defines *how agents find each other, prove who they are, and transact* — problems A2A explicitly leaves out of scope.**\n\n| | A2A | Inai |\n|---|---|---|\n| Discovery | You need the agent's URL; bring your own registry | Kademlia DHT — find any agent by capability, no URL needed |\n| Identity | Self-declared Agent Card, no cryptographic verification | `did:key` — keypair-derived, portable, verifiable |\n| Encryption | TLS (transport only) | DIDComm v2 — end-to-end, message-level |\n| Payments | Out of scope | x402 micropayments built in |\n| Network | Requires public HTTPS endpoint | P2P via libp2p QUIC; circuit relay for agents behind NAT |\n| Routing | Call a specific URL | Query by capability; mesh returns candidates |\n| Task model | Rich state machine (8 states, artifacts, webhooks) | Simple `/invoke` + `/invoke/stream` |\n| Enterprise auth | OAuth2, OIDC, mTLS — first-class | Via HTTP layer |\n\nThey are **complementary, not competing.** A2A handles the task conversation; Inai handles discovery, identity, encryption, and payment. A Inai agent can expose an A2A-compatible endpoint — discovered via Inai's DHT and invoked using A2A's task protocol.\n\n→ Full analysis: **[docs/vs-a2a.md](docs/vs-a2a.md)**\n\n---\n\n## TODOs\n\n- [ ] More examples, tutorials and videos\n- [ ] Public hosted discovery registry\n- [ ] ERC-8004 delegation (`checkPermission`) on-chain enforcement\n- [ ] True token-by-token streaming (requires agent-side `streamRequest` method)\n\n---\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE)\n","readmeFilename":"README.md","_rev":"1-eb91c8e72dedb7079ff40dad7b1895ac"}