Skip to content

Advertise delegated A2A write capabilities - #2831

Open
3mdistal wants to merge 7 commits into
mainfrom
codex/a2a-delegated-action-capabilities
Open

Advertise delegated A2A write capabilities#2831
3mdistal wants to merge 7 commits into
mainfrom
codex/a2a-delegated-action-capabilities

Conversation

@3mdistal

@3mdistal 3mdistal commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Problem

Workspace agents discover what sibling apps can do from authenticated Agent-to-Agent (A2A) cards. Today those cards describe only the small set of read-only Actions that a caller may invoke directly. An app can already define an authenticated write Action and opt it into publicAgent, but that capability disappears from discovery entirely.

As a result, a coordinating agent cannot deterministically learn that the owning app can create or update a resource. It either guesses from prose, accumulates app-specific routing knowledge, or reaches for a granular direct-action design. That undercuts the framework's intended model: define an Action once, then delegate work to the agent that owns the app and its data.

Approach

Project one Action definition into two distinct authenticated A2A capability shapes:

  • exact, connector-selected reads remain directly invocable and include their input schemas;
  • explicitly exposed writes are advertised without schemas as message-only capabilities.

The caller learns that the receiving app can perform the work, but must send a natural-language objective. The receiving agent keeps ownership of interpretation, local tool choice, validation, authorization, and approval. Anonymous discovery remains unchanged. Direct reads appear only for audience-bound JWTs that satisfy the same boundary enforced by actions/invoke; verified legacy JWTs without an audience can discover message-delegated capabilities but not direct-read schemas.

What changed

  • Added an authenticated delegated-capability filter derived directly from publicAgent: { expose: true, readOnly: false, requiresAuth: true }.
  • Kept agentTool: false and externalAgents.denyActions as authenticated-surface vetoes.
  • Combined direct reads and delegated writes in authenticated cards while omitting every write schema.
  • Aligned direct-read card disclosure with the endpoint's audience-bound JWT requirement while preserving legacy message delegation.
  • Canonicalized direct-read audiences to the receiver's exact base URL, including path-mounted workspace apps and manifests that contain the full A2A endpoint.
  • Updated the A2A type contract, mount documentation, and protocol skill to distinguish direct reads from message-only writes and to discourage a second capability registry.
  • Added a Core patch changeset and focused regression coverage.

Safety and operations

This does not add a direct write execution path. actions/invoke still re-filters through the existing read-only gate, and tests prove the advertised mutation is absent from both direct invocation and the anonymous card. Consequential or approval-gated work remains owned by the receiving agent's normal Action path.

The discovery renderer keeps its existing summary/detail/description caps, and write capabilities carry no input schemas, avoiding a mutation-schema flood in agent context. An authenticated token without an audience no longer receives a direct-read contract it cannot invoke. There are no schema migrations, data writes, credential changes, permission changes, or backfills. Rollback is a code revert.

Verification

  • Five focused Vitest files pass: 98 tests covering authenticated-card selection, anonymous-card isolation, audience-less legacy tokens, path-mounted and explicit-endpoint receiver audiences, direct-read enforcement, message-only write projection, policy vetoes, and lazy capability rendering.
  • pnpm --filter @agent-native/core typecheck passes on commit bab695266.
  • pnpm guard:workspace-skills passes, proving generated guidance remains synchronized.
  • pnpm test:content-product-impact passes all 30 declaration and workflow-policy tests.
  • pnpm guard:no-silent-coercion passes on the exact head. The full guard matrix still has one unrelated guard:i18n-catalogs failure on templates/slides/app/components/editor/SlideEditor.tsx:5012, unchanged from origin/main and absent from this PR's diff.

Review focus

  • Is publicAgent the right single opt-in for message-only write discovery, without requiring a second connector registry?
  • Does omitting write schemas create a sufficiently hard and legible boundary between capability discovery and direct invocation?
  • Do audience-bound, path-mounted, and legacy tokens now see exactly the capabilities they can use?
  • Are agentTool: false, the deny list, authenticated cards, and anonymous cards applied at the correct disclosure boundaries?

Follow-up

A separately scoped second PR will annotate Content's existing database discovery/intake Actions, expose user-authored database descriptions, and prove the deployed delegated intake workflow end to end. This framework PR intentionally does not claim that Content workflow yet.

content_product_impact:
  lane: contract_fulfillment
  features:
    - content.feature.collect-structured-input
  capabilities:
    - content.agent.action-parity
  record_change: none
  proof:
    - 98 focused A2A capability tests pass on commit bab695266
    - Core typecheck and workspace-skill sync pass
    - Content impact policy suite passes 30 tests
    - the branch-owned no-silent-coercion guard passes; the only full-matrix failure is an unchanged origin/main Slides i18n finding
  rationale: This adds the shared discovery substrate Content needs to delegate structured database intake through its existing Action surface without granting direct cross-app writes or changing the accepted Content contract.

builder-io-integration[bot]

This comment was marked as outdated.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Here's a visual recap of what changed:

Visual recap

Open the full interactive recap

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

builder-io-integration[bot]

This comment was marked as outdated.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@builder-io-integration builder-io-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder reviewed your changes and has a few items to flag 🟡

Review Details

Incremental Code Review Summary

The latest revision fixes the previously reported explicit-endpoint audience issue. canonicalA2AAudience now strips both /_agent-native/a2a and /a2a, and the added capability-cache test verifies that a full endpoint URL produces the receiver’s base-path audience. I resolved that stale review thread.

Two independent review passes found no new confirmed bugs in the current changes. They verified the endpoint and base-path canonicalization, authenticated card behavior, token issuance/verification consistency, and focused A2A test coverage. The existing delegated-write external-agent policy finding remains open and was not reposted.

Risk remains standard because this is shared A2A authentication and capability-discovery behavior.

✅ No new actionable findings in this incremental review.

🧪 Browser testing: Skipped — PR only modifies backend/framework logic, tests, types, docs, and a changeset; no user-facing UI impact.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

@netlify

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants