Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ uv run opencode-a2a serve
Run the default validation baseline before opening a PR:

```bash
uv run pre-commit run --all-files
uv run pytest
bash ./scripts/doctor.sh
```

`doctor.sh` is the primary repository validation entrypoint. It currently runs locked-environment sync, dependency compatibility checks, `pre-commit`, `mypy`, `pytest`, the repository coverage gate, package build, and a built-wheel smoke test.

If you change shell scripts, also run `bash -n` on each modified script, for example:

```bash
Expand All @@ -63,7 +64,7 @@ bash ./scripts/conformance.sh

Treat that output as investigation input. Do not fold it into `doctor.sh` or the default CI quality gate unless the repository explicitly decides to promote a specific experiment into a maintained policy.

If you change extension methods, extension metadata, or Agent Card/OpenAPI contract surfaces, also run:
If you change extension methods, extension metadata, or Agent Card/OpenAPI contract surfaces, also make sure the targeted contract checks stay green:

```bash
uv run pytest tests/contracts/test_extension_contract_consistency.py
Expand Down Expand Up @@ -107,7 +108,7 @@ Update docs together with code whenever you change:
- user-facing request or response shapes
- operational scripts

Keep compatibility guidance centralized in [docs/guide.md](docs/guide.md) unless a new standalone document is clearly necessary.
Keep usage details in [docs/guide.md](docs/guide.md) and compatibility-sensitive stability guidance in [docs/compatibility.md](docs/compatibility.md).

When changing extension contracts, update [`src/opencode_a2a/contracts/extensions.py`](src/opencode_a2a/contracts/extensions.py) first and keep these generated/documented surfaces aligned:

Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ curl http://127.0.0.1:8000/.well-known/agent-card.json
- Declared supported protocol lines: `0.3`, `1.0`
- `0.3` is the stable interoperability baseline for the current runtime surface.
- `1.0` currently covers version negotiation plus protocol-aware JSON-RPC and REST error shaping, while transport payloads, enums, pagination, signatures, and interface-level protocol declarations still follow the shipped SDK baseline.
- The detailed compatibility matrix and machine-readable support boundary are documented in [`docs/guide.md`](docs/guide.md).
- The detailed compatibility matrix and machine-readable support boundary are documented in [`docs/guide.md`](docs/guide.md) and [`docs/compatibility.md`](docs/compatibility.md).

## Peering Node / Outbound Access

Expand Down Expand Up @@ -162,7 +162,11 @@ Read before deployment:

## Further Reading

- [docs/architecture.md](docs/architecture.md) Service responsibility boundaries and request flow.
- [docs/maintainer-architecture.md](docs/maintainer-architecture.md) Internal module boundaries and maintainer call chains.
- [docs/compatibility.md](docs/compatibility.md) Compatibility-sensitive surface and contract-honesty guidance.
- [docs/guide.md](docs/guide.md) Usage guide, transport details, streaming behavior, extensions, and examples.
- [docs/conformance.md](docs/conformance.md) External TCK experiment workflow and artifact handling.
- [SECURITY.md](SECURITY.md) Threat model, deployment caveats, and vulnerability disclosure guidance.

## Development
Expand Down
101 changes: 101 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Architecture Guide

This document explains what `opencode-a2a` is responsible for, what remains inside OpenCode, and how requests move through the adapter boundary.

## System Role

`opencode-a2a` is an adapter layer between A2A clients and an OpenCode runtime.

It is responsible for:

- exposing A2A-facing HTTP+JSON and JSON-RPC endpoints
- normalizing transport, streaming, session, and interrupt contracts
- applying authentication, logging, persistence, and deployment-side guardrails
- hosting an outbound A2A client for peer calls triggered by CLI usage or `a2a_call`

It is not responsible for:

- replacing OpenCode's own provider or model selection internals
- hard multi-tenant isolation inside one shared deployment by default
- acting as a general OpenCode process supervisor

## Adapter Layers

```mermaid
flowchart LR
Client["A2A Client / Hub / Gateway"] --> Gateway["opencode-a2a"]
Gateway --> Contracts["A2A contracts\ntransport/session/interrupt"]
Gateway --> Ops["Auth / logging / persistence / deployment boundary"]
Contracts --> OpenCode["OpenCode runtime"]
Ops --> OpenCode
Gateway --> Peer["Embedded outbound A2A client"]
```

This view emphasizes service responsibility boundaries rather than internal module structure. The root [README](../README.md) keeps the overview path for first-time readers, while [maintainer-architecture.md](./maintainer-architecture.md) covers module boundaries and request call chains for contributors.

## Request Flow

### Standard send/stream flow

1. A client calls the REST or JSON-RPC endpoint.
2. FastAPI middleware validates auth, request size, protocol version, and logging policy.
3. The adapter maps transport payloads into the OpenCode-facing execution path.
4. The execution layer calls the upstream OpenCode runtime and consumes its stream or unary response.
5. The service maps the result back into shared A2A-facing task, message, and stream event contracts.

### Streaming flow

For streaming requests, the adapter does more than simple passthrough:

- classifies stream blocks into shared types such as `text`, `reasoning`, and `tool_call`
- preserves stable event and message identity where possible
- emits shared interrupt lifecycle state
- avoids duplicate final snapshots when streaming already produced the final text

Detailed streaming contract: [Usage Guide](guide.md)

### Session flow

The service keeps a shared session continuation contract around `metadata.shared.session.id` and adapter-derived `contextId`, so clients can continue work without binding directly to raw OpenCode session IDs.

### Outbound peer flow

The same process can also act as an embedded A2A client:

- CLI calls route through the local client facade
- server-side `a2a_call` tool execution uses the same outbound client settings
- outbound auth, timeouts, and transport preferences are configured independently from inbound auth

## Boundary Model

The adapter improves the runtime boundary, but it is not a full trust-boundary replacement.

### What the adapter boundary helps with

- stable A2A-facing contract shape
- auth enforcement on A2A entrypoints
- payload logging controls
- lightweight persistence for SDK task rows plus adapter-managed runtime state
- compatibility metadata for clients and operators

### What still belongs to the OpenCode runtime boundary

- provider credential consumption
- workspace side effects
- upstream model/provider behavior
- host-level process supervision and isolation

That is why deployments should still be treated as trusted or controlled unless stronger isolation is added outside this repository.

## Documentation Split

Use the docs by responsibility:

- [README](../README.md): project overview, install/start path, and entry navigation
- [Compatibility Guide](compatibility.md): compatibility-sensitive surface and stability expectations
- [Usage Guide](guide.md): runtime configuration, transport contracts, extensions, and examples
- [Maintainer Architecture Guide](maintainer-architecture.md): internal module boundaries, request call chains, and persistence touchpoints
- [Extension Specifications](extension-specifications.md): stable extension URI/spec index and disclosure policy
- [Conformance Notes](conformance.md): external TCK experiment workflow
- [Contributing Guide](../CONTRIBUTING.md): contributor workflow and validation
- [Security Policy](../SECURITY.md): threat model and disclosure guidance
147 changes: 147 additions & 0 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Compatibility Guide

This document explains the compatibility promises `opencode-a2a` currently tries to uphold for A2A consumers, operators, and maintainers.

## Runtime Support

- Python versions: 3.11, 3.12, 3.13
- A2A SDK line: `0.3.x`
- Default advertised protocol line: `0.3`
- Declared supported protocol lines: `0.3`, `1.0`

The repository pins the SDK version in `pyproject.toml`. Upgrade the SDK deliberately rather than relying on floating dependency resolution.

## Contract Honesty

Machine-readable discovery surfaces must reflect actual runtime behavior:

- public Agent Card
- authenticated extended card
- OpenAPI metadata
- JSON-RPC wire contract
- compatibility profile

If runtime support is not actually implemented, do not publish it as a supported machine-readable capability.

Consumer guidance:

- Treat the core A2A send / stream / task methods as the portable baseline.
- Treat `urn:a2a:*` entries in this repository as shared repo-family conventions, not as a claim that they are part of the A2A core baseline.
- Treat `opencode.*` methods and `metadata.opencode.*` fields as provider-private OpenCode control and discovery surfaces layered on top of the portable A2A baseline.
- Treat [extension-specifications.md](./extension-specifications.md) as the stable URI/spec index, not as the main usage guide.

## Normative Sources

When docs or reference material disagree, treat these as normative in this order:

- runtime behavior validated by tests
- machine-readable discovery output such as Agent Card, authenticated extended card, and OpenAPI metadata
- repository-owned docs in `README.md`, `docs/`, and `CONTRIBUTING.md`

External TCK runs and local conformance experiments are investigation inputs. They do not override the repository's declared contract by themselves.

## Compatibility-Sensitive Surface

This repository still ships as an alpha project. Within that alpha line, these declared surfaces should not drift silently:

- core A2A send / stream / task methods
- version negotiation and protocol-aware error shaping
- shared session-binding metadata
- shared model-selection metadata
- shared streaming metadata
- declared custom JSON-RPC extension methods
- authenticated extended card and OpenAPI wire-contract metadata

Changes to those surfaces should be treated as compatibility-sensitive and should include corresponding test updates.

Service-level behavior layered on top of those core methods should also be declared explicitly when interoperability depends on it. Current examples:

- `tasks/resubscribe` replay-once behavior for terminal updates
- first-terminal-state-wins task persistence policy
- task-scoped `acceptedOutputModes` negotiation persistence across send / stream / get / resubscribe
- request-body rejection behavior for oversized transport payloads

## Deployment Profile

The current service profile is intentionally:

- single-tenant
- shared-workspace
- adapter boundary around one OpenCode deployment

One deployed instance should be treated as a single-tenant trust boundary, not as a secure multi-tenant runtime boundary.

Execution-environment boundary fields published through the runtime profile are declarative deployment metadata. They are not promises that every host-side approval, sandbox escalation, or filesystem change will be reflected live per request.

## Persistence Compatibility

Task durability is deployment-dependent:

- `A2A_TASK_STORE_BACKEND=database` preserves SDK task rows plus adapter-managed session and interrupt state across restarts
- `A2A_TASK_STORE_BACKEND=memory` keeps the service in an ephemeral development profile

Task-store behavior that should remain stable for clients:

- once a task reaches a terminal state, later conflicting writes are dropped on a first-terminal-state-wins basis
- task-store I/O failures are surfaced as stable service errors instead of leaking backend-specific exceptions
- accepted output-mode negotiation for a task is persisted with the task so later reads keep the same filtered output contract
- adapter-managed migrations only own adapter state tables; SDK-managed task schema remains SDK-owned

The default SQLite-first profile is intended for local or controlled single-instance deployments. Wider SQLAlchemy dialect compatibility should be treated as implementation latitude rather than a strong public promise unless explicitly documented later.

## Extension Stability

- Shared metadata and extension contracts should stay synchronized across Agent Card, OpenAPI, and runtime behavior.
- Public Agent Card should stay intentionally minimal. Detailed extension params belong in the authenticated extended card and OpenAPI, not back in the anonymous discovery surface.
- Deployment-conditional methods must be declared as conditional rather than silently disappearing.
- `opencode.sessions.prompt_async` input-part passthrough is compatibility-sensitive. Changes to supported part types, passthrough field semantics, or rejection behavior should be treated as wire-level changes.
- `opencode.sessions.shell` is compatibility-sensitive as a deployment-conditional shell snapshot surface. It should not silently widen into a general interactive shell API.
- `opencode.workspaces.*` and `opencode.worktrees.*` are boundary-sensitive and should remain explicitly provider-private, operator-scoped, and deployment-conditional where applicable.
- Interrupt callback and recovery methods are compatibility-sensitive because clients may depend on request ID lifecycle, expiry semantics, and identity scoping.
- Agent Card media modes and `acceptedOutputModes` handling are compatibility-sensitive. Changes to declared chat modes, to task-scoped negotiation persistence, or to `DataPart` -> `TextPart` downgrade behavior should be treated as wire-level changes.
- Agent Card and OpenAPI publication of `protocol_compatibility`, `service_behaviors`, and runtime feature toggles is compatibility-sensitive discoverability surface.

## Extension Boundary Governance

When evaluating or evolving `opencode.*` methods, this repository uses the following rules:

- The adapter may document, validate, route, and normalize stable upstream-facing behavior, but it should not grow into a general replacement for upstream private runtime internals or host-level control planes.
- New `opencode.*` methods default to provider-private extension status.
- Read-only discovery, compatibility-preserving projections, and low-risk control methods are preferred over stronger mutating or destructive provider controls.
- A2A core object mappings should be used only for stable, low-ambiguity read projections.
- Subtask/subagent fan-out, task-tool internals, and similar upstream execution mechanisms should stay framed as upstream runtime behavior even when passthrough compatibility exists.

Each new extension proposal should answer:

- what client value exists beyond the current chat/session flow?
- is the upstream behavior stable enough to carry as a maintained contract?
- should the surface be provider-private, deployment-conditional, or excluded?
- are authorization and destructive-side-effect boundaries enforceable?
- can the result shape avoid overfitting OpenCode internals into fake A2A core semantics?

## Extension Taxonomy

This repository distinguishes between three layers:

- core A2A surface
- standard send / stream / task methods
- shared extensions
- repo-family conventions such as session binding, model selection, stream hints, and interrupt callbacks
- OpenCode-specific extensions
- `opencode.*` JSON-RPC methods plus `metadata.opencode.*`

Important note:

- `urn:a2a:*` extension URIs used here should be read as shared conventions in this repository family.
- They are not a claim that those extensions are part of the A2A core baseline.
- `opencode.*` methods are intentionally product-specific. They improve OpenCode-aware workflows but should not be assumed to transfer unchanged to unrelated A2A agents.

## Non-Goals

This repository does not currently promise:

- hard multi-tenant isolation inside one instance
- generic provider-auth orchestration on behalf of OpenCode
- a claim that all declared `1.0` protocol surfaces are fully implemented beyond the documented compatibility matrix

Those areas may evolve later, but they should not be implied by current machine-readable discovery output.
2 changes: 1 addition & 1 deletion docs/extension-specifications.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Extension Specifications

This document is the stable specification surface referenced by the extension URIs published in the Agent Card. It is intentionally a compact URI/spec index, not the main consumer guide. For runtime behavior, request/response examples, and client integration guidance, see [`guide.md`](./guide.md).
This document is the stable specification surface referenced by the extension URIs published in the Agent Card. It is intentionally a compact URI/spec index, not the main consumer guide. For runtime behavior, request/response examples, and client integration guidance, see [`guide.md`](./guide.md). For compatibility-sensitive surface and contract-honesty guidance, see [`compatibility.md`](./compatibility.md).

## SDK Compatibility Note

Expand Down
Loading