{"id":48131574,"url":"https://github.com/arielarevalo/opensage-acp","last_synced_at":"2026-04-04T16:40:00.416Z","repository":{"id":347248654,"uuid":"1190303953","full_name":"arielarevalo/opensage-acp","owner":"arielarevalo","description":"ACP adapter for OpenSage agent runtime","archived":false,"fork":false,"pushed_at":"2026-03-27T16:25:29.000Z","size":100,"stargazers_count":1,"open_issues_count":8,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-27T17:40:53.272Z","etag":null,"topics":["acp","acpx","agent","coding-agent","llm","mcp","opensage","python"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/arielarevalo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-24T06:41:08.000Z","updated_at":"2026-03-27T06:27:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/arielarevalo/opensage-acp","commit_stats":null,"previous_names":["arielarevalo/opensage-acp"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/arielarevalo/opensage-acp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arielarevalo%2Fopensage-acp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arielarevalo%2Fopensage-acp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arielarevalo%2Fopensage-acp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arielarevalo%2Fopensage-acp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/arielarevalo","download_url":"https://codeload.github.com/arielarevalo/opensage-acp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/arielarevalo%2Fopensage-acp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31405708,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["acp","acpx","agent","coding-agent","llm","mcp","opensage","python"],"created_at":"2026-04-04T16:39:59.742Z","updated_at":"2026-04-04T16:40:00.407Z","avatar_url":"https://github.com/arielarevalo.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# opensage-acp\n\n`opensage-acp` is an [Agent Client Protocol (ACP)](https://github.com/openclaw/acpx)\nadapter that exposes [opensage](https://github.com/opensage-agent/opensage) as\nan external coding agent reachable via\n[acpx](https://github.com/openclaw/acpx). It bridges the JSON-RPC 2.0 messages\nacpx sends on stdin/stdout to HTTP calls against a locally-managed `opensage\nweb` server, translating ACP sessions into opensage sessions, forwarding MCP\nserver configs, and streaming opensage's SSE responses back as ACP\n`session/update` notifications.\n\n## Architecture\n\n```\nacpx (or any ACP client)\n  |  JSON-RPC 2.0 / NDJSON on stdin/stdout\n  v\nopensage-acp  (this adapter)\n  |  HTTP + SSE to localhost:\u003cport\u003e\n  v\nopensage web  (one process per ACP session)\n  |  ADK Runner + OpenSageSession\n  v\nDocker sandboxes, Neo4j (optional), LLM backends (via LiteLLM)\n```\n\nacpx spawns `opensage-acp` as a subprocess and speaks JSON-RPC 2.0 over\nNDJSON on its stdin/stdout. For each ACP session, the adapter spawns a\ndedicated `opensage web` process on a free localhost port, generates a\nper-session TOML config (including any MCP servers the client requested),\nand proxies all prompts and responses.\n\n## Prerequisites\n\n- **Python \u003e= 3.12**\n- **Docker** — required by opensage for its sandbox backend\n- **opensage** — installed from source (not yet on PyPI):\n  ```bash\n  pip install git+https://github.com/opensage-agent/opensage.git\n  ```\n- **acpx \u003e= 0.3** — the ACP client that spawns this adapter (Node.js \u003e= 22):\n  ```bash\n  npm install -g acpx@latest\n  ```\n- **Neo4j** (optional) — for persistent memory across sessions\n\n## Installation\n\n```bash\npip install opensage-acp\n```\n\nOr, from source:\n\n```bash\npip install git+https://github.com/arielarevalo/opensage-acp.git\n```\n\nFor local development:\n\n```bash\npip install -e \".[dev]\"\n```\n\nThis installs the `opensage-acp` binary on your PATH.\n\n\u003e **Note:** opensage itself is not on PyPI and must be installed separately\n\u003e (see Prerequisites above). The adapter communicates with `opensage web` via\n\u003e HTTP — it does not import opensage as a Python library.\n\n## Quick Start\n\n### 1. Verify the adapter works (no opensage needed)\n\n```bash\nOPENSAGE_ECHO_MODE=1 acpx --agent opensage-acp exec \"hello\"\n```\n\nThis runs the adapter in echo mode — it echoes back whatever you send. If you\nsee `hello` echoed back with `[done] end_turn`, the ACP protocol layer works.\n\n### 2. Run with a real opensage agent\n\n```bash\n# Prepare a config (see examples/default_config.toml)\ncp examples/default_config.toml /tmp/my_config.toml\n# Edit: set your LLM model and API key env vars\n\n# Run\nOPENSAGE_AGENT_DIR=/path/to/your/agent \\\nOPENSAGE_CONFIG_TEMPLATE=/tmp/my_config.toml \\\nacpx --agent opensage-acp --verbose exec \"hello, what can you do?\"\n```\n\nThe adapter spawns `opensage web` on a free port, auto-discovers the app name,\ncreates a session, and streams the response back through acpx.\n\n## Configuration\n\n### Config file\n\nCreate `~/.config/opensage-acp/config.toml` (or set `OPENSAGE_CONFIG_FILE` to\nan alternate path):\n\n```toml\n[opensage-acp]\nagent_dir = \"/path/to/your/opensage/agent\"\nopensage_config_template = \"/path/to/your/opensage/config.toml\"\necho_mode = false\n```\n\n### Environment variables\n\nAll config values can be overridden via environment variables (take precedence\nover the TOML file):\n\n| Variable | Description |\n|---|---|\n| `OPENSAGE_AGENT_DIR` | Path to the opensage agent directory (required) |\n| `OPENSAGE_CONFIG_TEMPLATE` | Path to the base opensage `config.toml` (required for real usage) |\n| `OPENSAGE_ECHO_MODE` | Set to `1` to skip opensage and echo prompts back (for testing) |\n| `OPENSAGE_CONFIG_FILE` | Override path to the opensage-acp adapter config TOML |\n| `OPENSAGE_PORT_RANGE_START` | First port for opensage-web instances (default: `8100`) |\n| `OPENSAGE_TIMEOUT` | HTTP timeout in seconds for bridge calls (default: `120`) |\n| `OPENSAGE_LOG_LEVEL` | Logging level: `DEBUG`, `INFO`, `WARNING` (default: `WARNING`) |\n\n### opensage config.toml\n\nThe opensage `config.toml` is the agent's own configuration — LLM backend,\nAPI keys, Neo4j connection, MCP servers. A fully-documented template is\nprovided at [`examples/default_config.toml`](examples/default_config.toml).\nCopy it, fill in the placeholders, and point the adapter at it:\n\n```bash\nOPENSAGE_CONFIG_TEMPLATE=/path/to/your/config.toml opensage-acp\n```\n\nThe adapter deep-merges session-specific overrides on top of this file at\nruntime. Fields it manages automatically:\n- `agent_storage_path` — scoped per session to avoid collisions\n- `[mcp.services.*]` — injected from the `mcpServers[]` in `session/new`\n\nMCP servers configured in the template are available to all sessions; MCP\nservers passed dynamically by acpx in `session/new` are merged in at session\ncreation time.\n\nSee the [opensage documentation](https://github.com/opensage-agent/opensage)\nfor details on all config fields.\n\n## Usage\n\n### Register with acpx\n\nAdd `opensage-acp` as an agent in your acpx configuration:\n\n```json\n{\n  \"agents\": {\n    \"opensage\": {\n      \"command\": \"opensage-acp\",\n      \"env\": {\n        \"OPENSAGE_AGENT_DIR\": \"/path/to/agent\",\n        \"OPENSAGE_CONFIG_TEMPLATE\": \"/path/to/config.toml\"\n      }\n    }\n  }\n}\n```\n\n### Run directly (for debugging)\n\n```bash\nOPENSAGE_AGENT_DIR=/path/to/agent \\\nOPENSAGE_CONFIG_TEMPLATE=/path/to/config.toml \\\nopensage-acp\n```\n\nThe adapter reads JSON-RPC 2.0 messages from stdin and writes responses to\nstdout. In normal use this is managed entirely by acpx.\n\n## How It Works\n\n1. acpx spawns `opensage-acp` as a subprocess.\n2. On `session/new`, the adapter allocates a free port, writes a per-session\n   `config.toml` (merging any `mcpServers[]` from the ACP request), and spawns\n   `opensage web --agent \u003cdir\u003e --config \u003cgenerated_toml\u003e` on that port.\n3. On `session/prompt`, the adapter POSTs to `opensage web`'s `/run_sse`\n   endpoint and streams the SSE response back to acpx as ACP `session/update`\n   notifications.\n4. On `session/cancel`, the adapter POSTs to `/control/stop_turn`.\n5. On `session/load`, the adapter restarts `opensage web` with `--resume`,\n   restoring chat history from the persisted session snapshot.\n6. On session teardown, the adapter terminates the opensage-web process and\n   cleans up the generated config file.\n\n## Limitations \u0026 Known Issues\n\n- **One opensage-web process per session.** opensage's web server is\n  single-session by design. The adapter manages a pool of processes, one per\n  ACP session. Concurrent sessions require proportional memory and ports.\n\n- **MCP servers are fixed at session creation.** MCP server configurations\n  passed in `session/new` are baked into the per-session TOML. They cannot be\n  added or removed while the session is running without restarting the opensage\n  process (which loses in-memory state).\n\n- **Dynamic sub-agents are lost on adapter restart.** opensage writes sub-agent\n  metadata to disk but the reload code is not implemented in upstream opensage.\n  After an adapter restart and `session/load` resume, the LLM must recreate\n  its agent topology from chat history context.\n\n- **Neo4j memory is shared across all sessions.** opensage writes memories\n  tagged with a session ID but reads all memories without a session filter.\n  Two concurrent sessions see each other's memories. This is a feature for\n  a persistent long-lived assistant but a potential concern for multi-user\n  deployments.\n\n- **Dynamic agent reload not implemented upstream.** The\n  `DynamicAgentManager._load_persisted_agents_on_demand()` method in opensage\n  is commented out. Until opensage fixes this, sub-agent state does not survive\n  process boundaries.\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---|---|---|\n| `opensage-acp: command not found` | Not installed or not on PATH | `pip install opensage-acp` |\n| Timeout on `session/new` | opensage-web failed to start | Check `OPENSAGE_AGENT_DIR` is valid, Docker is running, port range is free |\n| `ConnectionRefusedError` | opensage-web process died | Check stderr; often a missing Docker image or invalid agent dir |\n| `Model X not found` | LLM model name not recognized by litellm | Check `config.toml` model name matches litellm format (`provider/model`) |\n| `App not found` on session creation | App name mismatch | The adapter auto-discovers the app name; ensure opensage-web starts cleanly |\n| Empty response | LLM API key missing | Set `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc. per your config |\n| `TOML decode error` | Invalid config template | Validate: `python -c \"import tomllib; tomllib.load(open('config.toml','rb'))\"` |\n\nFor verbose logging, set `OPENSAGE_LOG_LEVEL=DEBUG` to see spawn commands,\nhealth check attempts, app name discovery, and session ID mapping.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). For architectural context, see\n[docs/adr/](docs/adr/).\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farielarevalo%2Fopensage-acp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farielarevalo%2Fopensage-acp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farielarevalo%2Fopensage-acp/lists"}