Skip to content

Commit 1e8f5d6

Browse files
SimplyLizclaude
andauthored
feat(a2a): spec-compliant A2A v0.3 implementation (#1)
* docs: add CognitiveVault to Memory Backends and MessageBoard sections in README * feat(a2a): spec-compliant A2A v0.3 implementation Rewrites the A2A module to align with the Agent2Agent v0.3 specification. Server: - JSON-RPC 2.0 dispatch on single endpoint (replaces REST-style URL routing) - Agent card at /.well-known/agent-card.json (with legacy path fallback) - All spec methods: message/send, message/stream, tasks/get, tasks/cancel, tasks/resubscribe, push notification config CRUD, getAuthenticatedExtendedCard - SSE streaming with heartbeat and subscriber management - Authentication middleware support - contextFactory for wiring real AgentContext into task execution - Push notifications with Bearer/Basic auth - Multi-turn via input-required state and task resume - Proper JSON-RPC error codes (1001-1009) - CORS support Client: - Agent card discovery with canonical + legacy path fallback - Polling and SSE streaming execution modes - Multi-turn with onInputRequired callback - Bearer and API key authentication - cancelA2ATask helper - onStreamEvent callback for streaming updates Types: - Full spec-compliant type definitions in dedicated types.ts - AgentCard with kind, protocolVersion, provider, securitySchemes, security - All Part types: TextPart, FilePart (bytes + uri), DataPart - All task states: submitted, working, input-required, auth-required, completed, failed, canceled, rejected - Artifact schema with id, name, description, parts, metadata - contextId for conversation grouping - A2A error codes enum Agent Card: - ToAgentCardOptions for provider, security, streaming, push notifications - Spec-compliant card generation with all optional fields Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(a2a): address review findings — stream leak, timeout, skill routing - Fix resource leak in client SSE streaming: rewrite recursive .then() chain as async while loop with reader.cancel() in finally block (#2, #5) - Add timeout enforcement in server executeTask via Promise.race using agent.timeoutMs (default 5min), prevents tasks hanging forever (#3) - Add skillId-based agent resolution: when MessageSendParams includes skillId, server matches against agent capabilities; returns UNSUPPORTED_SKILL (1004) error if no match (#4) - Add test for multi-agent skill routing (correct dispatch + error case) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c51c2fc commit 1e8f5d6

8 files changed

Lines changed: 1987 additions & 232 deletions

File tree

README.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,15 +337,35 @@ swarm.register(externalAgent)
337337
### Without Memory (default)
338338
Every execution is stateless. Results returned and forgotten.
339339

340-
### With ANCS (coming soon)
341-
Persistent cognitive memory with truth tracking, entity graphs, and importance decay.
340+
### With CognitiveVault
341+
Persist agent messages across sessions and processes. Agents in different SwarmWire executions — or even MCP tools like Claude Code — see each other's work.
342+
343+
```typescript
344+
import { Swarm } from 'swarmwire'
345+
import { CognitiveVaultBoard } from 'swarmwire/adapters'
346+
347+
const board = new CognitiveVaultBoard({
348+
apiUrl: 'https://cognitive-vault.com',
349+
apiKey: process.env.CV_API_KEY!,
350+
vaultId: 'vault-123',
351+
})
352+
await board.hydrate() // catch up on prior messages
353+
354+
const swarm = new Swarm({ providers, board })
355+
// All agent messages now persist to CognitiveVault
356+
```
357+
358+
Falls back to local file (`.swarmwire/board.jsonl`) when CV is unreachable. See [CognitiveVault integration guide](./docs/cognitive-vault-integration.md).
359+
360+
### With ANCS
361+
Persistent cognitive memory with truth tracking, entity graphs, and importance decay. ANCS can run alongside CognitiveVault as its knowledge intelligence backend.
342362
```typescript
343363
import { Swarm, ancsMemory } from 'swarmwire'
344364

345365
const swarm = new Swarm({
346366
providers,
347367
memory: ancsMemory({
348-
url: 'http://localhost:3000',
368+
url: 'http://localhost:3100',
349369
tenantId: 'my-project',
350370
}),
351371
})
@@ -440,6 +460,8 @@ async execute(input: string, ctx: AgentContext) {
440460
```
441461

442462
Message types: `finding`, `warning`, `question`, `answer`, `coordination`, `status`, `custom`.
463+
464+
**Persistence options:** The default `MessageBoard` is in-memory only. Use `FileBoard` for local persistence or `CognitiveVaultBoard` for cross-machine, cross-session durability. See [Adapters](./docs/adapters.md).
443465
Priorities: `normal`, `high`, `urgent`.
444466

445467
The full `MessageBoard` class is also available standalone:

src/a2a/agent-card.ts

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,71 @@
44
*/
55

66
import type { Agent } from '../types/agent.js'
7+
import type { AgentCard, AgentProvider, SecurityScheme } from './types.js'
78

8-
export interface AgentCard {
9-
name: string
10-
description: string
11-
url: string
12-
version: string
13-
capabilities: AgentCapabilities
14-
skills: AgentSkill[]
15-
defaultInputModes: string[]
16-
defaultOutputModes: string[]
17-
}
18-
19-
export interface AgentCapabilities {
9+
export interface ToAgentCardOptions {
10+
/** Protocol version to advertise. Default '0.3.0' */
11+
protocolVersion?: string
12+
/** Agent provider info */
13+
provider?: AgentProvider
14+
/** Icon URL */
15+
iconUrl?: string
16+
/** Documentation URL */
17+
documentationUrl?: string
18+
/** Security schemes supported */
19+
securitySchemes?: Record<string, SecurityScheme>
20+
/** Security requirements (OR-of-ANDs) */
21+
security?: Record<string, string[]>[]
22+
/** Whether to enable streaming */
2023
streaming?: boolean
24+
/** Whether to enable push notifications */
2125
pushNotifications?: boolean
26+
/** Whether to include state transition history */
2227
stateTransitionHistory?: boolean
23-
}
24-
25-
export interface AgentSkill {
26-
id: string
27-
name: string
28-
description: string
29-
tags: string[]
30-
examples?: string[]
28+
/** Whether the agent supports an extended card for authenticated callers */
29+
supportsAuthenticatedExtendedCard?: boolean
30+
/** Default input MIME types. Default ['text/plain', 'application/json'] */
31+
defaultInputModes?: string[]
32+
/** Default output MIME types. Default ['text/plain', 'application/json'] */
33+
defaultOutputModes?: string[]
3134
}
3235

3336
/**
3437
* Generate an A2A Agent Card from a SwarmWire Agent.
3538
*/
36-
export function toAgentCard(agent: Agent, baseUrl: string): AgentCard {
37-
return {
39+
export function toAgentCard(agent: Agent, baseUrl: string, options?: ToAgentCardOptions): AgentCard {
40+
const opts = options ?? {}
41+
42+
const card: AgentCard = {
43+
kind: 'agentCard',
3844
name: agent.name,
3945
description: agent.role,
40-
url: `${baseUrl}/a2a/${agent.name}`,
46+
url: `${baseUrl}`,
4147
version: '0.1.0',
48+
protocolVersion: opts.protocolVersion ?? '0.3.0',
4249
capabilities: {
43-
streaming: false,
44-
pushNotifications: false,
45-
stateTransitionHistory: true,
50+
streaming: opts.streaming ?? false,
51+
pushNotifications: opts.pushNotifications ?? false,
52+
stateTransitionHistory: opts.stateTransitionHistory ?? true,
4653
},
4754
skills: agent.capabilities.map((cap) => ({
4855
id: cap,
4956
name: cap,
5057
description: `Capability: ${cap}`,
5158
tags: [cap],
5259
})),
53-
defaultInputModes: ['text/plain', 'application/json'],
54-
defaultOutputModes: ['text/plain', 'application/json'],
60+
defaultInputModes: opts.defaultInputModes ?? ['text/plain', 'application/json'],
61+
defaultOutputModes: opts.defaultOutputModes ?? ['text/plain', 'application/json'],
5562
}
63+
64+
if (opts.provider) card.provider = opts.provider
65+
if (opts.iconUrl) card.iconUrl = opts.iconUrl
66+
if (opts.documentationUrl) card.documentationUrl = opts.documentationUrl
67+
if (opts.securitySchemes) card.securitySchemes = opts.securitySchemes
68+
if (opts.security) card.security = opts.security
69+
if (opts.supportsAuthenticatedExtendedCard) card.supportsAuthenticatedExtendedCard = true
70+
71+
return card
5672
}
73+
74+
export type { AgentCard, AgentCapabilities, AgentSkill } from './types.js'

0 commit comments

Comments
 (0)