File size: 5,404 Bytes
6918275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# OpenOperator A2A Integration Contract

OpenOperator exposes autonomous integration points using the authenticated FastA2A JSON-RPC protocol over HTTPS.

## Service Discovery

Begin integration by requesting these read-only routes:

- **Health:** `GET /health`
- **OpenOperator docs:** `GET /docs`
- **Machine-readable A2A docs:** `GET /docs/a2a.json`
- **Agent Card:** `GET /a2a/.well-known/agent.json`
- **Compatibility Agent Card:** `GET /a2a/.well-known/agent-card.json`
- **FastA2A library docs:** `GET /a2a/docs`

## A2A JSON-RPC

- **A2A JSON-RPC Endpoint:** `POST /a2a/`

### Critical Routing Warning

`message/send`, `tasks/get`, and `tasks/cancel` are **JSON-RPC method names sent in the POST body**.

They are **NOT HTTP URL paths**.

Explicitly mark these as incorrect and they will return HTTP 404/405:
- `/a2a/message/send`
- `/a2a/tasks/get`
- `/a2a/agent-card`

Document the canonical JSON-RPC endpoint exactly as: `/a2a/`. Clients should not depend on redirect behavior.

### FastA2A 0.5 Wire Casing

The JSON wire format uses **camelCase** aliases. Do not use snake_case for A2A protocol parameters.

Correct:
```json
{
  "messageId": "msg-1",
  "acceptedOutputModes": ["application/json"]
}
```

Incorrect:
```json
{
  "message_id": "msg-1",
  "accepted_output_modes": ["application/json"]
}
```

Required camelCase fields include:
- `messageId`
- `contextId`
- `acceptedOutputModes`
- `historyLength`

## Authentication

OpenOperator supports two primary authentication schemes using the canonical deployment secret (`SPYNEL_AGENT_ZERO_API_KEY`).

- HTTP Bearer: `Authorization: Bearer <secret>`
- X-API-KEY: `X-API-KEY: <secret>`

Never expose this credential.

## Argus Capability

The `argus-supervisor` profile provides dedicated read-only functions to supervise OpenTraining.

Currently supported operational capabilities over A2A (when `ARGUS_SUPERVISOR_ENABLED=1`):
- `supervisor.health`
- `supervisor.status`
- `campaign.list`

Future capabilities (NOT currently supported over A2A):
- `campaign.create`
- `campaign.claim`
- `campaign.release`
- `campaign.submit`
- `campaign.intervene`
- `campaign.cancel`
- `campaign.mission`
- `campaign.answer`
- `campaign.resolve_intent`

### Canonical Argus Extension Envelope

The extension envelope correctly uses the URN namespace and canonical `requestId`:

```json
{
  "urn:openoperator:argus-supervisor:v1": {
    "operation": "supervisor.health",
    "requestId": "req-1"
  }
}
```

### Exact Argus Request Example

This copy-paste-safe example uses environment variables instead of literal tokens:

```bash
curl -X POST "$OPENOPERATOR_URL/a2a/" \
  -H "Authorization: Bearer $SPYNEL_AGENT_ZERO_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "jsonrpc": "2.0",
    "id": "argus-health-1",
    "method": "message/send",
    "params": {
      "configuration": {
        "blocking": true,
        "acceptedOutputModes": ["application/json"]
      },
      "message": {
        "role": "user",
        "kind": "message",
        "messageId": "argus-health-1",
        "extensions": [
          "urn:openoperator:argus-supervisor:v1"
        ],
        "metadata": {
          "urn:openoperator:argus-supervisor:v1": {
            "operation": "supervisor.health",
            "requestId": "argus-health-1"
          }
        },
        "parts": [
          {
            "kind": "data",
            "data": {
              "params": {}
            }
          }
        ]
      }
    }
  }'
```

## Capability identity and read-only limits

Use skill `id` as identity, never its display name. Disabled Argus advertises only
`general_assistance`. Enabled Argus advertises that skill plus
`argus_supervisor_health`, `argus_supervisor_status` and `argus_campaign_list`.
`argus_campaign_control` is not advertised or accepted by this extension.
The read operations bypass Agent Zero initialization and all model calls, including
when no Argus backend is configured. Installation of the `argus-supervisor` profile
is asserted separately from runtime enablement.

Malformed envelopes return `invalid_argus_envelope`, disabled mode returns
`argus_supervisor_disabled`, unsupported operations return
`unsupported_argus_operation`, and internal failures return
`argus_supervisor_unavailable`. Error details do not include exception strings.
Legacy `request_id` is accepted; responses use `requestId`.

The incoming HF authority remains `SPYNEL_AGENT_ZERO_API_KEY` for API, MCP and A2A.
Outgoing OpenTraining still uses the existing OpenTrainingA2AClient and
OPENTRAINING_A2A_URL / OPENTRAINING_A2A_TOKEN / OPENTRAINING_A2A_PROJECT.
No incoming OPENOPERATOR_A2A_TOKEN authority is introduced.

The historical streaming flag is retained in this source reconstruction for parity;
this PR does not qualify streaming or campaign-control mutations. Standard task
cancellation is not authority to control a training campaign.

For a bounded read-only deployment smoke, run `scripts/qualify_openoperator_a2a.sh`
with the canonical key securely configured and EXPECTED_SOURCE_SHA set to the exact
clean deployment commit. The script prints assertion names, not credentials or raw
authenticated responses. It never starts training, acquires a controller or runs a
model-driven campaign. The separate Spynel observer-chain smoke requires an existing
production observer task/campaign and must leave controller identity/epoch absent.