Skip to main content

ACP adapter that exposes opensage as an external coding agent via acpx

Project description

opensage-acp

opensage-acp is an Agent Client Protocol (ACP) adapter that exposes opensage as an external coding agent reachable via acpx. It bridges the JSON-RPC 2.0 messages acpx sends on stdin/stdout to HTTP calls against a locally-managed opensage web server, translating ACP sessions into opensage sessions, forwarding MCP server configs, and streaming opensage's SSE responses back as ACP session/update notifications.

Architecture

acpx (or any ACP client)
  |  JSON-RPC 2.0 / NDJSON on stdin/stdout
  v
opensage-acp  (this adapter)
  |  HTTP + SSE to localhost:<port>
  v
opensage web  (one process per ACP session)
  |  ADK Runner + OpenSageSession
  v
Docker sandboxes, Neo4j (optional), LLM backends (via LiteLLM)

acpx spawns opensage-acp as a subprocess and speaks JSON-RPC 2.0 over NDJSON on its stdin/stdout. For each ACP session, the adapter spawns a dedicated opensage web process on a free localhost port, generates a per-session TOML config (including any MCP servers the client requested), and proxies all prompts and responses.

Prerequisites

  • Python >= 3.12
  • Docker — required by opensage for its sandbox backend
  • opensage — installed from source (not yet on PyPI):
    pip install git+https://github.com/opensage-agent/opensage.git
    
  • acpx >= 0.3 — the ACP client that spawns this adapter (Node.js >= 22):
    npm install -g acpx@latest
    
  • Neo4j (optional) — for persistent memory across sessions

Installation

pip install opensage-acp

Or, from source:

pip install git+https://github.com/arielarevalo/opensage-acp.git

For local development:

pip install -e ".[dev]"

This installs the opensage-acp binary on your PATH.

Note: opensage itself is not on PyPI and must be installed separately (see Prerequisites above). The adapter communicates with opensage web via HTTP — it does not import opensage as a Python library.

Quick Start

1. Verify the adapter works (no opensage needed)

OPENSAGE_ECHO_MODE=1 acpx --agent opensage-acp exec "hello"

This runs the adapter in echo mode — it echoes back whatever you send. If you see hello echoed back with [done] end_turn, the ACP protocol layer works.

2. Run with a real opensage agent

# Prepare a config (see examples/default_config.toml)
cp examples/default_config.toml /tmp/my_config.toml
# Edit: set your LLM model and API key env vars

# Run
OPENSAGE_AGENT_DIR=/path/to/your/agent \
OPENSAGE_CONFIG_TEMPLATE=/tmp/my_config.toml \
acpx --agent opensage-acp --verbose exec "hello, what can you do?"

The adapter spawns opensage web on a free port, auto-discovers the app name, creates a session, and streams the response back through acpx.

Configuration

Config file

Create ~/.config/opensage-acp/config.toml (or set OPENSAGE_CONFIG_FILE to an alternate path):

[opensage-acp]
agent_dir = "/path/to/your/opensage/agent"
opensage_config_template = "/path/to/your/opensage/config.toml"
echo_mode = false

Environment variables

All config values can be overridden via environment variables (take precedence over the TOML file):

Variable Description
OPENSAGE_AGENT_DIR Path to the opensage agent directory (required)
OPENSAGE_CONFIG_TEMPLATE Path to the base opensage config.toml (required for real usage)
OPENSAGE_ECHO_MODE Set to 1 to skip opensage and echo prompts back (for testing)
OPENSAGE_CONFIG_FILE Override path to the opensage-acp adapter config TOML
OPENSAGE_PORT_RANGE_START First port for opensage-web instances (default: 8100)
OPENSAGE_TIMEOUT HTTP timeout in seconds for bridge calls (default: 120)
OPENSAGE_LOG_LEVEL Logging level: DEBUG, INFO, WARNING (default: WARNING)

opensage config.toml

The opensage config.toml is the agent's own configuration — LLM backend, API keys, Neo4j connection, MCP servers. A fully-documented template is provided at examples/default_config.toml. Copy it, fill in the placeholders, and point the adapter at it:

OPENSAGE_CONFIG_TEMPLATE=/path/to/your/config.toml opensage-acp

The adapter deep-merges session-specific overrides on top of this file at runtime. Fields it manages automatically:

  • agent_storage_path — scoped per session to avoid collisions
  • [mcp.services.*] — injected from the mcpServers[] in session/new

MCP servers configured in the template are available to all sessions; MCP servers passed dynamically by acpx in session/new are merged in at session creation time.

See the opensage documentation for details on all config fields.

Usage

Register with acpx

Add opensage-acp as an agent in your acpx configuration:

{
  "agents": {
    "opensage": {
      "command": "opensage-acp",
      "env": {
        "OPENSAGE_AGENT_DIR": "/path/to/agent",
        "OPENSAGE_CONFIG_TEMPLATE": "/path/to/config.toml"
      }
    }
  }
}

Run directly (for debugging)

OPENSAGE_AGENT_DIR=/path/to/agent \
OPENSAGE_CONFIG_TEMPLATE=/path/to/config.toml \
opensage-acp

The adapter reads JSON-RPC 2.0 messages from stdin and writes responses to stdout. In normal use this is managed entirely by acpx.

How It Works

  1. acpx spawns opensage-acp as a subprocess.
  2. On session/new, the adapter allocates a free port, writes a per-session config.toml (merging any mcpServers[] from the ACP request), and spawns opensage web --agent <dir> --config <generated_toml> on that port.
  3. On session/prompt, the adapter POSTs to opensage web's /run_sse endpoint and streams the SSE response back to acpx as ACP session/update notifications.
  4. On session/cancel, the adapter POSTs to /control/stop_turn.
  5. On session/load, the adapter restarts opensage web with --resume, restoring chat history from the persisted session snapshot.
  6. On session teardown, the adapter terminates the opensage-web process and cleans up the generated config file.

Limitations & Known Issues

  • One opensage-web process per session. opensage's web server is single-session by design. The adapter manages a pool of processes, one per ACP session. Concurrent sessions require proportional memory and ports.

  • MCP servers are fixed at session creation. MCP server configurations passed in session/new are baked into the per-session TOML. They cannot be added or removed while the session is running without restarting the opensage process (which loses in-memory state).

  • Dynamic sub-agents are lost on adapter restart. opensage writes sub-agent metadata to disk but the reload code is not implemented in upstream opensage. After an adapter restart and session/load resume, the LLM must recreate its agent topology from chat history context.

  • Neo4j memory is shared across all sessions. opensage writes memories tagged with a session ID but reads all memories without a session filter. Two concurrent sessions see each other's memories. This is a feature for a persistent long-lived assistant but a potential concern for multi-user deployments.

  • Dynamic agent reload not implemented upstream. The DynamicAgentManager._load_persisted_agents_on_demand() method in opensage is commented out. Until opensage fixes this, sub-agent state does not survive process boundaries.

Troubleshooting

Symptom Cause Fix
opensage-acp: command not found Not installed or not on PATH pip install opensage-acp
Timeout on session/new opensage-web failed to start Check OPENSAGE_AGENT_DIR is valid, Docker is running, port range is free
ConnectionRefusedError opensage-web process died Check stderr; often a missing Docker image or invalid agent dir
Model X not found LLM model name not recognized by litellm Check config.toml model name matches litellm format (provider/model)
App not found on session creation App name mismatch The adapter auto-discovers the app name; ensure opensage-web starts cleanly
Empty response LLM API key missing Set ANTHROPIC_API_KEY, OPENAI_API_KEY, etc. per your config
TOML decode error Invalid config template Validate: python -c "import tomllib; tomllib.load(open('config.toml','rb'))"

For verbose logging, set OPENSAGE_LOG_LEVEL=DEBUG to see spawn commands, health check attempts, app name discovery, and session ID mapping.

Contributing

See CONTRIBUTING.md. For architectural context, see docs/adr/.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opensage_acp-0.1.0.tar.gz (99.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opensage_acp-0.1.0-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file opensage_acp-0.1.0.tar.gz.

File metadata

  • Download URL: opensage_acp-0.1.0.tar.gz
  • Upload date:
  • Size: 99.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opensage_acp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 edf9f19c1feb6f1b59c60d304059bd2cb0cfba4fdf622d2e9bf2fa6acae2d6cc
MD5 770a9ebbdbe154de45caee39479618a7
BLAKE2b-256 8ca8fe768cb7d1f359d499a78bf6956a46ff25bfd0219c2319173e102766a44e

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensage_acp-0.1.0.tar.gz:

Publisher: cd.yml on arielarevalo/opensage-acp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file opensage_acp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: opensage_acp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for opensage_acp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2b368aaf0f9b8ac879d65201747b5bcb1b662d65344f421bb8b14aff0b3654e4
MD5 70c1244f5c40b80fc40a3e2c4ee7548b
BLAKE2b-256 74582f3b38d7c0cda029552157b4aa698c4bc1a4fbea47e1d8f394203fb2a82e

See more details on using hashes here.

Provenance

The following attestation bundles were made for opensage_acp-0.1.0-py3-none-any.whl:

Publisher: cd.yml on arielarevalo/opensage-acp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page