Skip to content

refactor: decouple InvokableAgent from A2A dependency#2

Closed
pgrayy wants to merge 92 commits intomainfrom
agent-tasks/agent-interface-hierarchy
Closed

refactor: decouple InvokableAgent from A2A dependency#2
pgrayy wants to merge 92 commits intomainfrom
agent-tasks/agent-interface-hierarchy

Conversation

@pgrayy
Copy link
Copy Markdown
Owner

@pgrayy pgrayy commented Mar 17, 2026

Motivation

Consumers of the multiagent module (Graph, Swarm) were transitively pulling in @a2a-js/sdk types because InvokableAgent.stream() yielded AgentStreamEvent | A2AStreamEvent. This made the A2A package effectively required for anyone using multi-agent orchestration, even when no A2A agents were involved.

Changes

InvokableAgent.stream() now yields StreamEvent, an empty abstract base class with no fields. This removes the transitive dependency on A2A types from the public interface.

To preserve type-safe event handling in multi-agent orchestration, NodeStreamUpdateEvent.event is renamed to NodeStreamUpdateEvent.inner with a new NodeInnerEvent discriminated union type. The union is tagged by source:

type NodeInnerEvent =
  | (AgentStreamEvent & { readonly source: 'agent' })
  | (Exclude<MultiAgentStreamEvent, NodeStreamUpdateEvent> & { readonly source: 'multiAgent' })
  | (StreamEvent & { readonly source: 'custom' })

Consumers switch on inner.source first, then inner.type for full discriminated union narrowing:

for await (const event of graph.stream('hello')) {
  if (event.type === 'nodeStreamUpdateEvent') {
    if (event.inner.source === 'agent') {
      // event.inner is AgentStreamEvent, narrows on .type
      if (event.inner.type === 'modelStreamUpdateEvent') { ... }
    }
  }
}

A2AExecutor uses instanceof narrowing internally since it receives StreamEvent from InvokableAgent.stream(). This is an internal implementation detail, not consumer-facing.

Breaking Changes

  • NodeStreamUpdateEvent.event renamed to NodeStreamUpdateEvent.inner
  • NodeStreamUpdateEvent.inner type changed from AgentStreamEvent | A2AStreamEvent | ... to NodeInnerEvent (source-tagged union)
  • InvokableAgent.stream() yield type changed from AgentStreamEvent to StreamEvent

pgrayy and others added 30 commits January 26, 2026 10:04
…strands-agents#445)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ands-agents#450)

Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
Co-authored-by: Lionel Seguin <lionel.seguin@contentsquare.com>
…ts#518)

Co-authored-by: Containerized Agent <agent@containerized-strands.local>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…gents#541)

Co-authored-by: Strands Agent <217235299+strands-agent@users.noreply.github.com>
dependabot bot and others added 29 commits March 10, 2026 15:11
…gents#496)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…nts#526)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…gents#635)

Co-authored-by: Strands Agent <217235299+strands-agent@users.noreply.github.com>
…#619)

Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
…-agents#601)

Co-authored-by: Josh Samuel <3156090+jsamuel1@users.noreply.github.com>
Co-authored-by: Mackenzie Zastrow <zastrowm@users.noreply.github.com>
… rename MultiAgentBase to MultiAgent

- Rename AgentBase to InvokableAgent for invoke/stream capability
- Rename AgentData to LocalAgent for local state/messages/tools/hooks
- Agent implements both; A2AAgent implements InvokableAgent only
- Remove type discriminators from InvokableAgent and MultiAgent
- Rename MultiAgentBase to MultiAgent, rename base.ts to multiagent.ts
- Add A2AResultEvent so A2AAgent no longer fakes LocalAgent fields
- Remove A2AStreamUpdateEvent from AgentStreamEvent union
- Remove InvokableAgent and MultiAgent from public exports
- Update Graph._resolveNodes to use instanceof checks

BREAKING CHANGE: AgentBase renamed to InvokableAgent, AgentData renamed
to LocalAgent, MultiAgentBase renamed to MultiAgent. Type discriminators
removed from agent and multi-agent interfaces. AgentStreamEvent no longer
includes A2AStreamUpdateEvent.
Replace the A2A-dependent event union on InvokableAgent.stream() with
the abstract StreamEvent base class, removing the transitive @a2a-js/sdk
requirement from multiagent consumers.

- InvokableAgent.stream() now yields StreamEvent (empty abstract class)
- NodeStreamUpdateEvent.event renamed to .inner (NodeInnerEvent type)
- NodeInnerEvent is a source-tagged discriminated union:
  - source: 'agent' for AgentStreamEvent
  - source: 'multiAgent' for MultiAgentStreamEvent
  - source: 'custom' for third-party StreamEvent subclasses
- A2AExecutor uses instanceof narrowing (internal, not consumer-facing)
- Export NodeInnerEvent from multiagent barrel
@pgrayy pgrayy closed this Mar 17, 2026
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.