Skip to content

Pre-1.0 public surface cleanup (stubs, x402 paths, auth naming) #156

Description

@shkangr

Tracking issue for pre-1.0 surface stabilization. Bundles three independent cleanup tracks that share one goal: stop shipping public exports that lie about what they do, and fix the naming/path asymmetries that will be expensive to change after 1.0.

This was surfaced by a comprehensive audit of @a2x/sdk (7-axis review covering API surface, architecture, spec conformance, code quality, tests, security, docs). Filing as one tracking issue per AGENTS.md's domain-bundling guidance; the three tracks may land as separate PRs.


Track 1 — Phase-2/3 stub modules ship as real exports

Five public exports across three subpaths are documented as real APIs but either throw on every method call or silently no-op. New users following the README/docs hit these immediately.

1a. @a2x/sdk/authDeviceFlowClient and AuthenticatedA2AClient throw on every method

packages/a2x/src/auth/device-flow-client.ts — every public method throws Error('... not yet implemented (Phase 3)'):

  • DeviceFlowClient (lines 50, 57, 63)
  • AuthenticatedA2AClient (lines 78, 84, 88, 92)

The subpath is wired up everywhere a user would discover it:

  • packages/a2x/package.json#exports['./auth']
  • packages/a2x/README.md Exports table
  • packages/a2x/docs/guides/advanced/authentication.md (documents AuthenticatedA2AClient as the recommended way to add token acquisition/refresh to A2XClient)

1b. runner/database-session.tsDatabaseSessionService throws on every method

packages/a2x/src/runner/database-session.ts lines 11/15/19/23 — every method throws Error('DatabaseSessionService not yet implemented (Phase 2)').

Exported from runner/index.ts:14 as the swap-able alternative to InMemorySessionService. Anyone reading the runner barrel sees it as a real session backend.

1c. agent/{parallel,sequential,loop}-agent.ts — silent no-ops (worse than throwing)

packages/a2x/src/agent/parallel-agent.ts:25-28, sequential-agent.ts:25-28, loop-agent.ts:29-32run() immediately yields { type: 'done' } and returns. Sub-agents are accepted in the constructor and silently ignored.

This is more dangerous than a throw: a developer building a multi-agent pipeline gets back done events with no output and no error, then debugs why their sub-agents "didn't produce anything."

All three are exported from agent/index.ts:14-19 and re-exported from the package root.

Proposed resolution (per stub)

For each module, pick exactly one:

  • Implement before 1.0. File a sub-issue with the implementation plan.
  • Remove from public surface now. Delete the file (or move to an internal _internal/ directory that isn't re-exported), drop from barrels, drop from package.json#exports if applicable, remove the corresponding doc page, drop from README's Exports table.

Half-measures to avoid: leaving the export in place with a comment, marking @deprecated, or shipping a runtime warning. Pre-1.0 we can take the breaking change cleanly.


Track 2 — x402 is exported from two paths simultaneously

packages/a2x/src/index.ts:64-68 re-exports the entire ./x402/index.js from the package root, while package.json#exports['./x402'] also declares it as a dedicated subpath. The intent of the subpath was tree-shaking isolation for callers who don't use payments, but the root re-export defeats that.

Documentation reflects the confusion:

  • README.md:55 and packages/a2x/README.md:111 import x402 symbols from @a2x/sdk
  • packages/a2x/README.md:40 advertises x402 as a feature with no path qualifier
  • The @a2x/sdk/x402 subpath also exists, used by some samples and the migration guide

Proposed resolution

Pick one canonical path before 1.0. Two reasonable options:

  • Option A — root only. Drop the ./x402 subpath from package.json#exports. x402 is a first-class feature; the tree-shaking concern is small if the bundler is modern.
  • Option B — subpath only. Remove export * from './x402/index.js' from src/index.ts and update all docs/samples to import from @a2x/sdk/x402. Preserves isolation; minor breaking change for anyone already importing from the root.

Either choice + update README, root README, x402 guide, migration guide, and samples to use only the chosen path.


Track 3 — Server *Authorization vs client *AuthScheme naming asymmetry

The SDK has two parallel hierarchies that should mirror each other but use different suffixes and have different gaps.

Server side (packages/a2x/src/security/index.ts) — suffix *Authorization:

  • ApiKeyAuthorization, HttpBearerAuthorization
  • OAuth2{AuthorizationCode,ClientCredentials,DeviceCode}Authorization
  • OpenIdConnectAuthorization, MutualTlsAuthorization

Client side (packages/a2x/src/client/index.ts) — suffix *AuthScheme:

  • AuthScheme, ApiKeyAuthScheme, HttpBearerAuthScheme, HttpBasicAuthScheme
  • OAuth2{DeviceCode,AuthorizationCode,ClientCredentials,Implicit,Password}AuthScheme
  • OpenIdConnectAuthScheme

Two problems

Naming. A user who learns one side has to relearn the other. Searching for Authorization misses client matches; searching for AuthScheme misses server matches.

Gaps. The hierarchies are not 1:1:

Scheme Server (*Authorization) Client (*AuthScheme)
API key yes yes
HTTP Bearer yes yes
HTTP Basic no yes
OAuth2 Authorization Code yes yes
OAuth2 Client Credentials yes yes
OAuth2 Device Code yes yes
OAuth2 Implicit no yes
OAuth2 Password no yes
OpenID Connect yes yes
Mutual TLS yes no

An SDK-built agent can advertise OAuth2 Implicit / Password / HTTP Basic on its AgentCard but cannot use the SDK to enforce any of them; conversely a client cannot use the SDK to satisfy a Mutual-TLS-protected agent.

Proposed resolution

  1. Pick one suffix. Either *AuthScheme (matches the OpenAPI / A2A spec term) or *Authorization (more idiomatic for server-side enforcement). Apply consistently to both sides.
  2. Close the gaps. For each of {HTTP Basic, OAuth2 Implicit, OAuth2 Password, Mutual TLS}, decide: implement the missing side, or remove the existing one if the scheme isn't going to be supported end-to-end.

Pre-1.0 is the only window for this; renaming a public symbol after 1.0 costs every consumer.


Acceptance criteria

  • Track 1: each of the five stub exports is either implemented (with tests) or removed from the public surface (package.json#exports, barrels, README, docs).
  • Track 2: x402 is reachable from exactly one path; all docs, samples, and READMEs updated accordingly.
  • Track 3: server and client auth class hierarchies use one consistent suffix; gap table is either fully filled or symmetrically pruned.
  • pnpm test and pnpm typecheck pass.
  • pnpm lint clean.
  • packages/a2x/docs/manifest.json and listed guides reflect the final state (per AGENTS.md docs-sync policy).
  • Each user-visible change has a changeset entry.

Out of scope

References

Findings traced from the audit at .omc/research/research-20260506-a2x/ (S1-F1, S1-F2, S1-F4, S1-F11, S2-F6, S2-F7).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions