From 7dfb457d32983c9a456661cde583863ab2cbadf2 Mon Sep 17 00:00:00 2001 From: chaodu-agent <274062505+chaodu-agent@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:57:38 +0000 Subject: [PATCH 1/6] docs(adr): define authenticated OAB delegation protocol --- docs/adr/oab-to-oab-delegation.md | 532 ++++++++++++++++++++++++++++++ 1 file changed, 532 insertions(+) create mode 100644 docs/adr/oab-to-oab-delegation.md diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md new file mode 100644 index 000000000..5de5b26e0 --- /dev/null +++ b/docs/adr/oab-to-oab-delegation.md @@ -0,0 +1,532 @@ +# ADR: Authenticated OAB-to-OAB Delegation with Capability Attenuation and Signed Provenance + +- **Status:** Proposed +- **Date:** 2026-07-22 +- **Author:** @chaodu-agent +- **Discussion:** [Discord delegation discussion](https://discord.com/channels/1490282656913559673/1529261985881919529) + +## 1. Context + +OpenAB currently runs an agent behind a platform adapter or an ACP connection. A +running OAB agent can therefore answer messages and invoke its locally configured +agent runtime, but there is no first-class, authenticated way for one OAB agent +to ask another trusted OAB agent to perform a bounded subtask. + +Forwarding a prompt through a chat platform is not an adequate delegation +mechanism. It loses the execution identity, makes capability boundaries +implicit, complicates cancellation and retry, and makes it difficult to prove +which agent produced an artifact or side effect. Reusing the caller's sessions, +credentials, environment, or live tool handles would also turn delegation into +authority cloning rather than authority attenuation. + +The desired model is a parent OAB agent delegating a narrowly scoped task to a +worker OAB agent while preserving: + +- authenticated peer identity; +- least-authority capability attenuation; +- isolated execution state; +- explicit lifecycle and failure semantics; and +- tamper-evident provenance from the original request to the result. + +This ADR defines the protocol and security contract. It does not implement the +adapter or change the behavior of existing platform adapters. + +## 2. Decision + +Introduce a dedicated OAB delegation control plane with two roles: + +- **`DelegationClient`** — sends authenticated, bounded requests from a parent + OAB agent. +- **`DelegationServer`** — authenticates the caller, authorizes the request, + executes it through a locally provisioned agent session, and returns a signed + result envelope. + +A transport-specific **delegation adapter** may implement the control-plane +boundary, but it is not a `ChatAdapter` and must not route delegation through +Discord, Slack, or another user-facing chat surface. + +The MVP uses a local Unix-domain socket between explicitly paired OAB processes. +Remote delegation and federation are later transports over the same logical +protocol, protected by mTLS or an equivalent signed-agent identity mechanism. + +Where execution is backed by an ACP agent, the server reuses OpenAB's existing +ACP connection and session-pool lifecycle: bounded prompts, cancellation, +process-group cleanup, and controlled child environments remain local to the +callee. + +## 3. Authority and identity model + +A delegation request is an authority reduction, never an authority transfer. +The callee computes the effective authority as the intersection of four +independent constraints: + +```text +effective_authority = + caller_grants + ∩ callee_policy + ∩ request_capabilities + ∩ deployment_limits +``` + +An empty intersection is a denial. The caller cannot grant a capability it does +not possess, and the request cannot widen the callee's policy or deployment +limits. + +### 3.1 Peer identity + +"Trusted OAB agent" means an authenticated agent identity, not a Discord UID, +bot display name, branch name, or message origin. The server maintains an +explicit deny-by-default peer allowlist containing an agent ID and a public-key +identity (or local Unix peer-credential binding for the MVP). + +The principal chain is preserved separately from peer authentication: + +```text +human or system principal → parent OAB agent → callee OAB agent +``` + +A callee may authorize the parent peer while still recording the initiating +principal. No agent may impersonate a human or another peer in the chain. + +### 3.2 Capability attenuation + +Capabilities are structured, namespaced values such as: + +```text +repo:read +repo:test +artifact:write +network:read:approved-hosts +``` + +The MVP deliberately omits `delegate` from the capability catalogue. Every +request is evaluated against the four-way authority intersection before an +execution session is created. Capability names, resource scopes, expiry, and +budgets are part of the request digest. + +The server must reject unknown or malformed capabilities rather than silently +ignoring them. A future capability registry may add resource-specific limits, +but it must preserve monotonic attenuation. + +## 4. Wire contract + +The wire protocol is JSON-RPC-shaped and version-negotiated. The initial method +set is: + +```text +delegate/describe +delegate/start +delegate/status +delegate/cancel +delegate/events +``` + +`delegate/start` accepts a normalized request with at least: + +```json +{ + "protocol_version": 1, + "delegation_id": "uuid", + "requested_by": "agent-a", + "principal_chain": ["principal-x", "agent-a"], + "task": "Review this diff for security issues", + "capabilities": ["repo:read", "repo:test"], + "workspace": "isolated-worktree-ref", + "deadline": "2026-07-22T05:00:00Z", + "budget": {"max_runtime_ms": 300000, "max_output_bytes": 200000}, + "delegation_depth": 0, + "nonce": "unique-request-nonce", + "idempotency_key": "parent-task-123" +} +``` + +The request schema MUST NOT contain credentials, session handles, raw +environment maps, or live tool handles. Workspace references identify a +callee-approved resource; they are not a mechanism for passing an arbitrary +host path. + +The server emits explicit lifecycle states: + +```text +received → validated → reserved+audited → executing → terminal +``` + +Terminal states are `completed`, `failed`, `cancelled`, and `expired`. Rejected +requests use: + +```text +received → validated → rejected+audited +``` + +A request must never reach `executing` without a committed reservation and audit +record. + +### 4.1 Canonicalization and digests + +Protocol v1 uses deterministic canonical serialization before hashing. The +implementation should use RFC 8785 JSON Canonicalization Scheme (JCS), or a +protocol-compatible deterministic encoding if the implementation cannot support +JCS. The chosen encoding is part of protocol-version negotiation; ordinary JSON +object key order is not sufficient. + +- `request_digest` covers the complete normalized request, including authority- + relevant fields, capabilities, workspace, deadline, budget, nonce, and + protocol version. +- `authority_digest` covers the evaluated authority inputs and resulting + effective capability set. +- `result_digest` covers the exact result envelope payload before its signature. + +## 5. Signed result and provenance envelope + +The callee returns a result envelope that is immutable once signed: + +```json +{ + "protocol_version": 1, + "delegation_id": "uuid", + "requested_by": "agent-a", + "principal_chain": ["principal-x", "agent-a", "agent-b"], + "executed_by": "agent-b", + "effective_capabilities": ["repo:read"], + "authority_digest": "sha256:...", + "request_digest": "sha256:...", + "result_digest": "sha256:...", + "status": "completed", + "side_effects": [], + "artifacts": [ + { + "uri": "artifact://agent-b/sha256:...", + "owner": "agent-b", + "created_by": "agent-b", + "digest": "sha256:...", + "derived_from": [] + } + ], + "issued_at": "2026-07-22T04:50:00Z", + "expires_at": "2026-07-22T05:50:00Z", + "nonce": "unique-request-nonce", + "callee_key_id": "agent-b-key-1", + "attestation": { + "algorithm": "ed25519", + "signature": "base64url..." + } +} +``` + +The callee signature binds the request to the result: + +```text +signature = Sign_callee( + request_digest + + result_digest + + authority_digest + + principal_chain + + delegation_id + + issued_at + + expires_at + + nonce +) +``` + +The exact signed payload is the canonical serialized form, not an informal +concatenation. A caller may summarize a result for its user or create a new +derived artifact, but it may not rewrite `executed_by`, effective capabilities, +side effects, artifact ownership, or the principal chain. + +An artifact created by the callee remains callee-owned. A caller-created derived +artifact receives a new immutable owner and a `derived_from` provenance edge; +ownership cannot be overwritten or transferred by forwarding the result. + +Mutation records contain at least: + +```text +target, action, executor_identity, timestamp, outcome, before_digest, after_digest +``` + +## 6. Security invariants + +The following are protocol invariants and must be enforced fail-closed by the +server and result validator: + +1. **Recursive delegation is prohibited in the MVP.** + `delegation_depth > 0` is rejected with stable code + `recursive_delegation_forbidden`. The request capability catalogue does not + expose `delegate`. +2. **Credential forwarding is prohibited.** Any credential, session, raw + environment, or live tool-handle field is rejected with + `credential_forwarding_forbidden`. The callee uses only its locally + provisioned identity. +3. **Artifact ownership is immutable.** `artifact.owner != artifact.created_by` + for a newly created artifact is rejected with + `artifact_ownership_mismatch`, except where a new derived artifact and an + explicit `derived_from` edge are present. +4. **Capability widening is prohibited.** The effective capability set must be + a subset of every input authority set and deployment limit. +5. **No secret inheritance.** Child execution receives no parent credentials, + session state, environment variables, or live tool objects. The existing + OAB child-process environment allowlist remains the default. +6. **No chat transport impersonation.** A chat message or bot UID is not a + delegation credential and cannot select the internal delegation bypass. +7. **Signed provenance is mandatory.** A result without a valid callee signature, + matching request digest, matching result digest, and intact principal chain + is not accepted as a delegated result. +8. **Replay is rejected before execution.** A reused nonce, expired envelope, + duplicate idempotency key with a conflicting request digest, or unsupported + protocol version is rejected and audited. + +## 7. Atomic reservation, audit, and replay protection + +Nonce/idempotency reservation and durable audit recording must commit together, +using one database transaction or a transactional outbox. The only valid +execution transition is: + +```text +validate request + → atomically reserve nonce/idempotency key + → durably record audit event (or commit transactional outbox) + → commit + → start execution +``` + +If reservation, audit persistence, or the outbox commit fails, the server returns +`reservation_failed` or `audit_unavailable` and does not create a child session, +call a tool, or create an artifact. + +Audit events record at least the delegation ID, request digest, peer identity, +principal chain, effective capabilities, decision, rejection code when present, +and timestamp. The audit event is durable before execution begins. + +A retry with the same idempotency key and the same request digest resumes or +returns the existing delegation. A conflicting digest is rejected. Execution and +terminal result persistence use the same delegation ID so crash recovery cannot +silently create a second execution. + +Rejection tests must assert all of the following: + +- stable rejection code; +- no child session, tool call, artifact, or other execution side effect; +- durable audit event containing the rejection reason; and +- no second execution when the request is retried. + +## 8. Lifecycle, progress, and failure semantics + +`delegate/start` returns the delegation ID and current state. Progress events +carry a monotonically increasing sequence number so a disconnected caller can +resume from a known point. `delegate/status` is idempotent. + +Cancellation is best effort: the server records `cancel_requested`, asks the +local ACP session to cancel, and emits a terminal `cancelled` result only after +local execution stops. A timeout becomes `expired`; it must not be reported as +successful merely because the transport disconnected. + +The parent and worker use a lease/heartbeat for remote transports. For the MVP, +a local socket disconnect does not automatically transfer authority or claim +success; the worker either completes under its lease or records an indeterminate +failure that requires status reconciliation. + +The protocol must distinguish: + +- `failed`: execution returned a known error; +- `cancelled`: cancellation was observed; +- `expired`: deadline or lease expired before completion; and +- `unknown`: the worker cannot prove whether external side effects occurred. + +No status other than `completed` may be presented as a successful delegated +result. + +## 9. MVP scope and non-goals + +### In scope + +- Local Unix-domain socket transport between explicitly paired OAB processes. +- Authenticated peer identity and deny-by-default allowlist. +- One parent-to-one worker delegation request at a time, with bounded runtime, + output, and capability scope. +- Text task input and streamed progress. +- Isolated worker session using existing ACP/session-pool lifecycle. +- Capability attenuation, signed result envelopes, artifact digests, and audit + records. +- Cancellation, deadline, idempotency, replay protection, and explicit terminal + states. +- Negative tests for schema, environment, authorization, result validation, and + replay paths. + +### Explicitly prohibited or deferred + +- Recursive delegation and nested worker trees. +- Credential, session, environment, or live tool-handle forwarding. +- Caller-owned attribution of callee-created artifacts. +- Remote federation, dynamic peer discovery, and cross-organization trust. +- Delegation through Discord, Slack, webhooks, or other public chat channels. +- Shared conversation history or implicit parent-session access. +- Arbitrary tool grants, unrestricted host paths, and unbounded network access. +- Durable execution semantics that survive worker loss without a separate + scheduler/workflow design. + +Federation may later add trusted nodes and provenance edges, but it must not +relax the three core rejection rules: recursive delegation, credential +forwarding, and ownership mismatch. + +## 10. Negative-test acceptance criteria + +The implementation PR for this ADR must add tests that independently exercise +all four enforcement layers: + +### Schema layer + +- Reject credential/session/environment/live-handle fields. +- Reject unknown or malformed capabilities. +- Reject unsupported protocol versions and malformed canonical payloads. + +### Execution layer + +- Verify the child environment contains only the deployment allowlist and + explicitly provisioned local values. +- Verify no parent session or live tool handle is visible to the worker. +- Verify an unauthorized workspace reference cannot escape the worker boundary. + +### Authorization layer + +- Reject an unknown peer identity. +- Reject `delegation_depth > 0` with + `recursive_delegation_forbidden`. +- Reject capability widening when requested capabilities exceed any authority + intersection input. +- Verify the effective capability set is recorded in the authority digest. + +### Result and provenance layer + +- Reject a tampered request digest or result digest. +- Reject a missing or invalid callee signature. +- Reject `owner != executor` with `artifact_ownership_mismatch`. +- Accept a valid derived artifact only when it has a new owner and a valid + `derived_from` edge. +- Verify mutation side effects contain target, operation, executor, timestamp, + and outcome. + +### Wire-level replay and atomicity + +- Reject a reused nonce with `replay_detected`. +- Reject an expired request with `request_expired`. +- Reject a conflicting reuse of an idempotency key. +- Verify reservation and audit are atomic: if either persistence step fails, + execution does not start. +- Verify every rejection produces a durable audit event and no execution side + effect. +- Verify a retry of an accepted request does not create a second execution or + second successful result. + +## 11. Alternatives considered + +### Forward a prompt through a chat platform + +Rejected. Chat transport does not provide peer authentication, capability +attenuation, lifecycle control, replay protection, or signed provenance. It also +creates bot-loop and attribution problems. + +### Share the caller's ACP session with the callee + +Rejected. Session sharing crosses conversation state and authority boundaries, +prevents independent cancellation and auditing, and makes it impossible to +prove which agent performed a tool call. + +### Pass the caller's credentials or environment to the callee + +Rejected. This is credential forwarding and authority cloning. The callee must +use its own locally provisioned identity. + +### Let the callee delegate recursively + +Rejected for the MVP. Recursive trees multiply cost and complicate provenance, +lease ownership, replay protection, and failure recovery. A future orchestrator +mode can be designed as a separate protocol revision with explicit depth and +budget controls. + +### Use an unauthenticated HTTP endpoint with a shared static token + +Rejected for remote federation. A static token does not provide strong peer +identity, rotation, request signing, or useful provenance. It may be acceptable +as a narrowly scoped local development fixture, but not as the production wire +contract. + +## 12. Prior art + +### OpenClaw + +OpenClaw documents a multi-agent Gateway model in [Multi-agent +routing](https://github.com/openclaw/openclaw/blob/main/docs/concepts/multi-agent.md). +Each agent has separate workspace, state directory, auth profiles, and session +store, and routing uses explicit bindings. OpenClaw also makes agent-to-agent +messaging opt-in and allowlisted, and per-agent tool policies can only further +restrict global policy. + +We adopt the separation of agent state, explicit routing, and monotonic +per-agent restrictions. OpenClaw's model is primarily co-located routing inside +one Gateway process; it does not define the cross-process OAB wire contract +needed here, particularly the request/result digest binding and callee-signed +artifact provenance. + +### Hermes Agent + +Hermes documents a `delegate_task` tool in [Subagent +Delegation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/delegation.md). +It creates fresh child conversations and terminal sessions, limits concurrent +children, supports cancellation and background completion, and blocks recursive +delegation for leaf children by default. Hermes also records durable completion +records for background results and treats an interrupted child with unknown +side effects as `unknown` rather than claiming success. + +We adopt the fresh-context boundary, bounded concurrency/lifecycle, explicit +unknown outcome, and default-flat delegation. We diverge by making the OAB +boundary an authenticated peer protocol with capability intersection, no +credential forwarding, atomic replay reservation, and a callee signature that +binds the request digest to the result digest and authority digest. + +## 13. Consequences + +### Positive + +- A parent can use a running OAB worker without treating it as a chat user. +- Authority is narrowed at every boundary and cannot be widened by the caller. +- Credentials and session state remain local to the executing agent. +- Results and artifacts can be independently verified and audited. +- The protocol can support local delegation first and remote federation later + without changing the provenance model. + +### Costs and residual risks + +- Key management, canonicalization, nonce storage, and durable audit increase + implementation complexity compared with forwarding a prompt. +- A worker crash during an external side effect may remain `unknown`; the client + must reconcile status rather than retry blindly. +- Unix-socket MVP deployment is local-only and requires explicit process pairing. +- Artifact storage and garbage collection are not defined by this ADR; the + implementation must provide stable content-addressed references or defer + artifact-producing tasks. +- The ADR defines a contract, not a cryptographic implementation review. The + implementation PR must pin algorithms, key rotation behavior, and library + choices before enabling remote federation. + +## 14. Implementation sequence + +1. Add protocol types, canonical digest helpers, stable error codes, and negative + schema tests. +2. Add local peer authentication and the deny-by-default delegation policy. +3. Add atomic nonce/idempotency reservation plus audit/outbox persistence. +4. Add the worker execution bridge over the existing ACP/session-pool lifecycle, + with local environment and workspace restrictions. +5. Add signed result validation, artifact provenance, progress, cancellation, + and crash reconciliation. +6. Add end-to-end tests for the state machine and all negative-test acceptance + criteria in §10. +7. Consider remote mTLS federation only after the local protocol is stable and + its signed-envelope fixtures are independently verifiable. + +## 15. References + +- [OpenAB ACP connection and session pool](../../crates/openab-core/src/acp/connection.rs) +- [OpenAB child-process environment policy](../../AGENTS.md#3-security--child-process-environment) +- [OpenClaw multi-agent routing](https://github.com/openclaw/openclaw/blob/main/docs/concepts/multi-agent.md) +- [Hermes Agent subagent delegation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/delegation.md) +- [RFC 8785 — JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785) +- [Ed25519 signatures](https://www.rfc-editor.org/rfc/rfc8032) From f3dc9cb778ab0acc7edbeedbacf96f451ccbf807 Mon Sep 17 00:00:00 2001 From: chaodu-agent <274062505+chaodu-agent@users.noreply.github.com> Date: Wed, 22 Jul 2026 05:00:30 +0000 Subject: [PATCH 2/6] docs(adr): clarify remote delegation protocol --- docs/adr/oab-to-oab-delegation.md | 120 +++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 35 deletions(-) diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md index 5de5b26e0..ef12fddcf 100644 --- a/docs/adr/oab-to-oab-delegation.md +++ b/docs/adr/oab-to-oab-delegation.md @@ -45,9 +45,12 @@ A transport-specific **delegation adapter** may implement the control-plane boundary, but it is not a `ChatAdapter` and must not route delegation through Discord, Slack, or another user-facing chat surface. -The MVP uses a local Unix-domain socket between explicitly paired OAB processes. -Remote delegation and federation are later transports over the same logical -protocol, protected by mTLS or an equivalent signed-agent identity mechanism. +The MVP is an inter-OAB protocol: OAB A connects to a preconfigured remote OAB B +through authenticated HTTPS for bootstrap/health and a long-lived WebSocket +carrying JSON-RPC messages. The WebSocket is only a transport; delegation never +travels as a Discord, Slack, or webhook message. Remote federation and dynamic +peer discovery are deferred, but the first implementation is already remote +OAB-to-OAB execution rather than an in-process subagent API. Where execution is backed by an ACP agent, the server reuses OpenAB's existing ACP connection and session-pool lifecycle: bounded prompts, cancellation, @@ -77,7 +80,7 @@ limits. "Trusted OAB agent" means an authenticated agent identity, not a Discord UID, bot display name, branch name, or message origin. The server maintains an explicit deny-by-default peer allowlist containing an agent ID and a public-key -identity (or local Unix peer-credential binding for the MVP). +identity (or a configured public-key identity bound to the remote OAB endpoint). The principal chain is preserved separately from peer authentication: @@ -115,18 +118,19 @@ set is: ```text delegate/describe -delegate/start -delegate/status -delegate/cancel -delegate/events +delegate/task.create +delegate/task.get +delegate/task.events +delegate/task.send +delegate/task.interrupt ``` -`delegate/start` accepts a normalized request with at least: +`delegate/task.create` accepts a normalized request with at least: ```json { "protocol_version": 1, - "delegation_id": "uuid", + "request_id": "parent-request-123", "requested_by": "agent-a", "principal_chain": ["principal-x", "agent-a"], "task": "Review this diff for security issues", @@ -151,15 +155,24 @@ The server emits explicit lifecycle states: received → validated → reserved+audited → executing → terminal ``` -Terminal states are `completed`, `failed`, `cancelled`, and `expired`. Rejected -requests use: +Terminal states are `completed`, `failed`, `cancelled`, `expired`, and `unknown`. +Requests that cannot be parsed, whose protocol version is unsupported, or whose +signature cannot be verified are rejected immediately: + +```text +received → rejected+audited +``` + +A structurally valid and authenticated request can be rejected after policy +validation: ```text received → validated → rejected+audited ``` A request must never reach `executing` without a committed reservation and audit -record. +record. OAB A may wait on the same task, reconnect, or resume event delivery +without creating a second task. ### 4.1 Canonicalization and digests @@ -176,6 +189,25 @@ object key order is not sufficient. effective capability set. - `result_digest` covers the exact result envelope payload before its signature. +## 4.2 Task and event identity + +The remote task is not identified by a mutable display name or routing path: + +- `request_id` is supplied by OAB A as the idempotency key for task creation. +- `task_id` is generated by OAB B and is immutable and globally unique. It is + the storage, correlation, and crash-recovery key. +- `task_path` is a human-readable, mutable routing/ancestry path. Rename or + reparent operations update path mapping only; they never rewrite event + ownership. +- `turn_id` identifies each execution or follow-up turn under one task. +- An event is identified by `(task_id, turn_id, sequence)`. +- `cursor` is a separate durable, monotonically increasing replay position. + +All follow-up, wait, interrupt, and reconnect operations first resolve any +human-readable path to `task_id`, then use the immutable ID. A duplicate +`request_id` may return the existing `task_id` and result, but may not create a +second remote task. + ## 5. Signed result and provenance envelope The callee returns a result envelope that is immutable once signed: @@ -183,7 +215,9 @@ The callee returns a result envelope that is immutable once signed: ```json { "protocol_version": 1, - "delegation_id": "uuid", + "request_id": "parent-request-123", + "task_id": "callee-task-456", + "turn_id": "turn-1", "requested_by": "agent-a", "principal_chain": ["principal-x", "agent-a", "agent-b"], "executed_by": "agent-b", @@ -191,6 +225,7 @@ The callee returns a result envelope that is immutable once signed: "authority_digest": "sha256:...", "request_digest": "sha256:...", "result_digest": "sha256:...", + "outcome_digest": "sha256:...", "status": "completed", "side_effects": [], "artifacts": [ @@ -221,7 +256,8 @@ signature = Sign_callee( + result_digest + authority_digest + principal_chain - + delegation_id + + task_id + + turn_id + issued_at + expires_at + nonce @@ -296,10 +332,12 @@ Audit events record at least the delegation ID, request digest, peer identity, principal chain, effective capabilities, decision, rejection code when present, and timestamp. The audit event is durable before execution begins. -A retry with the same idempotency key and the same request digest resumes or -returns the existing delegation. A conflicting digest is rejected. Execution and -terminal result persistence use the same delegation ID so crash recovery cannot -silently create a second execution. +A retry with the same `request_id` and the same request digest returns the +existing `task_id`, existing terminal result, or resumes the one incomplete +execution. A conflicting digest is rejected. A terminal record persists the +exact signed envelope and its `outcome_digest`. Repeating a request for the same +`task_id` can therefore only return the existing outcome or recover the unfinished +execution; it must never start a second execution. Rejection tests must assert all of the following: @@ -310,19 +348,24 @@ Rejection tests must assert all of the following: ## 8. Lifecycle, progress, and failure semantics -`delegate/start` returns the delegation ID and current state. Progress events -carry a monotonically increasing sequence number so a disconnected caller can -resume from a known point. `delegate/status` is idempotent. +`delegate/task.create` returns the caller's `request_id` together with the +callee-generated immutable `task_id` and current state. OAB A may wait through +`delegate/task.events` or query `delegate/task.get`. Progress events carry a +monotonically increasing sequence number and durable cursor so a disconnected +caller can reconnect without resubmitting the task. `delegate/task.send` creates +a new `turn_id` under the same task; `delegate/task.interrupt` propagates +cancellation. Cancellation is best effort: the server records `cancel_requested`, asks the local ACP session to cancel, and emits a terminal `cancelled` result only after local execution stops. A timeout becomes `expired`; it must not be reported as successful merely because the transport disconnected. -The parent and worker use a lease/heartbeat for remote transports. For the MVP, -a local socket disconnect does not automatically transfer authority or claim -success; the worker either completes under its lease or records an indeterminate -failure that requires status reconciliation. +The parent and worker use a lease/heartbeat on the remote connection. A +disconnect does not automatically cancel, duplicate, or claim success for the +task. OAB A must reattach using the immutable `task_id` and last durable cursor; +if OAB B cannot prove the outcome, it returns `unknown` and OAB A must not blindly +retry. The protocol must distinguish: @@ -331,15 +374,19 @@ The protocol must distinguish: - `expired`: deadline or lease expired before completion; and - `unknown`: the worker cannot prove whether external side effects occurred. -No status other than `completed` may be presented as a successful delegated -result. +Every terminal state persists an `outcome_digest` and the corresponding signed +result envelope. No status other than `completed` may be presented as a successful +delegated result. A reconnect or retry with the same immutable `task_id` returns +that persisted outcome or resumes the single incomplete execution; it never +re-executes a terminal task. ## 9. MVP scope and non-goals ### In scope -- Local Unix-domain socket transport between explicitly paired OAB processes. -- Authenticated peer identity and deny-by-default allowlist. +- Inter-OAB remote execution over authenticated HTTPS/WebSocket transport. +- Preconfigured remote OAB peers with authenticated identity and deny-by-default + allowlist. - One parent-to-one worker delegation request at a time, with bounded runtime, output, and capability scope. - Text task input and streamed progress. @@ -356,7 +403,8 @@ result. - Recursive delegation and nested worker trees. - Credential, session, environment, or live tool-handle forwarding. - Caller-owned attribution of callee-created artifacts. -- Remote federation, dynamic peer discovery, and cross-organization trust. +- Dynamic peer discovery, cross-organization federation, and automatic trust + enrollment. - Delegation through Discord, Slack, webhooks, or other public chat channels. - Shared conversation history or implicit parent-session access. - Arbitrary tool grants, unrestricted host paths, and unbounded network access. @@ -490,8 +538,8 @@ binds the request digest to the result digest and authority digest. - Authority is narrowed at every boundary and cannot be widened by the caller. - Credentials and session state remain local to the executing agent. - Results and artifacts can be independently verified and audited. -- The protocol can support local delegation first and remote federation later - without changing the provenance model. +- The protocol supports a remote OAB user story while leaving discovery and + cross-organization federation for later, without changing the provenance model. ### Costs and residual risks @@ -499,7 +547,8 @@ binds the request digest to the result digest and authority digest. implementation complexity compared with forwarding a prompt. - A worker crash during an external side effect may remain `unknown`; the client must reconcile status rather than retry blindly. -- Unix-socket MVP deployment is local-only and requires explicit process pairing. +- The MVP requires preconfigured remote endpoints and key rotation remains an + operational responsibility until federation is designed. - Artifact storage and garbage collection are not defined by this ADR; the implementation must provide stable content-addressed references or defer artifact-producing tasks. @@ -511,7 +560,8 @@ binds the request digest to the result digest and authority digest. 1. Add protocol types, canonical digest helpers, stable error codes, and negative schema tests. -2. Add local peer authentication and the deny-by-default delegation policy. +2. Add HTTPS/WebSocket transport, remote peer authentication, and the deny-by- + default delegation policy. 3. Add atomic nonce/idempotency reservation plus audit/outbox persistence. 4. Add the worker execution bridge over the existing ACP/session-pool lifecycle, with local environment and workspace restrictions. From 7a45595546f108a33f2b38bfb63272d09944b229 Mon Sep 17 00:00:00 2001 From: chaodu-agent <274062505+chaodu-agent@users.noreply.github.com> Date: Wed, 22 Jul 2026 05:26:39 +0000 Subject: [PATCH 3/6] docs(adr): refocus delegation adapter ADR --- docs/adr/oab-to-oab-delegation.md | 792 ++++++++++++------------------ 1 file changed, 315 insertions(+), 477 deletions(-) diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md index ef12fddcf..d8bd26a33 100644 --- a/docs/adr/oab-to-oab-delegation.md +++ b/docs/adr/oab-to-oab-delegation.md @@ -1,582 +1,420 @@ -# ADR: Authenticated OAB-to-OAB Delegation with Capability Attenuation and Signed Provenance +# ADR: OAB Delegation Adapter for Inter-OAB Remote Tasks - **Status:** Proposed - **Date:** 2026-07-22 - **Author:** @chaodu-agent - **Discussion:** [Discord delegation discussion](https://discord.com/channels/1490282656913559673/1529261985881919529) -## 1. Context +## 1. User story -OpenAB currently runs an agent behind a platform adapter or an ACP connection. A -running OAB agent can therefore answer messages and invoke its locally configured -agent runtime, but there is no first-class, authenticated way for one OAB agent -to ask another trusted OAB agent to perform a bounded subtask. +A running OAB agent should be able to delegate work instead of doing the work +itself: -Forwarding a prompt through a chat platform is not an adequate delegation -mechanism. It loses the execution identity, makes capability boundaries -implicit, complicates cancellation and retry, and makes it difficult to prove -which agent produced an artifact or side effect. Reusing the caller's sessions, -credentials, environment, or live tool handles would also turn delegation into -authority cloning rather than authority attenuation. - -The desired model is a parent OAB agent delegating a narrowly scoped task to a -worker OAB agent while preserving: +```text +Agent A + -> OAB A delegation adapter + -> authenticated remote OAB B + -> Agent B performs the task + -> OAB A waits for the result + -> Agent A continues its original task +``` -- authenticated peer identity; -- least-authority capability attenuation; -- isolated execution state; -- explicit lifecycle and failure semantics; and -- tamper-evident provenance from the original request to the result. +For example, while reviewing a change, Agent A may ask a remote OAB specialized +in security review to inspect the diff. Agent A should receive progress and a +structured final result, then use that result in its own conversation. The +remote worker is an OAB peer, not a Discord user, a forwarded chat message, or +an exposed ACP process. -This ADR defines the protocol and security contract. It does not implement the -adapter or change the behavior of existing platform adapters. +This is an **inter-OAB operation**. The adapter must work when OAB A and OAB B +run in different processes, hosts, or clusters. A relay may be used when both +OAB instances can only make outbound connections, but a particular relay +provider is not part of this protocol decision. ## 2. Decision -Introduce a dedicated OAB delegation control plane with two roles: +Add a dedicated **delegation adapter** with a client side in OAB A and a server +side in OAB B: -- **`DelegationClient`** — sends authenticated, bounded requests from a parent - OAB agent. -- **`DelegationServer`** — authenticates the caller, authorizes the request, - executes it through a locally provisioned agent session, and returns a signed - result envelope. +```text +Agent A + | + | local delegate operation + v +OAB A DelegationClient + | + | HTTPS bootstrap/health + | WebSocket + JSON-RPC 2.0 + v +OAB B DelegationServer + | + | local session and policy enforcement + v +Agent B via ACP +``` -A transport-specific **delegation adapter** may implement the control-plane -boundary, but it is not a `ChatAdapter` and must not route delegation through -Discord, Slack, or another user-facing chat surface. +The delegation adapter is not another `ChatAdapter`. Chat adapters translate +human-facing platform events; the delegation adapter translates a bounded local +agent operation into a remote OAB task and maps the remote lifecycle back to +the caller. -The MVP is an inter-OAB protocol: OAB A connects to a preconfigured remote OAB B -through authenticated HTTPS for bootstrap/health and a long-lived WebSocket -carrying JSON-RPC messages. The WebSocket is only a transport; delegation never -travels as a Discord, Slack, or webhook message. Remote federation and dynamic -peer discovery are deferred, but the first implementation is already remote -OAB-to-OAB execution rather than an in-process subagent API. +OAB B keeps its ACP connection, credentials, environment, workspace policy, and +tool approvals local. The outer delegation protocol must never expose ACP +directly to a remote peer or forward OAB A's session and credentials. -Where execution is backed by an ACP agent, the server reuses OpenAB's existing -ACP connection and session-pool lifecycle: bounded prompts, cancellation, -process-group cleanup, and controlled child environments remain local to the -callee. +### 2.1 Transport and protocol boundary -## 3. Authority and identity model +The MVP uses authenticated HTTPS for bootstrap/health and a long-lived +WebSocket carrying a versioned JSON-RPC 2.0 protocol. The transport provides +bidirectional progress and cancellation; JSON-RPC provides method and error +versioning without coupling the public contract to a vendor-specific ACP +extension. -A delegation request is an authority reduction, never an authority transfer. -The callee computes the effective authority as the intersection of four -independent constraints: +The initial method set is intentionally small: ```text -effective_authority = - caller_grants - ∩ callee_policy - ∩ request_capabilities - ∩ deployment_limits +delegate/describe capability and protocol negotiation +delegate/task.create submit one remote task +delegate/task.get read current task state +delegate/task.events wait/replay progress and terminal events +delegate/task.cancel request cancellation ``` -An empty intersection is a denial. The caller cannot grant a capability it does -not possess, and the request cannot widen the callee's policy or deployment -limits. - -### 3.1 Peer identity - -"Trusted OAB agent" means an authenticated agent identity, not a Discord UID, -bot display name, branch name, or message origin. The server maintains an -explicit deny-by-default peer allowlist containing an agent ID and a public-key -identity (or a configured public-key identity bound to the remote OAB endpoint). - -The principal chain is preserved separately from peer authentication: +A deployment may connect directly: ```text -human or system principal → parent OAB agent → callee OAB agent +OAB A -- outbound or inbound WSS --> OAB B ``` -A callee may authorize the parent peer while still recording the initiating -principal. No agent may impersonate a human or another peer in the chain. - -### 3.2 Capability attenuation - -Capabilities are structured, namespaced values such as: +or use a relay that pairs two outbound WebSockets: ```text -repo:read -repo:test -artifact:write -network:read:approved-hosts +OAB A -- outbound WSS --> relay <-- outbound WSS -- OAB B ``` -The MVP deliberately omits `delegate` from the capability catalogue. Every -request is evaluated against the four-way authority intersection before an -execution session is created. Capability names, resource scopes, expiry, and -budgets are part of the request digest. +The relay is a deployment option, not a protocol dependency. Cloudflare Worker +and Durable Object are possible implementations, but the ADR does not make +Cloudflare APIs, pricing, or free-tier behavior part of the OAB contract. -The server must reject unknown or malformed capabilities rather than silently -ignoring them. A future capability registry may add resource-specific limits, -but it must preserve monotonic attenuation. +### 2.2 Task identity and result mapping -## 4. Wire contract - -The wire protocol is JSON-RPC-shaped and version-negotiated. The initial method -set is: +The caller supplies an idempotent `request_id`; the callee creates an immutable +`task_id`: ```text -delegate/describe -delegate/task.create -delegate/task.get -delegate/task.events -delegate/task.send -delegate/task.interrupt +request_id -> task_id -> turn_id -> event sequence/cursor ``` -`delegate/task.create` accepts a normalized request with at least: +- `request_id` deduplicates task creation at OAB B. +- `task_id` is the immutable storage and reconnect key generated by OAB B. +- `task_path` is an optional human-readable route such as `/root/security`; it + can change without changing task ownership or event history. +- `turn_id` identifies an execution turn if the task later supports follow-up + work. +- `(task_id, turn_id, sequence)` identifies an event; a durable `cursor` allows + replay after reconnect. + +The caller waits by reading events or task state. Reconnecting with the same +`task_id` resumes observation; it does not submit a second task. A terminal +result includes at least `status`, `summary`, `error`, `artifacts`, and +`executed_by`. + +## 3. MVP scope + +The MVP validates the remote delegation user story without attempting to build a +full distributed workflow engine. + +### 3.1 In scope + +- Explicitly configured, deny-by-default OAB peers. +- Authenticated peer connections using mTLS or signed short-lived agent tokens. +- One parent OAB submitting a bounded task to one remote worker OAB. +- Text task input, a deadline, output/runtime budget, and requested capabilities. +- Read-only repository work by default. +- A local ACP session on OAB B for actual execution. +- `create`, `get`, event wait/replay, and cancellation lifecycle operations. +- Durable request/task identity, idempotency, reconnect, and bounded event + retention. +- Streamed progress followed by one structured terminal result. +- Callee-owned artifact and side-effect provenance in the result envelope. +- A maximum delegation depth of one: the remote worker cannot delegate again. +- Per-peer and global concurrency limits with backpressure. +- Negative tests for unauthorized peers, capability widening, duplicate tasks, + timeout, cancellation, reconnect, worker failure, and credential leakage. + +A minimal request is conceptually: ```json { "protocol_version": 1, "request_id": "parent-request-123", - "requested_by": "agent-a", - "principal_chain": ["principal-x", "agent-a"], + "target_peer": "oab-b", "task": "Review this diff for security issues", - "capabilities": ["repo:read", "repo:test"], - "workspace": "isolated-worktree-ref", - "deadline": "2026-07-22T05:00:00Z", - "budget": {"max_runtime_ms": 300000, "max_output_bytes": 200000}, + "capabilities": ["repo:read"], + "deadline_ms": 300000, + "budget": {"max_output_bytes": 200000}, "delegation_depth": 0, "nonce": "unique-request-nonce", - "idempotency_key": "parent-task-123" + "idempotency_key": "parent-request-123" } ``` -The request schema MUST NOT contain credentials, session handles, raw -environment maps, or live tool handles. Workspace references identify a -callee-approved resource; they are not a mechanism for passing an arbitrary -host path. - -The server emits explicit lifecycle states: - -```text -received → validated → reserved+audited → executing → terminal -``` - -Terminal states are `completed`, `failed`, `cancelled`, `expired`, and `unknown`. -Requests that cannot be parsed, whose protocol version is unsupported, or whose -signature cannot be verified are rejected immediately: +The schema deliberately has no credential, session, raw environment, arbitrary +host path, or live tool-handle field. + +### 3.2 Explicit non-goals + +- In-process-only subagents: the user story is remote inter-OAB execution. +- Delegation through Discord, Slack, webhooks, or other chat channels. +- Recursive delegation, arbitrary DAG workflow, or unbounded fan-out. +- Dynamic peer discovery, automatic trust enrollment, or cross-organization + federation. +- Shared conversation history or implicit access to the parent's session. +- Shared writable worktrees or parallel writers. Read-heavy exploration and + testing are the initial use cases. +- Arbitrary remote tool grants, credential forwarding, or capability expansion. +- Durable workflow scheduling that guarantees recovery after every worker crash. +- A Cloudflare-specific relay implementation in the core OAB repository. + +## 4. Lessons from Codex multi-agent v2 + +Codex multi-agent v2 is useful as an orchestration design reference, not as the +OAB wire contract. OAB should adopt the semantics that solve the user story and +avoid copying an experimental tool schema. + +| Codex v2 lesson | OAB decision | +| --- | --- | +| Delegation is a task tree, not just `delegate(prompt) -> text`. | Model a remote task explicitly. Use immutable `task_id` for storage and a readable `task_path` for routing and UI. MVP depth is one. | +| Canonical task paths make routing and ancestry visible. | Accept an optional path for human-facing routing, but never use it as the permanent identity; events and reconnects use `task_id`. | +| Spawn, message, follow-up, interrupt, wait, and discovery are distinct operations. | Keep create, state/events, and cancel distinct in the MVP. If follow-up turns are added, `message`, `followup`, and `interrupt` remain separate operations rather than one overloaded flag. | +| Waiting is an event/mailbox wake-up, not a giant tool response containing all history. | Return a task identity immediately and stream/replay events with sequence numbers and cursors. The caller can fetch the terminal result separately. | +| A worker owns its own context/thread. | Default context policy to `none`; do not copy Discord/private connector context or the parent's session. Future policies must be explicit (`none`, `summary`, or selected references). | +| Concurrency is bounded globally. | Add both per-root and process-wide active-delegation limits, plus deadline, output, and budget limits. Do not consume the ordinary chat session pool without a separate delegation budget. | +| Child agents inherit a restricted runtime, not arbitrary new authority. | Effective authority is the intersection of caller grants, OAB A delegation policy, OAB B policy, request capabilities, and deployment limits. The child cannot widen it. | +| Progress and completion are lifecycle events. | Preserve explicit `accepted`, `running`, `completed`, `failed`, `cancelled`, `expired`, and `unknown` states and make reconnect/replay first-class. | +| Encrypted inter-agent messages can reduce auditability. | Encrypt the transport when needed, but persist a redacted human-readable audit record containing caller, callee, task, capabilities, decision, and outcome. | +| Experimental implementation details should not become a public contract. | Define an OAB-owned versioned delegation protocol and a backend-neutral coordinator; map Codex-like semantics to it rather than exposing Codex names or IDs. | + +The main lesson is that delegation is a controlled task-tree runtime with +mailbox-style lifecycle events, not a prompt-forwarding helper. The main OAB +addition is the remote trust, reconnect, provenance, and failure boundary that +a local multi-agent implementation does not provide. + +## 5. Context, authority, and provenance + +Delegation must reduce authority at every boundary: ```text -received → rejected+audited -``` - -A structurally valid and authenticated request can be rejected after policy -validation: - -```text -received → validated → rejected+audited +effective_authority = + caller_grants + intersection OAB A delegation policy + intersection OAB B callee policy + intersection request capabilities + intersection deployment limits ``` -A request must never reach `executing` without a committed reservation and audit -record. OAB A may wait on the same task, reconnect, or resume event delivery -without creating a second task. +An empty intersection is denied. OAB B executes only with its own locally +provisioned identity. No credentials, session handles, environment maps, live +tool objects, or arbitrary host paths cross the connection. -### 4.1 Canonicalization and digests - -Protocol v1 uses deterministic canonical serialization before hashing. The -implementation should use RFC 8785 JSON Canonicalization Scheme (JCS), or a -protocol-compatible deterministic encoding if the implementation cannot support -JCS. The chosen encoding is part of protocol-version negotiation; ordinary JSON -object key order is not sufficient. - -- `request_digest` covers the complete normalized request, including authority- - relevant fields, capabilities, workspace, deadline, budget, nonce, and - protocol version. -- `authority_digest` covers the evaluated authority inputs and resulting - effective capability set. -- `result_digest` covers the exact result envelope payload before its signature. - -## 4.2 Task and event identity - -The remote task is not identified by a mutable display name or routing path: - -- `request_id` is supplied by OAB A as the idempotency key for task creation. -- `task_id` is generated by OAB B and is immutable and globally unique. It is - the storage, correlation, and crash-recovery key. -- `task_path` is a human-readable, mutable routing/ancestry path. Rename or - reparent operations update path mapping only; they never rewrite event - ownership. -- `turn_id` identifies each execution or follow-up turn under one task. -- An event is identified by `(task_id, turn_id, sequence)`. -- `cursor` is a separate durable, monotonically increasing replay position. - -All follow-up, wait, interrupt, and reconnect operations first resolve any -human-readable path to `task_id`, then use the immutable ID. A duplicate -`request_id` may return the existing `task_id` and result, but may not create a -second remote task. - -## 5. Signed result and provenance envelope - -The callee returns a result envelope that is immutable once signed: +The result envelope is signed by OAB B and binds the request to its outcome: ```json { "protocol_version": 1, "request_id": "parent-request-123", "task_id": "callee-task-456", - "turn_id": "turn-1", - "requested_by": "agent-a", - "principal_chain": ["principal-x", "agent-a", "agent-b"], - "executed_by": "agent-b", + "status": "completed", + "summary": "No security issues found", + "executed_by": "oab-b-agent", "effective_capabilities": ["repo:read"], - "authority_digest": "sha256:...", "request_digest": "sha256:...", "result_digest": "sha256:...", - "outcome_digest": "sha256:...", - "status": "completed", + "artifacts": [], "side_effects": [], - "artifacts": [ - { - "uri": "artifact://agent-b/sha256:...", - "owner": "agent-b", - "created_by": "agent-b", - "digest": "sha256:...", - "derived_from": [] - } - ], - "issued_at": "2026-07-22T04:50:00Z", - "expires_at": "2026-07-22T05:50:00Z", - "nonce": "unique-request-nonce", - "callee_key_id": "agent-b-key-1", - "attestation": { - "algorithm": "ed25519", - "signature": "base64url..." - } + "attestation": {"algorithm": "ed25519", "signature": "..."} } ``` -The callee signature binds the request to the result: +The caller may summarize the result, but may not rewrite `executed_by`, +effective capabilities, side effects, or artifact ownership. A callee-created +artifact remains callee-owned; a caller-created derivative receives a new owner +and a provenance link. -```text -signature = Sign_callee( - request_digest - + result_digest - + authority_digest - + principal_chain - + task_id - + turn_id - + issued_at - + expires_at - + nonce -) -``` +The MVP rejects, fail-closed: + +- unknown peers or invalid authentication; +- `delegation_depth > 0`; +- unknown capabilities or capability widening; +- credential/session/environment/tool-handle fields; +- reused nonce or conflicting idempotency key; +- expired or malformed requests; +- invalid result signature, digest, executor identity, or artifact ownership. -The exact signed payload is the canonical serialized form, not an informal -concatenation. A caller may summarize a result for its user or create a new -derived artifact, but it may not rewrite `executed_by`, effective capabilities, -side effects, artifact ownership, or the principal chain. +Nonce/idempotency reservation and the audit event must be committed before +execution. If reservation or audit persistence fails, OAB B must not create a +worker session, call a tool, or create an artifact. -An artifact created by the callee remains callee-owned. A caller-created derived -artifact receives a new immutable owner and a `derived_from` provenance edge; -ownership cannot be overwritten or transferred by forwarding the result. +## 6. Lifecycle and failure semantics -Mutation records contain at least: +The lifecycle is: ```text -target, action, executor_identity, timestamp, outcome, before_digest, after_digest +received -> validated -> accepted -> running -> terminal + | | + | +-> completed + | +-> failed + | +-> cancelled + | +-> expired + | +-> unknown + +-> rejected ``` -## 6. Security invariants - -The following are protocol invariants and must be enforced fail-closed by the -server and result validator: - -1. **Recursive delegation is prohibited in the MVP.** - `delegation_depth > 0` is rejected with stable code - `recursive_delegation_forbidden`. The request capability catalogue does not - expose `delegate`. -2. **Credential forwarding is prohibited.** Any credential, session, raw - environment, or live tool-handle field is rejected with - `credential_forwarding_forbidden`. The callee uses only its locally - provisioned identity. -3. **Artifact ownership is immutable.** `artifact.owner != artifact.created_by` - for a newly created artifact is rejected with - `artifact_ownership_mismatch`, except where a new derived artifact and an - explicit `derived_from` edge are present. -4. **Capability widening is prohibited.** The effective capability set must be - a subset of every input authority set and deployment limit. -5. **No secret inheritance.** Child execution receives no parent credentials, - session state, environment variables, or live tool objects. The existing - OAB child-process environment allowlist remains the default. -6. **No chat transport impersonation.** A chat message or bot UID is not a - delegation credential and cannot select the internal delegation bypass. -7. **Signed provenance is mandatory.** A result without a valid callee signature, - matching request digest, matching result digest, and intact principal chain - is not accepted as a delegated result. -8. **Replay is rejected before execution.** A reused nonce, expired envelope, - duplicate idempotency key with a conflicting request digest, or unsupported - protocol version is rejected and audited. - -## 7. Atomic reservation, audit, and replay protection - -Nonce/idempotency reservation and durable audit recording must commit together, -using one database transaction or a transactional outbox. The only valid -execution transition is: +`unknown` is required when OAB B cannot prove whether an external side effect +occurred. OAB A must not silently convert `unknown`, `failed`, `cancelled`, or +`expired` into success or blindly retry the task. -```text -validate request - → atomically reserve nonce/idempotency key - → durably record audit event (or commit transactional outbox) - → commit - → start execution -``` +Cancellation is best effort: OAB B records the request, asks its local ACP +session to stop, and emits `cancelled` only after the worker stops. A transport +disconnect does not mean success, cancellation, or permission to create a +second task. OAB A reconnects with `task_id` and its last cursor. + +Every terminal task persists its outcome digest and signed envelope. A duplicate +request with the same normalized request returns the existing task or result; +a conflicting request with the same idempotency key is rejected. -If reservation, audit persistence, or the outbox commit fails, the server returns -`reservation_failed` or `audit_unavailable` and does not create a child session, -call a tool, or create an artifact. - -Audit events record at least the delegation ID, request digest, peer identity, -principal chain, effective capabilities, decision, rejection code when present, -and timestamp. The audit event is durable before execution begins. - -A retry with the same `request_id` and the same request digest returns the -existing `task_id`, existing terminal result, or resumes the one incomplete -execution. A conflicting digest is rejected. A terminal record persists the -exact signed envelope and its `outcome_digest`. Repeating a request for the same -`task_id` can therefore only return the existing outcome or recover the unfinished -execution; it must never start a second execution. - -Rejection tests must assert all of the following: - -- stable rejection code; -- no child session, tool call, artifact, or other execution side effect; -- durable audit event containing the rejection reason; and -- no second execution when the request is retried. - -## 8. Lifecycle, progress, and failure semantics - -`delegate/task.create` returns the caller's `request_id` together with the -callee-generated immutable `task_id` and current state. OAB A may wait through -`delegate/task.events` or query `delegate/task.get`. Progress events carry a -monotonically increasing sequence number and durable cursor so a disconnected -caller can reconnect without resubmitting the task. `delegate/task.send` creates -a new `turn_id` under the same task; `delegate/task.interrupt` propagates -cancellation. - -Cancellation is best effort: the server records `cancel_requested`, asks the -local ACP session to cancel, and emits a terminal `cancelled` result only after -local execution stops. A timeout becomes `expired`; it must not be reported as -successful merely because the transport disconnected. - -The parent and worker use a lease/heartbeat on the remote connection. A -disconnect does not automatically cancel, duplicate, or claim success for the -task. OAB A must reattach using the immutable `task_id` and last durable cursor; -if OAB B cannot prove the outcome, it returns `unknown` and OAB A must not blindly -retry. - -The protocol must distinguish: - -- `failed`: execution returned a known error; -- `cancelled`: cancellation was observed; -- `expired`: deadline or lease expired before completion; and -- `unknown`: the worker cannot prove whether external side effects occurred. - -Every terminal state persists an `outcome_digest` and the corresponding signed -result envelope. No status other than `completed` may be presented as a successful -delegated result. A reconnect or retry with the same immutable `task_id` returns -that persisted outcome or resumes the single incomplete execution; it never -re-executes a terminal task. - -## 9. MVP scope and non-goals - -### In scope - -- Inter-OAB remote execution over authenticated HTTPS/WebSocket transport. -- Preconfigured remote OAB peers with authenticated identity and deny-by-default - allowlist. -- One parent-to-one worker delegation request at a time, with bounded runtime, - output, and capability scope. -- Text task input and streamed progress. -- Isolated worker session using existing ACP/session-pool lifecycle. -- Capability attenuation, signed result envelopes, artifact digests, and audit - records. -- Cancellation, deadline, idempotency, replay protection, and explicit terminal - states. -- Negative tests for schema, environment, authorization, result validation, and - replay paths. - -### Explicitly prohibited or deferred - -- Recursive delegation and nested worker trees. -- Credential, session, environment, or live tool-handle forwarding. -- Caller-owned attribution of callee-created artifacts. -- Dynamic peer discovery, cross-organization federation, and automatic trust - enrollment. -- Delegation through Discord, Slack, webhooks, or other public chat channels. -- Shared conversation history or implicit parent-session access. -- Arbitrary tool grants, unrestricted host paths, and unbounded network access. -- Durable execution semantics that survive worker loss without a separate - scheduler/workflow design. - -Federation may later add trusted nodes and provenance edges, but it must not -relax the three core rejection rules: recursive delegation, credential -forwarding, and ownership mismatch. - -## 10. Negative-test acceptance criteria - -The implementation PR for this ADR must add tests that independently exercise -all four enforcement layers: - -### Schema layer - -- Reject credential/session/environment/live-handle fields. -- Reject unknown or malformed capabilities. -- Reject unsupported protocol versions and malformed canonical payloads. - -### Execution layer - -- Verify the child environment contains only the deployment allowlist and - explicitly provisioned local values. -- Verify no parent session or live tool handle is visible to the worker. -- Verify an unauthorized workspace reference cannot escape the worker boundary. - -### Authorization layer - -- Reject an unknown peer identity. -- Reject `delegation_depth > 0` with - `recursive_delegation_forbidden`. -- Reject capability widening when requested capabilities exceed any authority - intersection input. -- Verify the effective capability set is recorded in the authority digest. - -### Result and provenance layer - -- Reject a tampered request digest or result digest. -- Reject a missing or invalid callee signature. -- Reject `owner != executor` with `artifact_ownership_mismatch`. -- Accept a valid derived artifact only when it has a new owner and a valid - `derived_from` edge. -- Verify mutation side effects contain target, operation, executor, timestamp, - and outcome. - -### Wire-level replay and atomicity - -- Reject a reused nonce with `replay_detected`. -- Reject an expired request with `request_expired`. -- Reject a conflicting reuse of an idempotency key. -- Verify reservation and audit are atomic: if either persistence step fails, - execution does not start. -- Verify every rejection produces a durable audit event and no execution side - effect. -- Verify a retry of an accepted request does not create a second execution or - second successful result. - -## 11. Alternatives considered +## 7. Alternatives considered ### Forward a prompt through a chat platform -Rejected. Chat transport does not provide peer authentication, capability -attenuation, lifecycle control, replay protection, or signed provenance. It also -creates bot-loop and attribution problems. +Rejected for the formal path. Chat messages do not provide reliable task +identity, cancellation, replay, peer authentication, or provenance. Discord can +still be used to notify a human about a delegated task. -### Share the caller's ACP session with the callee +### Expose ACP directly to the remote agent -Rejected. Session sharing crosses conversation state and authority boundaries, -prevents independent cancellation and auditing, and makes it impossible to -prove which agent performed a tool call. +Rejected. ACP is OAB B's local runtime boundary, not a stable inter-OAB +contract. Direct exposure would leak local process and credential assumptions. -### Pass the caller's credentials or environment to the callee +### Use MCP for the outer protocol -Rejected. This is credential forwarding and authority cloning. The callee must -use its own locally provisioned identity. +Rejected for this user story. MCP models agent-to-tool/resource access; this +feature models OAB agent-to-remote-OAB task ownership, progress, cancellation, +and result provenance. OAB B may use MCP internally, but it is not the +inter-OAB delegation protocol. -### Let the callee delegate recursively +### Implement only local subagents -Rejected for the MVP. Recursive trees multiply cost and complicate provenance, -lease ownership, replay protection, and failure recovery. A future orchestrator -mode can be designed as a separate protocol revision with explicit depth and -budget controls. +Rejected as the MVP boundary. A local coordinator may be useful later, but it +does not let any running OAB agent connect to another remote OAB as requested. -### Use an unauthenticated HTTP endpoint with a shared static token +### Stateless HTTP request/response -Rejected for remote federation. A static token does not provide strong peer -identity, rotation, request signing, or useful provenance. It may be acceptable -as a narrowly scoped local development fixture, but not as the production wire -contract. +Deferred as the only transport. HTTP is appropriate for bootstrap and health, +but a long-running delegated task needs bidirectional progress, cancellation, +and reconnect. WebSocket is the MVP transport; another transport can map to the +same protocol later. -## 12. Prior art +## 8. Prior art -### OpenClaw +### Codex multi-agent v2 -OpenClaw documents a multi-agent Gateway model in [Multi-agent -routing](https://github.com/openclaw/openclaw/blob/main/docs/concepts/multi-agent.md). -Each agent has separate workspace, state directory, auth profiles, and session -store, and routing uses explicit bindings. OpenClaw also makes agent-to-agent -messaging opt-in and allowlisted, and per-agent tool policies can only further -restrict global policy. +Codex v2 provides the most relevant orchestration lessons: canonical task +paths, separate lifecycle operations, mailbox/event wake-ups, explicit context +inheritance, bounded concurrency, and structured collaboration events. -We adopt the separation of agent state, explicit routing, and monotonic -per-agent restrictions. OpenClaw's model is primarily co-located routing inside -one Gateway process; it does not define the cross-process OAB wire contract -needed here, particularly the request/result digest binding and callee-signed -artifact provenance. +- [Codex subagents](https://developers.openai.com/codex/concepts/subagents) +- [Codex multi-agent v2 handlers](https://github.com/openai/codex/tree/main/codex-rs/core/src/tools/handlers/multi_agents_v2) +- [Codex multi-agent specifications](https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/multi_agents_spec.rs) +- [Codex auditability issue](https://github.com/openai/codex/issues/28058) -### Hermes Agent +### OpenClaw + +OpenClaw demonstrates explicit per-agent routing, separate state/workspace +boundaries, and opt-in agent-to-agent messaging. OAB adopts those boundaries +but adds a remote protocol, request/result binding, and callee-signed +provenance. -Hermes documents a `delegate_task` tool in [Subagent -Delegation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/delegation.md). -It creates fresh child conversations and terminal sessions, limits concurrent -children, supports cancellation and background completion, and blocks recursive -delegation for leaf children by default. Hermes also records durable completion -records for background results and treats an interrupted child with unknown -side effects as `unknown` rather than claiming success. +- [OpenClaw multi-agent routing](https://github.com/openclaw/openclaw/blob/main/docs/concepts/multi-agent.md) +- [OpenClaw repository](https://github.com/openclaw/openclaw) -We adopt the fresh-context boundary, bounded concurrency/lifecycle, explicit -unknown outcome, and default-flat delegation. We diverge by making the OAB -boundary an authenticated peer protocol with capability intersection, no -credential forwarding, atomic replay reservation, and a callee signature that -binds the request digest to the result digest and authority digest. +### Hermes Agent -## 13. Consequences +Hermes demonstrates fresh child conversations, bounded concurrency, +cancellation/background completion, and explicit `unknown` handling when a +worker may have had side effects. OAB adopts these lifecycle principles while +keeping the inter-OAB trust and capability boundary explicit. + +- [Hermes subagent delegation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/delegation.md) +- [Hermes Agent repository](https://github.com/NousResearch/hermes-agent) + +## 9. Acceptance criteria for the implementation + +The implementation that follows this ADR is acceptable only when it demonstrates +all of the following: + +1. Agent A can submit a bounded task to a configured remote OAB B, wait for + progress, and receive one structured terminal result. +2. OAB B authenticates and authorizes the peer before creating a worker session. +3. A duplicate request reconnects to the existing task instead of executing it + twice; a conflicting request is rejected. +4. Progress and terminal events can be replayed from a durable cursor after a + WebSocket reconnect. +5. Cancellation, deadline expiry, worker failure, and `unknown` outcomes are + distinguishable and tested. +6. The child receives only its locally allowed environment and capabilities; + parent credentials, sessions, and live handles are absent. +7. No recursive delegation, capability widening, credential forwarding, or + caller-owned callee artifact claim is possible through the MVP API. +8. The signed result binds `request_digest`, `result_digest`, effective + capabilities, executor identity, task identity, and artifact/side-effect + provenance. +9. Per-peer and global concurrency/backpressure limits prevent delegation from + starving ordinary OAB chat work. +10. A human-readable audit record remains available even when the transport is + encrypted. + +## 10. Consequences ### Positive -- A parent can use a running OAB worker without treating it as a chat user. -- Authority is narrowed at every boundary and cannot be widened by the caller. -- Credentials and session state remain local to the executing agent. -- Results and artifacts can be independently verified and audited. -- The protocol supports a remote OAB user story while leaving discovery and - cross-organization federation for later, without changing the provenance model. +- Any running OAB agent can use another running OAB agent as a bounded remote + specialist without pretending it is a chat participant. +- The user-facing operation is simple while the host retains policy, + authentication, retry, and audit responsibilities. +- Codex v2's useful task-tree and mailbox semantics are available without + coupling OAB to an experimental vendor wire format. +- Read-only parallel exploration, review, testing, and summarization become + possible before introducing write coordination. ### Costs and residual risks -- Key management, canonicalization, nonce storage, and durable audit increase - implementation complexity compared with forwarding a prompt. -- A worker crash during an external side effect may remain `unknown`; the client - must reconcile status rather than retry blindly. -- The MVP requires preconfigured remote endpoints and key rotation remains an - operational responsibility until federation is designed. -- Artifact storage and garbage collection are not defined by this ADR; the - implementation must provide stable content-addressed references or defer - artifact-producing tasks. -- The ADR defines a contract, not a cryptographic implementation review. The - implementation PR must pin algorithms, key rotation behavior, and library - choices before enabling remote federation. - -## 14. Implementation sequence - -1. Add protocol types, canonical digest helpers, stable error codes, and negative - schema tests. -2. Add HTTPS/WebSocket transport, remote peer authentication, and the deny-by- - default delegation policy. -3. Add atomic nonce/idempotency reservation plus audit/outbox persistence. -4. Add the worker execution bridge over the existing ACP/session-pool lifecycle, - with local environment and workspace restrictions. -5. Add signed result validation, artifact provenance, progress, cancellation, - and crash reconciliation. -6. Add end-to-end tests for the state machine and all negative-test acceptance - criteria in §10. -7. Consider remote mTLS federation only after the local protocol is stable and - its signed-envelope fixtures are independently verifiable. - -## 15. References +- Remote identity, key rotation, durable cursors, and audit storage add + operational complexity. +- A worker crash during an external side effect can leave the result `unknown`; + callers must reconcile rather than blindly retry. +- The MVP requires preconfigured peers and does not solve global discovery or + federation. +- Shared writable worktrees and multi-writer coordination remain future design + work; the safe default is read-only delegation. + +## 11. Implementation sequence + +1. Define versioned request/result/event types, stable errors, task identity, and + capability/context policies. +2. Implement the backend-neutral delegation coordinator and local task registry. +3. Add authenticated WebSocket JSON-RPC transport with create, state/events, + reconnect, and cancel. +4. Bridge OAB B tasks to the existing ACP connection/session pool without + changing the child environment allowlist. +5. Add idempotency, durable cursors, signed result validation, and audit records. +6. Add negative and end-to-end tests covering the acceptance criteria. +7. Evaluate relay deployment and richer follow-up/message operations only after + the depth-one read-only MVP is stable. + +## 12. References - [OpenAB ACP connection and session pool](../../crates/openab-core/src/acp/connection.rs) - [OpenAB child-process environment policy](../../AGENTS.md#3-security--child-process-environment) -- [OpenClaw multi-agent routing](https://github.com/openclaw/openclaw/blob/main/docs/concepts/multi-agent.md) -- [Hermes Agent subagent delegation](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/delegation.md) -- [RFC 8785 — JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785) +- [Agent Client Protocol](https://agentclientprotocol.com/) +- [RFC 8785 - JSON Canonicalization Scheme](https://www.rfc-editor.org/rfc/rfc8785) - [Ed25519 signatures](https://www.rfc-editor.org/rfc/rfc8032) From 6162eaa31236b2fe2b1ee42c8e9f26c9e5d3afa3 Mon Sep 17 00:00:00 2001 From: chaodu-agent <274062505+chaodu-agent@users.noreply.github.com> Date: Wed, 22 Jul 2026 05:46:41 +0000 Subject: [PATCH 4/6] docs(adr): consider A2A delegation protocol --- docs/adr/oab-to-oab-delegation.md | 70 +++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md index d8bd26a33..02bdb9f54 100644 --- a/docs/adr/oab-to-oab-delegation.md +++ b/docs/adr/oab-to-oab-delegation.md @@ -302,6 +302,72 @@ feature models OAB agent-to-remote-OAB task ownership, progress, cancellation, and result provenance. OAB B may use MCP internally, but it is not the inter-OAB delegation protocol. +### Adopt the A2A protocol as the outer protocol + +A2A is a serious candidate for the inter-OAB wire protocol and should be +re-evaluated before implementation hardens the custom `delegate/*` method set. +A2A v1.0 is explicitly designed for communication between independent, +potentially opaque agents without exposing their internal state, memory, or +tools. Its task, message, artifact, Agent Card, authentication, cancellation, +and asynchronous update model overlaps substantially with this ADR. + +The conceptual mapping is direct: + +| OAB delegation operation | A2A operation or object | +| --- | --- | +| Describe a remote peer | `AgentCard` and, when enabled, `GetExtendedAgentCard` | +| Create a remote task | `SendMessage`, normally with `returnImmediately: true` | +| Read task state/result | `GetTask` and the A2A `Task` object | +| Stream progress and artifacts | `SendStreamingMessage` or `SubscribeToTask` | +| Cancel a task | `CancelTask` | +| Follow-up input | `Message` associated with `taskId` and `contextId` | +| Structured outputs | `Artifact` and `Part` | + +A2A also supports direct configuration of an Agent Card, which is compatible +with the MVP's configured, deny-by-default peer registry. An OAB deployment +can publish cards privately or use a preconfigured card URL; dynamic discovery +and federation remain out of scope. + +A2A is not a drop-in replacement for the complete OAB contract. The v1.0 +standard bindings are HTTP+JSON, JSON-RPC over HTTP, and gRPC, with SSE for +streaming; WebSocket is a custom binding. A custom binding must preserve all +A2A core operations, data-model semantics, event ordering, reconnection +behavior, authentication integration, and Agent Card declaration. Therefore, +the MVP should not select WebSocket merely because it is convenient if an +A2A standard binding meets the topology requirement. If a persistent WSS +transport remains necessary, it should be specified as an A2A custom binding, +not as an unrelated OAB protocol. + +A2A also does not replace OAB-specific policy. Its authentication and +authorization model relies on deployment and web-security practices, and its +Agent Card signature authenticates the card rather than signing an individual +task result. The core protocol does not define OAB's authority intersection, +credential/session/environment isolation, depth-one rule, signed request/result +binding, artifact ownership, or durable event cursor. A2A streaming supports +ordered events and resubscription, but disconnected clients may miss messages; +OAB's durable cursor and replay guarantee must remain an OAB requirement. +A2A's extension and metadata mechanisms are appropriate places to declare +these requirements, but credentials and live handles must never be forwarded +through them. + +The preferred direction is therefore **A2A 1.0 plus an OAB Delegation Profile**, +not raw A2A and not a second fully custom protocol: + +```text +OAB DelegationAdapter / A2A Client + -> A2A standard binding (prefer HTTP+JSON or JSON-RPC + SSE) + -> OAB DelegationAdapter / A2A Server + -> local ACP session +``` + +The profile should define OAB extensions for request idempotency, authority +attenuation, delegation depth, budgets and deadlines, principal chain, +durable cursors, signed provenance, artifact ownership, audit records, and +`unknown`/`expired` outcomes. Until that profile and its conformance tests are +agreed, this ADR keeps the transport and method boundary implementation-neutral +and treats A2A as the preferred protocol candidate rather than claiming that +A2A alone satisfies the security and reliability acceptance criteria. + ### Implement only local subagents Rejected as the MVP boundary. A local coordinator may be useful later, but it @@ -413,6 +479,10 @@ all of the following: ## 12. References +- [A2A v1.0 specification](https://a2a-protocol.org/latest/specification/) +- [A2A streaming and asynchronous operations](https://a2a-protocol.org/latest/topics/streaming-and-async/) +- [A2A protocol definition and schema](https://a2a-protocol.org/latest/definitions/) +- [A2A protocol repository](https://github.com/a2aproject/A2A) - [OpenAB ACP connection and session pool](../../crates/openab-core/src/acp/connection.rs) - [OpenAB child-process environment policy](../../AGENTS.md#3-security--child-process-environment) - [Agent Client Protocol](https://agentclientprotocol.com/) From 4b497ae4129454f811c233e5d22b12118d16b45c Mon Sep 17 00:00:00 2001 From: chaodu-agent <274062505+chaodu-agent@users.noreply.github.com> Date: Wed, 22 Jul 2026 13:21:55 +0000 Subject: [PATCH 5/6] docs(adr): make A2A gate and fallback normative --- docs/adr/oab-to-oab-delegation.md | 227 ++++++++++++++++++++---------- 1 file changed, 152 insertions(+), 75 deletions(-) diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md index 02bdb9f54..93d119776 100644 --- a/docs/adr/oab-to-oab-delegation.md +++ b/docs/adr/oab-to-oab-delegation.md @@ -33,88 +33,156 @@ provider is not part of this protocol decision. ## 2. Decision Add a dedicated **delegation adapter** with a client side in OAB A and a server -side in OAB B: +side in OAB B. The decision intent is to use **A2A 1.0 as the inter-OAB wire +candidate**, with an OAB Delegation Profile that carries the OAB-specific +security and reliability contract: ```text Agent A | | local delegate operation v -OAB A DelegationClient +OAB A DelegationAdapter / A2A Client | - | HTTPS bootstrap/health - | WebSocket + JSON-RPC 2.0 + | A2A standard HTTP+JSON or JSON-RPC over HTTP + SSE + | optional A2A custom WSS binding v -OAB B DelegationServer +OAB B DelegationAdapter / A2A Server | + | OAB Delegation Profile enforcement | local session and policy enforcement v Agent B via ACP ``` -The delegation adapter is not another `ChatAdapter`. Chat adapters translate -human-facing platform events; the delegation adapter translates a bounded local -agent operation into a remote OAB task and maps the remote lifecycle back to -the caller. +The adapter is not another `ChatAdapter`. Chat adapters translate human-facing +platform events; the delegation adapter translates a bounded local agent +operation into an A2A task and maps the remote lifecycle back to the caller. OAB B keeps its ACP connection, credentials, environment, workspace policy, and -tool approvals local. The outer delegation protocol must never expose ACP -directly to a remote peer or forward OAB A's session and credentials. +tool approvals local. Neither A2A nor the OAB profile may expose ACP directly to +a remote peer or forward OAB A's session, credentials, environment, or live +tool handles. -### 2.1 Transport and protocol boundary +This decision has a gate before a binding becomes normative. Until the gate +passes, the adapter and coordinator remain transport-neutral and no WSS, +SSE, or `delegate/*` method names are a compatibility promise. If the gate +fails, OAB falls back to a custom OAB protocol rather than shipping an A2A +wrapper that changes A2A core semantics or falsely claims standard +interoperability. -The MVP uses authenticated HTTPS for bootstrap/health and a long-lived -WebSocket carrying a versioned JSON-RPC 2.0 protocol. The transport provides -bidirectional progress and cancellation; JSON-RPC provides method and error -versioning without coupling the public contract to a vendor-specific ACP -extension. +### 2.1 Transport and protocol boundary -The initial method set is intentionally small: +The preferred standard binding is authenticated HTTP+JSON or JSON-RPC over HTTP +with SSE for server-to-client progress and artifact updates. Ordinary HTTP +operations carry task creation, state retrieval, and cancellation. The A2A +operation mapping is: ```text -delegate/describe capability and protocol negotiation -delegate/task.create submit one remote task -delegate/task.get read current task state -delegate/task.events wait/replay progress and terminal events -delegate/task.cancel request cancellation +remote task creation -> SendMessage(returnImmediately: true) +task state/result -> GetTask +task progress/events -> SendStreamingMessage or SubscribeToTask +task cancellation -> CancelTask +peer description -> AgentCard / GetExtendedAgentCard ``` -A deployment may connect directly: - -```text -OAB A -- outbound or inbound WSS --> OAB B -``` +A persistent WSS transport remains possible only as an A2A custom binding. Such +a binding must preserve the A2A core data model and operation semantics, declare +itself in the Agent Card, and document event ordering, reconnect, termination, +authentication, and error mapping. It must not silently become a second +`delegate/*` protocol. -or use a relay that pairs two outbound WebSockets: +A deployment may connect directly using the selected binding, or use a relay +when both OAB instances are outbound-only. The spike must compare the standard +HTTP/SSE topology with the existing outbound WSS pairing: ```text -OAB A -- outbound WSS --> relay <-- outbound WSS -- OAB B +OAB A -- outbound selected binding --> OAB B + +OAB A -- outbound binding --> relay <-- outbound binding -- OAB B ``` The relay is a deployment option, not a protocol dependency. Cloudflare Worker and Durable Object are possible implementations, but the ADR does not make Cloudflare APIs, pricing, or free-tier behavior part of the OAB contract. -### 2.2 Task identity and result mapping +### 2.2 OAB Delegation Profile normative requirements + +The profile is mandatory for an OAB peer; it is not optional metadata. It must +be represented by explicitly declared, versioned, negotiable extensions that a +peer can reject. The profile must define at least: + +- **Authority attenuation:** + `caller_grants intersection policy_A intersection policy_B intersection + request_capabilities intersection deployment_limits`; an empty intersection + is denied. +- **Isolation:** no credential, session, environment, arbitrary host path, or + live tool-handle forwarding. +- **Depth and scope:** maximum delegation depth one, bounded text input, + deadline, runtime/output budget, requested capabilities, and read-only default. +- **Idempotency:** normalized request identity, nonce replay protection, + duplicate task reuse, and rejection of conflicting reuse. +- **Durable observation:** event sequence/cursor, retention, reconnect replay, + gap detection, terminal-event durability, and no duplicate side effect from + observation or retry. +- **Outcome semantics:** explicit `unknown` and `expired` outcomes; neither may + be silently converted into success or blindly retried. +- **Provenance:** signed request/result digest binding, executor identity, + effective capabilities, artifact ownership, side effects, and audit record. +- **Conformance:** a peer that does not negotiate the required profile must be + rejected fail-closed; A2A core authentication or an Agent Card signature is + not a substitute for these requirements. + +The profile must not redefine A2A core task lifecycle, operation names, or +artifact semantics. If any required guarantee can only be implemented by +rewriting those core semantics, the A2A gate fails and the implementation must +use the custom OAB fallback instead. + +### 2.3 Implementation gate and fallback + +Before selecting a normative binding, run a time-bounded protocol spike. The +spike is an implementation gate, not an open-ended research phase, and must +produce a short conformance report with pass/fail evidence for all three gates: + +1. **Replay gate:** run the same `create -> progress -> cancel -> disconnect -> + reconnect` cases over HTTP/SSE and WSS where applicable. Durable cursors must + replay every event, including the terminal event, without restarting the ACP + task or repeating a side effect. +2. **Relay gate:** exercise direct and dual-outbound relay topologies. Measure + connection establishment, reconnect, backpressure, cancellation races, + latency, load-balancer/firewall compatibility, monitoring, and key rotation. + The selected A2A binding must not be materially more complex or less + operable than the WSS baseline without an explicit accepted trade-off. +3. **Profile gate:** count required fields, endpoints, and extensions and verify + that the profile remains a thin, explicitly negotiable addition. It must not + replace A2A lifecycle semantics or become a second protocol. + +A gate failure is conclusive: the implementation falls back to the custom OAB +protocol and records the failed criterion. There is no second indefinite debate +round. A gate pass permits adoption of A2A 1.0 plus the OAB profile, with the +selected binding and its conformance tests recorded before implementation is +called interoperable. + +### 2.4 Task identity and result mapping The caller supplies an idempotent `request_id`; the callee creates an immutable -`task_id`: +task identity: ```text -request_id -> task_id -> turn_id -> event sequence/cursor +request_id -> A2A taskId -> turn_id -> event sequence/cursor ``` - `request_id` deduplicates task creation at OAB B. -- `task_id` is the immutable storage and reconnect key generated by OAB B. +- A2A `taskId` is the immutable storage and reconnect key generated by OAB B. - `task_path` is an optional human-readable route such as `/root/security`; it can change without changing task ownership or event history. - `turn_id` identifies an execution turn if the task later supports follow-up work. -- `(task_id, turn_id, sequence)` identifies an event; a durable `cursor` allows - replay after reconnect. +- `(taskId, turn_id, sequence)` identifies an event; the OAB profile's durable + `cursor` allows replay after reconnect. -The caller waits by reading events or task state. Reconnecting with the same -`task_id` resumes observation; it does not submit a second task. A terminal +The caller waits by reading A2A events or task state. Reconnecting with the same +`taskId` resumes observation; it does not submit a second task. A terminal result includes at least `status`, `summary`, `error`, `artifacts`, and `executed_by`. @@ -302,14 +370,14 @@ feature models OAB agent-to-remote-OAB task ownership, progress, cancellation, and result provenance. OAB B may use MCP internally, but it is not the inter-OAB delegation protocol. -### Adopt the A2A protocol as the outer protocol +### A2A 1.0 plus OAB Delegation Profile (selected direction; gate pending) -A2A is a serious candidate for the inter-OAB wire protocol and should be -re-evaluated before implementation hardens the custom `delegate/*` method set. -A2A v1.0 is explicitly designed for communication between independent, -potentially opaque agents without exposing their internal state, memory, or -tools. Its task, message, artifact, Agent Card, authentication, cancellation, -and asynchronous update model overlaps substantially with this ADR. +The selected direction is A2A 1.0 as the inter-OAB wire candidate, subject to +the implementation gate in Section 2.3. A2A is explicitly designed for +communication between independent, potentially opaque agents without exposing +their internal state, memory, or tools. Its task, message, artifact, Agent Card, +authentication, cancellation, and asynchronous update model overlaps +substantially with this ADR. The conceptual mapping is direct: @@ -350,23 +418,12 @@ A2A's extension and metadata mechanisms are appropriate places to declare these requirements, but credentials and live handles must never be forwarded through them. -The preferred direction is therefore **A2A 1.0 plus an OAB Delegation Profile**, -not raw A2A and not a second fully custom protocol: - -```text -OAB DelegationAdapter / A2A Client - -> A2A standard binding (prefer HTTP+JSON or JSON-RPC + SSE) - -> OAB DelegationAdapter / A2A Server - -> local ACP session -``` - -The profile should define OAB extensions for request idempotency, authority -attenuation, delegation depth, budgets and deadlines, principal chain, -durable cursors, signed provenance, artifact ownership, audit records, and -`unknown`/`expired` outcomes. Until that profile and its conformance tests are -agreed, this ADR keeps the transport and method boundary implementation-neutral -and treats A2A as the preferred protocol candidate rather than claiming that -A2A alone satisfies the security and reliability acceptance criteria. +This is the normative direction for the adapter, not a claim that raw A2A +already satisfies OAB's contract. The selected binding becomes normative only +when the Section 2.3 gate and conformance tests pass. Until then, the transport +and method boundary remains implementation-neutral; if the gate fails, OAB +falls back to the custom protocol and must not claim standard A2A +interoperability. ### Implement only local subagents @@ -375,10 +432,12 @@ does not let any running OAB agent connect to another remote OAB as requested. ### Stateless HTTP request/response -Deferred as the only transport. HTTP is appropriate for bootstrap and health, -but a long-running delegated task needs bidirectional progress, cancellation, -and reconnect. WebSocket is the MVP transport; another transport can map to the -same protocol later. +Rejected as the only interaction model. A2A HTTP/JSON and JSON-RPC bindings +provide task creation, state retrieval, and cancellation over HTTP, while SSE +provides progress and artifact updates. The gate must verify whether that +standard binding satisfies OAB's durable replay and outbound-only relay needs. +A persistent WSS transport remains an optional A2A custom binding or the +fallback custom OAB protocol if the gate fails. ## 8. Prior art @@ -424,7 +483,8 @@ all of the following: 3. A duplicate request reconnects to the existing task instead of executing it twice; a conflicting request is rejected. 4. Progress and terminal events can be replayed from a durable cursor after a - WebSocket reconnect. + reconnect on the selected binding; the implementation does not restart an + ACP task or repeat a side effect during observation recovery. 5. Cancellation, deadline expiry, worker failure, and `unknown` outcomes are distinguishable and tested. 6. The child receives only its locally allowed environment and capabilities; @@ -438,6 +498,18 @@ all of the following: starving ordinary OAB chat work. 10. A human-readable audit record remains available even when the transport is encrypted. +11. The replay gate passes the same create/progress/cancel/disconnect/reconnect + cases for each candidate binding, including complete terminal-event replay, + no ACP restart, and no duplicate side effect. +12. The relay gate records direct and dual-outbound results for connection setup, + reconnect, backpressure, cancellation races, latency, load-balancer and + firewall compatibility, monitoring, and key rotation; no material + operability regression is accepted without an explicit trade-off. +13. The profile gate records required fields, endpoints, and extensions and + proves that the profile is thin, explicitly negotiable, rejectable, and does + not redefine A2A lifecycle or artifact semantics. +14. A failed gate records the failed criterion and selects the custom OAB + fallback; the implementation must not claim standard A2A interoperability. ## 10. Consequences @@ -465,16 +537,21 @@ all of the following: ## 11. Implementation sequence -1. Define versioned request/result/event types, stable errors, task identity, and - capability/context policies. +1. Define the A2A data-model mapping, version negotiation, OAB Profile + extension identifiers, stable errors, task identity, and context policy. 2. Implement the backend-neutral delegation coordinator and local task registry. -3. Add authenticated WebSocket JSON-RPC transport with create, state/events, - reconnect, and cancel. -4. Bridge OAB B tasks to the existing ACP connection/session pool without +3. Run the time-bounded spike against A2A HTTP/JSON or JSON-RPC + SSE and the + WSS baseline/custom binding where needed; publish replay, relay, and profile + gate evidence. +4. If the gate passes, select and declare the A2A binding in the Agent Card. If + it fails, record the criterion and select the custom OAB fallback without + claiming A2A interoperability. +5. Bridge OAB B tasks to the existing ACP connection/session pool without changing the child environment allowlist. -5. Add idempotency, durable cursors, signed result validation, and audit records. -6. Add negative and end-to-end tests covering the acceptance criteria. -7. Evaluate relay deployment and richer follow-up/message operations only after +6. Add idempotency, durable cursors, signed result validation, audit records, + and the OAB Profile conformance tests. +7. Add negative and end-to-end tests covering the acceptance criteria, then + evaluate relay deployment and richer follow-up/message operations only after the depth-one read-only MVP is stable. ## 12. References From 66a036b879b7735f1c5efff097e295027b02bb48 Mon Sep 17 00:00:00 2001 From: chaodu-agent Date: Wed, 22 Jul 2026 17:02:55 -0400 Subject: [PATCH 6/6] docs(adr): clarify delegation wire boundary --- docs/adr/oab-to-oab-delegation.md | 17 ++++++++++++++--- docs/refarch/kiro-with-defined-agents.md | 13 +++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/adr/oab-to-oab-delegation.md b/docs/adr/oab-to-oab-delegation.md index 93d119776..35a51f06f 100644 --- a/docs/adr/oab-to-oab-delegation.md +++ b/docs/adr/oab-to-oab-delegation.md @@ -110,7 +110,17 @@ Cloudflare APIs, pricing, or free-tier behavior part of the OAB contract. The profile is mandatory for an OAB peer; it is not optional metadata. It must be represented by explicitly declared, versioned, negotiable extensions that a -peer can reject. The profile must define at least: +peer can reject. + +The `DelegationRequest` and result-envelope examples in this ADR are internal +OAB domain models, not A2A HTTP+JSON or JSON-RPC wire messages. The binding maps +task creation to A2A `SendMessage` with a `Message` and +`returnImmediately: true`, and carries profile data only through declared, +negotiated extension metadata. The normative profile specification must define +that mapping before implementation, using A2A's camelCase JSON and enum +conventions rather than the domain model's field names. + +The profile must define at least: - **Authority attenuation:** `caller_grants intersection policy_A intersection policy_B intersection @@ -209,7 +219,7 @@ full distributed workflow engine. - Negative tests for unauthorized peers, capability widening, duplicate tasks, timeout, cancellation, reconnect, worker failure, and credential leakage. -A minimal request is conceptually: +An internal OAB `DelegationRequest` domain model is conceptually: ```json { @@ -284,7 +294,8 @@ An empty intersection is denied. OAB B executes only with its own locally provisioned identity. No credentials, session handles, environment maps, live tool objects, or arbitrary host paths cross the connection. -The result envelope is signed by OAB B and binds the request to its outcome: +OAB B creates a signed result envelope that binds the request to its outcome; +its internal OAB domain model is: ```json { diff --git a/docs/refarch/kiro-with-defined-agents.md b/docs/refarch/kiro-with-defined-agents.md index b7e2707d0..8cc629fff 100644 --- a/docs/refarch/kiro-with-defined-agents.md +++ b/docs/refarch/kiro-with-defined-agents.md @@ -72,7 +72,13 @@ The same pattern can back multiple **named OpenAB agents** with different model Use `terra` for an additional fixed, balanced tier or `luna` for a fixed high-throughput tier. The diagram below keeps two deployments for clarity; every added policy still requires its own OpenAB deployment. -Each named OAB agent is an independent deployment with its own `config.toml`, bot identity, process, and state. OpenAB does not infer task complexity in this pattern: a human can select the target bot directly, or a coordinator bot can delegate by explicitly mentioning the target bot. +Each named OAB agent is an independent deployment with its own `config.toml`, +bot identity, process, and state. OpenAB does not infer task complexity in this +pattern: a human can select the target bot directly, or an existing coordinator +bot can route a request through an explicit mention of the target bot. That +Discord path is message routing, not the formal delegation adapter: it has no +stable remote task identity, replay, or signed provenance. For bounded +inter-OAB delegation, use the [OAB Delegation Adapter ADR](../adr/oab-to-oab-delegation.md). ### Delegation architecture @@ -136,7 +142,10 @@ command = "kiro-cli" args = ["acp", "--agent", "sol", "--trust-all-tools"] ``` -For manual routing, users mention the appropriate bot. For coordinator-to-worker delegation over Discord, allow only explicit bot mentions on the receiving worker: +For manual routing, users mention the appropriate bot. Existing +coordinator-to-worker message routing over Discord allows only explicit bot +mentions on the receiving worker; it is not the formal delegation adapter +specified by the [OAB Delegation Adapter ADR](../adr/oab-to-oab-delegation.md): ```toml [discord]