370 lines
9.4 KiB
Markdown
370 lines
9.4 KiB
Markdown
# Gemini ACP Adapter
|
|
|
|
This document records the verified local behavior of `gemini --experimental-acp` and the recommended adapter design for integrating Gemini into `xworkmate-bridge` as a single-agent ACP backend.
|
|
|
|
## Goal
|
|
|
|
Keep the bridge semantics unchanged:
|
|
|
|
- `openclaw gateway` stays on gateway runtime forwarding
|
|
- `codex ACP` stays a single-agent ACP backend
|
|
- `opencode ACP` stays a single-agent ACP backend
|
|
- `gemini ACP` is added as another single-agent ACP backend
|
|
|
|
Gemini should therefore be integrated through an adapter layer, not through the gateway runtime path.
|
|
|
|
## Verified Local Findings
|
|
|
|
The local Gemini CLI binary supports an experimental ACP stdio mode:
|
|
|
|
```bash
|
|
gemini --experimental-acp
|
|
```
|
|
|
|
The bridge's current ACP RPC surface is not directly compatible with Gemini's ACP mode.
|
|
|
|
The following bridge-style methods were verified to be unsupported by Gemini ACP:
|
|
|
|
- `acp.capabilities`
|
|
- `session.start`
|
|
- `tools/list`
|
|
|
|
Example response:
|
|
|
|
```json
|
|
{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"\"Method not found\": acp.capabilities","data":{"method":"acp.capabilities"}}}
|
|
```
|
|
|
|
Gemini ACP does respond to `initialize`, and `protocolVersion` is required:
|
|
|
|
```bash
|
|
printf '%s\n' \
|
|
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1}}' \
|
|
| gemini --experimental-acp
|
|
```
|
|
|
|
Observed result:
|
|
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"result": {
|
|
"protocolVersion": 1,
|
|
"authMethods": [
|
|
{
|
|
"id": "oauth-personal",
|
|
"name": "Log in with Google",
|
|
"description": null
|
|
},
|
|
{
|
|
"id": "gemini-api-key",
|
|
"name": "Use Gemini API key",
|
|
"description": "Requires setting the `GEMINI_API_KEY` environment variable"
|
|
},
|
|
{
|
|
"id": "vertex-ai",
|
|
"name": "Vertex AI",
|
|
"description": null
|
|
}
|
|
],
|
|
"agentCapabilities": {
|
|
"loadSession": false,
|
|
"promptCapabilities": {
|
|
"image": true,
|
|
"audio": true,
|
|
"embeddedContext": true
|
|
},
|
|
"mcpCapabilities": {
|
|
"http": true,
|
|
"sse": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
Gemini ACP is reachable over stdio JSON-RPC, but it does not expose the same RPC methods expected by `xworkmate-bridge` today.
|
|
|
|
This means Gemini should be connected behind a small ACP adapter that:
|
|
|
|
1. speaks the bridge-facing ACP surface already used by `xworkmate-bridge`
|
|
2. speaks Gemini's experimental ACP surface on stdio
|
|
|
|
## Recommended Topology
|
|
|
|
```text
|
|
xworkmate app / agent manager
|
|
-> xworkmate-bridge
|
|
-> external provider: gemini
|
|
-> gemini ACP adapter
|
|
-> stdio child process: gemini --experimental-acp
|
|
```
|
|
|
|
The adapter should be registered in the bridge-owned provider catalog as the
|
|
`gemini` single-agent ACP backend:
|
|
|
|
- `providerId: "gemini"`
|
|
- `label: "Gemini"`
|
|
- `endpoint: http://127.0.0.1:<port>/acp/rpc` or `ws://127.0.0.1:<port>/acp`
|
|
- `enabled: true`
|
|
|
|
In production, `xworkmate-bridge` exposes `gemini` from its built-in provider
|
|
catalog rather than from app-driven sync.
|
|
|
|
## Adapter Responsibilities
|
|
|
|
The Gemini ACP adapter should own all protocol translation.
|
|
|
|
### Bridge-facing side
|
|
|
|
Expose the same single-agent ACP methods the bridge already forwards:
|
|
|
|
- `acp.capabilities`
|
|
- `session.start`
|
|
- `session.message`
|
|
- `session.cancel`
|
|
- `session.close`
|
|
|
|
### Gemini-facing side
|
|
|
|
Launch one Gemini ACP stdio child process:
|
|
|
|
```bash
|
|
gemini --experimental-acp
|
|
```
|
|
|
|
Then initialize it first:
|
|
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": "init-1",
|
|
"method": "initialize",
|
|
"params": {
|
|
"protocolVersion": 1,
|
|
"clientInfo": {
|
|
"name": "xworkmate-gemini-adapter",
|
|
"version": "0.1.0"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
The adapter should cache the initialization result and derive bridge-side capability information from it.
|
|
|
|
## Bridge-to-Gemini Mapping
|
|
|
|
The exact post-`initialize` Gemini method surface still needs to be enumerated. Until that discovery is finished, the adapter contract should be structured as follows.
|
|
|
|
### `acp.capabilities`
|
|
|
|
Return a bridge-compatible synthesized response. Suggested response:
|
|
|
|
```json
|
|
{
|
|
"singleAgent": true,
|
|
"multiAgent": false,
|
|
"providers": ["gemini"],
|
|
"capabilities": {
|
|
"single_agent": true,
|
|
"multi_agent": false,
|
|
"providers": ["gemini"]
|
|
},
|
|
"upstream": {
|
|
"protocolVersion": 1,
|
|
"promptCapabilities": {
|
|
"image": true,
|
|
"audio": true,
|
|
"embeddedContext": true
|
|
},
|
|
"mcpCapabilities": {
|
|
"http": true,
|
|
"sse": true
|
|
},
|
|
"authMethods": [
|
|
"oauth-personal",
|
|
"gemini-api-key",
|
|
"vertex-ai"
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### `session.start`
|
|
|
|
Suggested adapter behavior:
|
|
|
|
1. Ensure Gemini stdio process is started
|
|
2. Ensure `initialize` has succeeded
|
|
3. Create adapter-local session state keyed by `sessionId`
|
|
4. Translate the bridge prompt and metadata into the Gemini request format
|
|
5. Return a bridge-compatible response envelope
|
|
|
|
If Gemini requires a different request method than bridge `session.start`, the translation should remain fully internal to the adapter.
|
|
|
|
### `session.message`
|
|
|
|
Suggested adapter behavior:
|
|
|
|
1. Reuse adapter-local session state
|
|
2. Translate `taskPrompt`, attachments, and working directory metadata as supported
|
|
3. Stream or collect Gemini output
|
|
4. Repackage into the bridge's current single-agent result shape
|
|
|
|
### `session.cancel` and `session.close`
|
|
|
|
Because Gemini reported `loadSession: false`, the first adapter version should assume sessions are adapter-local, not durable upstream sessions.
|
|
|
|
Recommended behavior:
|
|
|
|
- `session.cancel`: cancel the current in-flight Gemini request or kill and restart the child process if fine-grained cancellation is unavailable
|
|
- `session.close`: drop adapter-local state and optionally recycle the child process
|
|
|
|
## Startup Configuration
|
|
|
|
## Environment
|
|
|
|
At minimum, support these startup modes:
|
|
|
|
### API key mode
|
|
|
|
```bash
|
|
export GEMINI_API_KEY=your_api_key
|
|
gemini --experimental-acp
|
|
```
|
|
|
|
### OAuth mode
|
|
|
|
Use the Gemini CLI's own authenticated environment, then run:
|
|
|
|
```bash
|
|
gemini --experimental-acp
|
|
```
|
|
|
|
### Vertex AI mode
|
|
|
|
Run Gemini CLI in an environment already configured for Vertex AI auth, then launch:
|
|
|
|
```bash
|
|
gemini --experimental-acp
|
|
```
|
|
|
|
## Recommended adapter process contract
|
|
|
|
Example adapter startup:
|
|
|
|
```bash
|
|
./build/bin/xworkmate-go-core gemini-acp-adapter \
|
|
--listen 127.0.0.1:8791 \
|
|
--gemini-bin /opt/homebrew/bin/gemini \
|
|
--gemini-args="--experimental-acp"
|
|
```
|
|
|
|
Recommended adapter environment:
|
|
|
|
```bash
|
|
XWORKMATE_GEMINI_BIN=/opt/homebrew/bin/gemini
|
|
XWORKMATE_GEMINI_ARGS=--experimental-acp
|
|
XWORKMATE_GEMINI_INIT_PROTOCOL_VERSION=1
|
|
```
|
|
|
|
The implemented adapter exposes:
|
|
|
|
- `POST /acp/rpc`
|
|
- `GET /acp` WebSocket ACP
|
|
|
|
Supported adapter methods:
|
|
|
|
- `acp.capabilities`
|
|
- `session.start`
|
|
- `session.message`
|
|
- `session.cancel`
|
|
- `session.close`
|
|
- `gemini.initialize`
|
|
- `gemini.raw`
|
|
|
|
`session.start` and `session.message` now use a compatibility layer by default:
|
|
|
|
- the adapter still initializes Gemini ACP first so `acp.capabilities` remains grounded in the real upstream ACP surface
|
|
- if `GEMINI_ADAPTER_UPSTREAM_METHOD` is unset, session traffic runs through adapter-local prompt mode
|
|
- the adapter keeps session-local history keyed by `sessionId`
|
|
- `session.start` resets adapter-local history for that session
|
|
- `session.message` replays prior user turns plus the new turn as one prompt to the Gemini CLI
|
|
- the adapter returns a bridge-compatible single-agent payload with `output`, `provider`, `mode`, and `upstreamMethod: "prompt"`
|
|
- `session.close` drops adapter-local state
|
|
|
|
This default exists because the verified Gemini ACP upstream did not expose bridge-compatible `session.start` / `session.message` methods during testing.
|
|
|
|
If Gemini ACP later gains a compatible conversation method, you can override the forwarded method name with:
|
|
|
|
```bash
|
|
export GEMINI_ADAPTER_UPSTREAM_METHOD=your-discovered-gemini-method
|
|
```
|
|
|
|
## Bridge Provider Catalog Example
|
|
|
|
Once the adapter is running, expose it from the bridge-owned provider catalog
|
|
as the `gemini` backend:
|
|
|
|
```json
|
|
{
|
|
"providerId": "gemini",
|
|
"label": "Gemini",
|
|
"endpoint": "http://127.0.0.1:8791/acp/rpc",
|
|
"enabled": true
|
|
}
|
|
```
|
|
|
|
Example local startup and sync flow:
|
|
|
|
```bash
|
|
./build/bin/xworkmate-go-core gemini-acp-adapter \
|
|
--listen 127.0.0.1:8791 \
|
|
--gemini-bin /opt/homebrew/bin/gemini \
|
|
--gemini-args="--experimental-acp"
|
|
```
|
|
|
|
Then wire the bridge startup/runtime config so the built-in `gemini` catalog
|
|
entry points at this adapter endpoint:
|
|
|
|
```json
|
|
{
|
|
"providerId": "gemini",
|
|
"label": "Gemini",
|
|
"endpoint": "http://127.0.0.1:8791",
|
|
"enabled": true
|
|
}
|
|
```
|
|
|
|
## Recommended First Implementation Scope
|
|
|
|
The first adapter milestone should stay intentionally small:
|
|
|
|
1. Start `gemini --experimental-acp`
|
|
2. Send `initialize`
|
|
3. Expose bridge-compatible `acp.capabilities`
|
|
4. Translate one synchronous prompt path for `session.start` and `session.message`
|
|
5. Return bridge-compatible `output`, `provider`, `mode`, and `turnId`
|
|
|
|
Do not block the first version on:
|
|
|
|
- durable upstream session restore
|
|
- multimodal attachment parity
|
|
- complex streaming semantics
|
|
- full cancellation fidelity
|
|
|
|
## Next Validation Tasks
|
|
|
|
Before implementing the adapter, enumerate Gemini's post-`initialize` callable methods and request schema.
|
|
|
|
Recommended next probes:
|
|
|
|
- inspect whether Gemini sends notifications after `initialize`
|
|
- test likely conversation methods after `initialize`
|
|
- determine whether the protocol requires explicit auth selection or out-of-band auth only
|
|
- verify whether one process supports multiple sequential requests safely
|
|
- verify whether one process supports concurrent requests or must be serialized
|