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/auth — DeviceFlowClient 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.ts — DatabaseSessionService 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-32 — run() 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
- Pick one suffix. Either
*AuthScheme (matches the OpenAPI / A2A spec term) or *Authorization (more idiomatic for server-side enforcement). Apply consistently to both sides.
- 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
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).
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/auth—DeviceFlowClientandAuthenticatedA2AClientthrow on every methodpackages/a2x/src/auth/device-flow-client.ts— every public method throwsError('... 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.mdExports tablepackages/a2x/docs/guides/advanced/authentication.md(documentsAuthenticatedA2AClientas the recommended way to add token acquisition/refresh toA2XClient)1b.
runner/database-session.ts—DatabaseSessionServicethrows on every methodpackages/a2x/src/runner/database-session.tslines 11/15/19/23 — every method throwsError('DatabaseSessionService not yet implemented (Phase 2)').Exported from
runner/index.ts:14as the swap-able alternative toInMemorySessionService. 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-32—run()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
doneevents 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-19and re-exported from the package root.Proposed resolution (per stub)
For each module, pick exactly one:
_internal/directory that isn't re-exported), drop from barrels, drop frompackage.json#exportsif 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 —
x402is exported from two paths simultaneouslypackages/a2x/src/index.ts:64-68re-exports the entire./x402/index.jsfrom the package root, whilepackage.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:55andpackages/a2x/README.md:111import x402 symbols from@a2x/sdkpackages/a2x/README.md:40advertises x402 as a feature with no path qualifier@a2x/sdk/x402subpath also exists, used by some samples and the migration guideProposed resolution
Pick one canonical path before 1.0. Two reasonable options:
./x402subpath frompackage.json#exports. x402 is a first-class feature; the tree-shaking concern is small if the bundler is modern.export * from './x402/index.js'fromsrc/index.tsand 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
*Authorizationvs client*AuthSchemenaming asymmetryThe 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,HttpBearerAuthorizationOAuth2{AuthorizationCode,ClientCredentials,DeviceCode}AuthorizationOpenIdConnectAuthorization,MutualTlsAuthorizationClient side (
packages/a2x/src/client/index.ts) — suffix*AuthScheme:AuthScheme,ApiKeyAuthScheme,HttpBearerAuthScheme,HttpBasicAuthSchemeOAuth2{DeviceCode,AuthorizationCode,ClientCredentials,Implicit,Password}AuthSchemeOpenIdConnectAuthSchemeTwo problems
Naming. A user who learns one side has to relearn the other. Searching for
Authorizationmisses client matches; searching forAuthSchememisses server matches.Gaps. The hierarchies are not 1:1:
*Authorization)*AuthScheme)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
*AuthScheme(matches the OpenAPI / A2A spec term) or*Authorization(more idiomatic for server-side enforcement). Apply consistently to both sides.Pre-1.0 is the only window for this; renaming a public symbol after 1.0 costs every consumer.
Acceptance criteria
package.json#exports, barrels, README, docs).x402is reachable from exactly one path; all docs, samples, and READMEs updated accordingly.pnpm testandpnpm typecheckpass.pnpm lintclean.packages/a2x/docs/manifest.jsonand listed guides reflect the final state (per AGENTS.md docs-sync policy).Out of scope
DefaultRequestHandler/AgentExecutor(separate issue, surfaced by the same audit).References
Findings traced from the audit at
.omc/research/research-20260506-a2x/(S1-F1, S1-F2, S1-F4, S1-F11, S2-F6, S2-F7).