@artinet/sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -4
- package/LICENSE +201 -21
- package/README.md +281 -413
- package/bin/artinet-metadata-validator-0.0.1.tgz +0 -0
- package/dist/client/a2a-client.d.ts +3 -4
- package/dist/client/a2a-client.d.ts.map +1 -1
- package/dist/client/a2a-client.js +9 -9
- package/dist/client/a2a-client.js.map +1 -1
- package/dist/client/interfaces/client.d.ts +95 -0
- package/dist/client/interfaces/client.d.ts.map +1 -1
- package/dist/server/a2a-server.d.ts +7 -0
- package/dist/server/a2a-server.d.ts.map +1 -1
- package/dist/server/a2a-server.js +34 -4
- package/dist/server/a2a-server.js.map +1 -1
- package/dist/server/interfaces/params.d.ts +46 -9
- package/dist/server/interfaces/params.d.ts.map +1 -1
- package/dist/server/interfaces/params.js.map +1 -1
- package/dist/server/interfaces/server.d.ts +1 -0
- package/dist/server/interfaces/server.d.ts.map +1 -1
- package/dist/server/lib/express-server.d.ts.map +1 -1
- package/dist/server/lib/express-server.js +2 -2
- package/dist/server/lib/express-server.js.map +1 -1
- package/dist/server/lib/json-middleware.d.ts +2 -18
- package/dist/server/lib/json-middleware.d.ts.map +1 -1
- package/dist/server/lib/json-middleware.js +2 -179
- package/dist/server/lib/json-middleware.js.map +1 -1
- package/dist/server/lib/middleware/a2a-methods.d.ts +7 -0
- package/dist/server/lib/middleware/a2a-methods.d.ts.map +1 -0
- package/dist/server/lib/middleware/a2a-methods.js +103 -0
- package/dist/server/lib/middleware/a2a-methods.js.map +1 -0
- package/dist/server/lib/middleware/factory.d.ts +12 -0
- package/dist/server/lib/middleware/factory.d.ts.map +1 -0
- package/dist/server/lib/middleware/factory.js +59 -0
- package/dist/server/lib/middleware/factory.js.map +1 -0
- package/dist/server/lib/storage/file.js +1 -0
- package/dist/server/lib/storage/file.js.map +1 -1
- package/dist/server/lib/storage/memory.js +1 -3
- package/dist/server/lib/storage/memory.js.map +1 -1
- package/dist/transport/streaming/event-stream.d.ts.map +1 -1
- package/dist/transport/streaming/event-stream.js +31 -14
- package/dist/transport/streaming/event-stream.js.map +1 -1
- package/dist/types/extended-schema.d.ts +2 -1
- package/dist/types/extended-schema.d.ts.map +1 -1
- package/dist/types/extended-schema.js.map +1 -1
- package/dist/utils/api/register.d.ts +25 -0
- package/dist/utils/api/register.d.ts.map +1 -0
- package/dist/utils/api/register.js +96 -0
- package/dist/utils/api/register.js.map +1 -0
- package/dist/utils/common/errors.d.ts +13 -13
- package/dist/utils/common/errors.d.ts.map +1 -1
- package/dist/utils/common/errors.js +10 -2
- package/dist/utils/common/errors.js.map +1 -1
- package/dist/utils/common/utils.d.ts +1 -0
- package/dist/utils/common/utils.d.ts.map +1 -1
- package/dist/utils/common/utils.js +9 -0
- package/dist/utils/common/utils.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logging/logger.d.ts +2 -2
- package/dist/utils/logging/logger.d.ts.map +1 -1
- package/dist/utils/logging/logger.js +1 -1
- package/dist/utils/logging/logger.js.map +1 -1
- package/package.json +13 -11
package/README.md
CHANGED
|
@@ -1,19 +1,65 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://www.npmjs.com/package/@artinet/sdk)
|
|
2
|
+
[](https://www.npmjs.com/package/@artinet/sdk)
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://snyk.io/test/npm/@artinet/sdk)
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
<!-- [](https://coveralls.io/github/the-artinet-project/artinet-sdk?branch=main) -->
|
|
7
|
+
|
|
8
|
+
# Artinet SDK
|
|
9
|
+
|
|
10
|
+
Artinet SDK is a [Agent2Agent (A2A) Protocol](https://github.com/google/A2A) compliant server and client written in TypeScript for [node.js](https://nodejs.org/) that aims to simplify the creation of interoperable AI agents. Learn more at [the artinet project](https://artinet.io/).
|
|
4
11
|
|
|
5
12
|
This SDK significantly enhances the foundational A2A concepts and samples provided by Google, offering a production-ready solution with a focus on developer experience, reliability, and comprehensive features.
|
|
6
13
|
|
|
7
|
-
##
|
|
14
|
+
## Table of Contents
|
|
15
|
+
- [Artinet SDK](#artinet-sdk)
|
|
16
|
+
- [Table of Contents](#table-of-contents)
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Requirements](#requirements)
|
|
20
|
+
- [Example](#example)
|
|
21
|
+
- [Class Documentation](#class-documentation)
|
|
22
|
+
- [Core Classes](#core-classes)
|
|
23
|
+
- [Key Types \& Interfaces](#key-types--interfaces)
|
|
24
|
+
- [Running Tests](#running-tests)
|
|
25
|
+
- [Typescript](#typescript)
|
|
26
|
+
- [Usage](#usage)
|
|
27
|
+
- [Client](#client)
|
|
28
|
+
- [Basic Client Usage](#basic-client-usage)
|
|
29
|
+
- [Streaming Updates](#streaming-updates)
|
|
30
|
+
- [Authentication](#authentication)
|
|
31
|
+
- [Server](#server)
|
|
32
|
+
- [Implementing an A2A Agent](#implementing-an-a2a-agent)
|
|
33
|
+
- [Persistent Storage](#persistent-storage)
|
|
34
|
+
- [Logging](#logging)
|
|
35
|
+
- [Server Registration \& Discovery](#server-registration--discovery)
|
|
36
|
+
- [Advanced Server Customization](#advanced-server-customization)
|
|
37
|
+
- [Contributing](#contributing)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
- [Acknowledgements](#acknowledgements)
|
|
8
40
|
|
|
9
|
-
|
|
41
|
+
## Features
|
|
10
42
|
|
|
11
|
-
- **Plug-and-Play Server:** Built on Express.js, the `A2AServer` handles JSON-RPC complexity, routing, protocol compliance, and streaming mechanics automatically. Just provide your core agent logic (`TaskHandler`) and
|
|
12
|
-
- **Enhanced Client:**
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
43
|
+
- **Plug-and-Play Server:** Built on Express.js, the `A2AServer` handles JSON-RPC complexity, routing, protocol compliance, and Server-Sent Events (SSE) streaming mechanics automatically. Just provide your core agent logic (`TaskHandler`) and configuration via `A2AServerParams`.
|
|
44
|
+
- **Enhanced Client:** `A2AClient` features refined error handling (`RpcError`), flexible header management for authentication, and clear separation of concerns.
|
|
45
|
+
- **TypeScript First:** Fully written in TypeScript with comprehensive type definitions for a robust developer experience.
|
|
46
|
+
- **Flexible Storage:** Offers built-in `InMemoryTaskStore` (development/testing) and `FileStore` (persistent), with the `TaskStore` interface allowing custom storage solutions.
|
|
47
|
+
- **Protocol Compliance:** Implements the complete A2A specification using the official JSON schema, ensuring interoperability.
|
|
48
|
+
- **Robust Streaming:** Reliable SSE support for `tasks/sendSubscribe` & `tasks/resubscribe` using `eventsource-parser`.
|
|
49
|
+
- **Configurable Logging:** Integrated structured logging via `pino`. Configurable levels using `configureLogger` and `LogLevel`.
|
|
50
|
+
- **Advanced Customization:** Allows providing a custom `JSONRPCServerFactory` for fine-grained control over the JSON-RPC server, enabling integration with existing Express apps or adding custom methods.
|
|
51
|
+
- **Comprehensive Testing:** Includes a suite of tests to ensure reliability and maintainability.
|
|
52
|
+
|
|
53
|
+
| Component/Feature | Description | Key Classes/Types |
|
|
54
|
+
| :------------------ | :-------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------- |
|
|
55
|
+
| **Client** | Interact with A2A-compliant agents. Supports standard & streaming requests. | `A2AClient`, `RpcError` |
|
|
56
|
+
| **Server** | Host A2A-compliant agents. Handles protocol details, routing, SSE. | `A2AServer`, `A2AServerParams` |
|
|
57
|
+
| **Task Handling** | Define agent logic using async generators. | `TaskHandler`, `TaskContext`, `TaskYieldUpdate` |
|
|
58
|
+
| **Storage** | Persist task state. In-memory and file-based options included. | `TaskStore`, `InMemoryTaskStore`, `FileStore` |
|
|
59
|
+
| **Streaming (SSE)** | Handle real-time updates via SSE for `tasks/sendSubscribe`/`resubscribe`. | `TaskStatusUpdateEvent`, `TaskArtifactUpdateEvent` |
|
|
60
|
+
| **Logging** | Configure structured logging for debugging and monitoring. | `logger`, `configureLogger`, `LogLevel` |
|
|
61
|
+
| **Advanced Server** | Customize the underlying JSON-RPC server or integrate into existing apps. | `JSONRPCServerFactory`, `CreateJSONRPCServerParams`, `createJSONRPCMethod`, A2A Method Types |
|
|
62
|
+
| **Core Types** | Based on the official A2A JSON Schema. | `AgentCard`, `Task`, `Message`, `Part`, `Artifact`, etc. |
|
|
17
63
|
|
|
18
64
|
## Installation
|
|
19
65
|
|
|
@@ -21,31 +67,13 @@ While the official A2A repository provides basic samples, the Artinet SDK is eng
|
|
|
21
67
|
npm install @artinet/sdk
|
|
22
68
|
```
|
|
23
69
|
|
|
24
|
-
##
|
|
70
|
+
## Requirements
|
|
25
71
|
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
- Built-in, informative error classes (`RpcError`).
|
|
32
|
-
- Easy-to-use methods for `agentCard` discovery and capability checking (`supports`).
|
|
33
|
-
- Flexible header management for authentication (`addHeader`, `setHeaders`).
|
|
34
|
-
- **Server Features:**
|
|
35
|
-
- Simplified Express.js server setup (`A2AServer`).
|
|
36
|
-
- Abstracted `TaskHandler` interface for easy integration of agent logic using async generators.
|
|
37
|
-
* Automatic handling of JSON-RPC request parsing, validation, and routing.
|
|
38
|
-
- Pluggable task storage (`InMemoryTaskStore`, `FileStore`, or custom).
|
|
39
|
-
- Full support for streaming responses and artifact updates.
|
|
40
|
-
- Standardized error handling (`A2AError`) mapped to JSON-RPC error codes.
|
|
41
|
-
- Configurable agent card and capabilities.
|
|
42
|
-
- **Developer Tools:**
|
|
43
|
-
- Integrated structured logging via `pino`, configurable levels (`configureLogger`).
|
|
44
|
-
- Exhaustive test suite (`jest`) ensuring high reliability.
|
|
45
|
-
|
|
46
|
-
## Quick Start
|
|
47
|
-
|
|
48
|
-
Get a basic A2A server running and interact with it using the client in just a few lines of code.
|
|
72
|
+
- Node.js (v22.0.0 or higher recommended, check `package.json` engines for exact requirement)
|
|
73
|
+
|
|
74
|
+
## Example
|
|
75
|
+
|
|
76
|
+
A basic A2A server and client interaction. For more detailed examples, see the `examples/` directory.
|
|
49
77
|
|
|
50
78
|
**1. Server (`quick-server.ts`)**
|
|
51
79
|
|
|
@@ -55,25 +83,16 @@ import {
|
|
|
55
83
|
TaskContext,
|
|
56
84
|
TaskHandler,
|
|
57
85
|
InMemoryTaskStore,
|
|
58
|
-
logger,
|
|
59
|
-
configureLogger,
|
|
60
86
|
} from "@artinet/sdk";
|
|
61
87
|
|
|
62
|
-
//
|
|
63
|
-
configureLogger({ level: "info" });
|
|
64
|
-
|
|
65
|
-
// Define the simplest possible agent logic
|
|
88
|
+
// Minimal agent logic: receive text, yield working state, yield completed state with echo
|
|
66
89
|
const quickAgentLogic: TaskHandler = async function* (context: TaskContext) {
|
|
67
90
|
const userInput =
|
|
68
91
|
context.userMessage.parts[0].type === "text"
|
|
69
92
|
? context.userMessage.parts[0].text
|
|
70
93
|
: "";
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
state: "working",
|
|
74
|
-
message: { role: "agent", parts: [{ type: "text", text: "Thinking..." }] },
|
|
75
|
-
};
|
|
76
|
-
await new Promise((resolve) => setTimeout(resolve, 500)); // Simulate work
|
|
94
|
+
yield { state: "working" };
|
|
95
|
+
// Simulate some work if needed, check context.isCancelled()
|
|
77
96
|
yield {
|
|
78
97
|
state: "completed",
|
|
79
98
|
message: {
|
|
@@ -81,10 +100,8 @@ const quickAgentLogic: TaskHandler = async function* (context: TaskContext) {
|
|
|
81
100
|
parts: [{ type: "text", text: `You said: ${userInput}` }],
|
|
82
101
|
},
|
|
83
102
|
};
|
|
84
|
-
logger.info(`Quick server responded.`);
|
|
85
103
|
};
|
|
86
104
|
|
|
87
|
-
// Configure and start the server
|
|
88
105
|
const server = new A2AServer({
|
|
89
106
|
taskHandler: quickAgentLogic,
|
|
90
107
|
taskStore: new InMemoryTaskStore(),
|
|
@@ -94,525 +111,376 @@ const server = new A2AServer({
|
|
|
94
111
|
name: "QuickStart Agent",
|
|
95
112
|
url: "http://localhost:4000/a2a",
|
|
96
113
|
version: "0.1.0",
|
|
97
|
-
capabilities: { streaming: true },
|
|
114
|
+
capabilities: { streaming: true },
|
|
98
115
|
skills: [{ id: "echo", name: "Echo Skill" }],
|
|
99
116
|
},
|
|
100
117
|
});
|
|
101
118
|
|
|
102
119
|
server.start();
|
|
103
|
-
|
|
120
|
+
console.log("A2A Server running at http://localhost:4000/a2a");
|
|
104
121
|
```
|
|
105
122
|
|
|
106
123
|
**2. Client (`quick-client.ts`)**
|
|
107
124
|
|
|
108
125
|
```typescript
|
|
109
|
-
import { A2AClient,
|
|
110
|
-
|
|
111
|
-
// Set the log level to info to see client messages
|
|
112
|
-
configureLogger({ level: "info" });
|
|
126
|
+
import { A2AClient, TaskStatusUpdateEvent } from "@artinet/sdk";
|
|
113
127
|
|
|
114
128
|
async function runClient() {
|
|
115
129
|
const client = new A2AClient("http://localhost:4000/a2a");
|
|
116
|
-
|
|
130
|
+
|
|
131
|
+
const message = {
|
|
117
132
|
role: "user" as const,
|
|
118
133
|
parts: [{ type: "text" as const, text: "Hello Quick Start!" }],
|
|
119
134
|
};
|
|
120
135
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if ("status" in update && update.status.message) {
|
|
127
|
-
const agentText = update.status.message.parts
|
|
128
|
-
.filter((p) => p.type === "text")
|
|
129
|
-
.map((p: any) => p.text)
|
|
130
|
-
.join(" ");
|
|
131
|
-
logger.info(`Client Received: [${update.status.state}] ${agentText}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
logger.info("Client finished.");
|
|
135
|
-
} catch (error) {
|
|
136
|
-
logger.error("Client Error:", error);
|
|
136
|
+
const stream = client.sendTaskSubscribe({ id: "quick-task-1", message });
|
|
137
|
+
|
|
138
|
+
for await (const update of stream) {
|
|
139
|
+
// process the update
|
|
140
|
+
...
|
|
137
141
|
}
|
|
142
|
+
console.log("Stream finished.");
|
|
138
143
|
}
|
|
139
144
|
|
|
140
|
-
runClient();
|
|
145
|
+
runClient().catch(console.error);
|
|
141
146
|
```
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
## Class Documentation
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
- Install the SDK: `npm install @artinet/sdk`
|
|
147
|
-
- Run the server: `npx tsx quick-server.ts`
|
|
148
|
-
- In another terminal, run the client: `npx tsx quick-client.ts`
|
|
150
|
+
The Artinet SDK provides several core classes and interfaces for building A2A clients and servers.
|
|
149
151
|
|
|
150
|
-
|
|
152
|
+
### Core Classes
|
|
151
153
|
|
|
152
|
-
|
|
154
|
+
| Class | Description |
|
|
155
|
+
| :------------------ | :------------------------------------------------------------------ |
|
|
156
|
+
| `A2AClient` | Client for interacting with A2A servers. |
|
|
157
|
+
| `A2AServer` | Express-based server implementation for hosting A2A agents. |
|
|
158
|
+
| `RpcError` | Represents client-side errors encountered during A2A communication. |
|
|
159
|
+
| `InMemoryTaskStore` | Simple in-memory task persistence (ideal for development/testing). |
|
|
160
|
+
| `FileStore` | File-based task persistence (stores task data in the filesystem). |
|
|
153
161
|
|
|
154
|
-
###
|
|
162
|
+
### Key Types & Interfaces
|
|
155
163
|
|
|
156
|
-
|
|
157
|
-
|
|
164
|
+
| Type/Interface | Description |
|
|
165
|
+
| :-------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `TaskHandler` | Async generator function defining the core agent logic (`async function*(context: TaskContext): AsyncGenerator<TaskYieldUpdate>`). |
|
|
167
|
+
| `TaskContext` | Provides task details (`task`, `userMessage`, `history`, `isCancelled()`) to the `TaskHandler`. |
|
|
168
|
+
| `TaskStore` | Interface defining the contract for task persistence implementations (like `InMemoryTaskStore`, `FileStore`). |
|
|
169
|
+
| `TaskYieldUpdate` | Union type for updates yielded by a `TaskHandler` (representing status changes or generated artifacts). |
|
|
170
|
+
| `A2AServerParams` | Configuration object passed to the `A2AServer` constructor (port, store, card, basePath, handler, etc.). |
|
|
171
|
+
| `AgentCard` | Describes the agent's capabilities, metadata, skills, and endpoint URL. |
|
|
172
|
+
| `Message`, `Part`, `Artifact`, `Task`, `TaskStatus`, etc. | Core types mirroring the structures defined in the A2A JSON Schema specification. Used for requests, responses, and task state. |
|
|
173
|
+
| `TaskStatusUpdateEvent`, `TaskArtifactUpdateEvent` | Specific types for Server-Sent Events (SSE) received during streaming operations (`tasks/sendSubscribe`, `tasks/resubscribe`). |
|
|
174
|
+
| `LogLevel` | Enum defining logging levels (`error`, `warn`, `info`, `debug`, `trace`) used with the built-in logger. |
|
|
175
|
+
| `JSONRPCServerFactory` | Function signature for providing a custom JSON-RPC server creation logic to `A2AServer` for advanced customization. |
|
|
176
|
+
| `CreateJSONRPCServerParams` | Object containing dependencies provided _to_ a `JSONRPCServerFactory` function. |
|
|
177
|
+
| `SendTaskMethod`, `GetTaskMethod`, ... | Type signatures for specific A2A method handlers, used when implementing custom server logic with `createJSONRPCMethod`. |
|
|
178
|
+
|
|
179
|
+
## Running Tests
|
|
158
180
|
|
|
159
|
-
|
|
160
|
-
|
|
181
|
+
```bash
|
|
182
|
+
npm test
|
|
183
|
+
```
|
|
161
184
|
|
|
162
|
-
|
|
163
|
-
// Note: The client needs the *direct* A2A endpoint URL. Card discovery might be a separate step.
|
|
164
|
-
// const agentCard = await client.agentCard(); // Assuming agentCard is fetched by other means or URL is known
|
|
165
|
-
// console.log(`Agent capabilities:`, agentCard.capabilities);
|
|
185
|
+
To run tests with coverage:
|
|
166
186
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
187
|
+
```bash
|
|
188
|
+
npm run test:coverage
|
|
189
|
+
```
|
|
170
190
|
|
|
171
|
-
|
|
172
|
-
const message: Message = {
|
|
173
|
-
role: "user",
|
|
174
|
-
parts: [{ type: "text", text: "What is the weather in London?" }],
|
|
175
|
-
};
|
|
191
|
+
## Typescript
|
|
176
192
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
The Artinet SDK is written entirely in TypeScript and includes comprehensive type definitions, providing strong typing and enhanced developer experience.
|
|
194
|
+
|
|
195
|
+
## Usage
|
|
196
|
+
|
|
197
|
+
### Client
|
|
198
|
+
|
|
199
|
+
Interact with A2A-compliant agents using the `A2AClient`. See `examples/` for more.
|
|
200
|
+
|
|
201
|
+
#### Basic Client Usage
|
|
202
|
+
|
|
203
|
+
Send a task using `tasks/send`.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { A2AClient, Message } from "@artinet/sdk";
|
|
207
|
+
|
|
208
|
+
async function runBasicTask() {
|
|
209
|
+
const client = new A2AClient("https://your-a2a-server.com/a2a");
|
|
210
|
+
const message: Message = {
|
|
211
|
+
role: "user",
|
|
212
|
+
parts: [{ type: "text", text: "What is the capital of France?" }],
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const task = await client.sendTask({ id: "basic-task-1", message });
|
|
216
|
+
console.log("Task Completed:", task);
|
|
193
217
|
}
|
|
194
218
|
```
|
|
195
219
|
|
|
196
|
-
|
|
220
|
+
#### Streaming Updates
|
|
197
221
|
|
|
198
|
-
|
|
222
|
+
Receive real-time updates via SSE using `tasks/sendSubscribe` (recommended).
|
|
199
223
|
|
|
200
224
|
```typescript
|
|
201
225
|
import {
|
|
202
226
|
A2AClient,
|
|
203
227
|
Message,
|
|
204
|
-
RpcError,
|
|
205
228
|
TaskStatusUpdateEvent,
|
|
206
229
|
TaskArtifactUpdateEvent,
|
|
207
230
|
} from "@artinet/sdk";
|
|
208
231
|
|
|
209
|
-
const client = new A2AClient("https://your-a2a-server.com/a2a");
|
|
210
|
-
|
|
211
232
|
async function runStreamingTask() {
|
|
212
|
-
|
|
213
|
-
console.log("Agent does not support streaming.");
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
233
|
+
const client = new A2AClient("https://your-a2a-server.com/a2a");
|
|
217
234
|
const message: Message = {
|
|
218
235
|
role: "user",
|
|
219
|
-
parts: [
|
|
220
|
-
{ type: "text", text: "Generate a short story and a cover image." },
|
|
221
|
-
],
|
|
236
|
+
parts: [{ type: "text", text: "Tell me a short story." }],
|
|
222
237
|
};
|
|
223
238
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// Type guard for status updates
|
|
235
|
-
const statusUpdate = update as TaskStatusUpdateEvent;
|
|
236
|
-
console.log(`Task Status: ${statusUpdate.status.state}`);
|
|
237
|
-
if (statusUpdate.status.message) {
|
|
238
|
-
const text = statusUpdate.status.message.parts
|
|
239
|
-
.filter((p) => p.type === "text")
|
|
240
|
-
.map((p: any) => p.text)
|
|
241
|
-
.join(" ");
|
|
242
|
-
if (text) console.log("Agent Message:", text);
|
|
243
|
-
}
|
|
244
|
-
if (statusUpdate.final) {
|
|
245
|
-
console.log("Task stream finished.");
|
|
246
|
-
}
|
|
247
|
-
} else if ((update as TaskArtifactUpdateEvent).artifact) {
|
|
248
|
-
// Type guard for artifact updates
|
|
249
|
-
const artifactUpdate = update as TaskArtifactUpdateEvent;
|
|
250
|
-
console.log(
|
|
251
|
-
`Received Artifact: ${artifactUpdate.artifact.name ?? "Unnamed"}`
|
|
252
|
-
);
|
|
253
|
-
// Process artifact parts (e.g., save file, display data)
|
|
254
|
-
artifactUpdate.artifact.parts.forEach((part) => {
|
|
255
|
-
if (part.type === "text")
|
|
256
|
-
console.log(` -> Text Part: ${part.text.substring(0, 50)}...`);
|
|
257
|
-
if (part.type === "file")
|
|
258
|
-
console.log(
|
|
259
|
-
` -> File Part: ${part.file.name} (${part.file.mimeType})`
|
|
260
|
-
);
|
|
261
|
-
// Add logic to handle file bytes or URI
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
} catch (error) {
|
|
266
|
-
console.error("A2A Streaming Error:", error);
|
|
267
|
-
if (error instanceof RpcError) {
|
|
268
|
-
console.error(`RPC Error Code: ${error.code}, Message: ${error.message}`);
|
|
239
|
+
const stream = client.sendTaskSubscribe({ id: "streaming-task-1", message });
|
|
240
|
+
|
|
241
|
+
for await (const update of stream) {
|
|
242
|
+
if ((update as TaskStatusUpdateEvent).status) {
|
|
243
|
+
console.log("Status:", (update as TaskStatusUpdateEvent).status.state);
|
|
244
|
+
} else if ((update as TaskArtifactUpdateEvent).artifact) {
|
|
245
|
+
console.log(
|
|
246
|
+
"Artifact:",
|
|
247
|
+
(update as TaskArtifactUpdateEvent).artifact.name
|
|
248
|
+
);
|
|
269
249
|
}
|
|
270
250
|
}
|
|
251
|
+
console.log("Stream finished.");
|
|
271
252
|
}
|
|
272
|
-
|
|
273
|
-
runStreamingTask();
|
|
274
253
|
```
|
|
275
254
|
|
|
276
|
-
|
|
255
|
+
#### Authentication
|
|
277
256
|
|
|
278
|
-
|
|
257
|
+
Add headers using `addHeader` or `setHeaders`.
|
|
279
258
|
|
|
280
259
|
```typescript
|
|
281
260
|
import { A2AClient } from "@artinet/sdk";
|
|
282
261
|
|
|
283
262
|
const client = new A2AClient("https://your-secure-a2a-server.com/a2a");
|
|
284
263
|
|
|
285
|
-
// Add a single header
|
|
264
|
+
// Add a single header
|
|
286
265
|
client.addHeader("Authorization", "Bearer your-api-token");
|
|
287
266
|
|
|
288
|
-
//
|
|
289
|
-
client.setHeaders({
|
|
290
|
-
Authorization: "Bearer your-api-token",
|
|
291
|
-
"X-Custom-Header": "value",
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
// Now make requests as usual
|
|
295
|
-
// await client.sendTask(...)
|
|
267
|
+
// Set multiple headers (overwrites existing)
|
|
268
|
+
client.setHeaders({ Authorization: "Bearer ...", "X-Custom": "value" });
|
|
296
269
|
```
|
|
297
270
|
|
|
298
|
-
|
|
271
|
+
### Server
|
|
299
272
|
|
|
300
|
-
|
|
273
|
+
Host agents using `A2AServer`. Handles protocol details. See `examples/` for more.
|
|
301
274
|
|
|
302
|
-
|
|
275
|
+
#### Implementing an A2A Agent
|
|
303
276
|
|
|
304
|
-
Define
|
|
277
|
+
Define agent behavior with an async generator `TaskHandler`.
|
|
305
278
|
|
|
306
279
|
```typescript
|
|
307
280
|
import {
|
|
308
281
|
A2AServer,
|
|
309
282
|
TaskContext,
|
|
310
|
-
TaskHandler
|
|
283
|
+
TaskHandler,
|
|
311
284
|
InMemoryTaskStore,
|
|
312
|
-
logger, // Use the built-in logger
|
|
313
285
|
} from "@artinet/sdk";
|
|
314
|
-
import path from "path";
|
|
315
286
|
|
|
316
|
-
// Define your agent's logic
|
|
317
|
-
async function* myAgentLogic(
|
|
318
|
-
context: TaskContext
|
|
319
|
-
): TaskHandler {
|
|
320
|
-
logger.info({ taskId: context.taskId }, "Received new task");
|
|
321
287
|
|
|
322
|
-
|
|
288
|
+
const myAgent: TaskHandler = async function* (context: TaskContext) {
|
|
289
|
+
|
|
323
290
|
yield {
|
|
324
291
|
state: "working",
|
|
325
292
|
message: {
|
|
326
293
|
role: "agent",
|
|
327
|
-
parts: [{ type: "text", text: "
|
|
294
|
+
parts: [{ type: "text", text: "Processing..." }],
|
|
328
295
|
},
|
|
329
296
|
};
|
|
330
297
|
|
|
331
|
-
//
|
|
332
|
-
await
|
|
298
|
+
// Check context.isCancelled() if operation is long
|
|
299
|
+
// await someAsyncTask();
|
|
300
|
+
...
|
|
333
301
|
|
|
334
|
-
// 2. Check for cancellation periodically during long operations
|
|
335
|
-
if (context.isCancelled()) {
|
|
336
|
-
logger.warn({ taskId: context.taskId }, "Task cancelled by client");
|
|
337
|
-
yield { state: "canceled" };
|
|
338
|
-
return; // Stop processing
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// 3. Process the user's message
|
|
342
|
-
const userRequest = context.userMessage.parts
|
|
343
|
-
.filter((part) => part.type === "text")
|
|
344
|
-
.map((part: any) => part.text)
|
|
345
|
-
.join(" ");
|
|
346
|
-
logger.debug(
|
|
347
|
-
{ taskId: context.taskId, request: userRequest },
|
|
348
|
-
"Processing user text"
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// 4. (Optional) Yield an artifact
|
|
352
|
-
const artifactContent = `This is a generated report for: "${userRequest}"`;
|
|
353
302
|
yield {
|
|
354
|
-
|
|
355
|
-
name: "report.txt",
|
|
303
|
+
name: "result.txt",
|
|
356
304
|
mimeType: "text/plain",
|
|
357
|
-
parts: [{ type: "text", text:
|
|
305
|
+
parts: [{ type: "text", text: "Report data" }],
|
|
358
306
|
};
|
|
359
|
-
logger.info({ taskId: context.taskId }, "Generated artifact report.txt");
|
|
360
|
-
|
|
361
|
-
// Simulate more work
|
|
362
|
-
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
363
|
-
if (context.isCancelled()) {
|
|
364
|
-
/* ... check again ... */
|
|
365
|
-
}
|
|
366
307
|
|
|
367
|
-
// 5. Yield the final response
|
|
368
|
-
const finalResponse = `Finished processing: "${userRequest}". See attached report.`;
|
|
369
308
|
yield {
|
|
370
309
|
state: "completed",
|
|
371
|
-
message: {
|
|
310
|
+
message: {
|
|
311
|
+
role: "agent",
|
|
312
|
+
parts: [{ type: "text", text: "Finished processing." }],
|
|
313
|
+
},
|
|
372
314
|
};
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Create a task store (in-memory for this example)
|
|
377
|
-
const store = new InMemoryTaskStore();
|
|
315
|
+
};
|
|
378
316
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
taskStore: store,
|
|
317
|
+
const myServer = new A2AServer({
|
|
318
|
+
taskHandler: myAgent,
|
|
319
|
+
taskStore: new InMemoryTaskStore(),
|
|
383
320
|
port: 3000,
|
|
384
|
-
basePath: "/a2a",
|
|
385
|
-
// Customize the agent card served at /.well-known/agent.json
|
|
321
|
+
basePath: "/a2a",
|
|
386
322
|
card: {
|
|
387
|
-
name: "
|
|
388
|
-
|
|
389
|
-
url: "http://localhost:3000/a2a", // Must match your server's accessible A2A endpoint
|
|
323
|
+
name: "Example Agent",
|
|
324
|
+
url: "http://localhost:3000/a2a",
|
|
390
325
|
version: "1.0.0",
|
|
391
|
-
capabilities: {
|
|
392
|
-
|
|
393
|
-
pushNotifications: false, // This agent doesn't implement push logic
|
|
394
|
-
},
|
|
395
|
-
skills: [
|
|
396
|
-
// Define agent skills
|
|
397
|
-
{
|
|
398
|
-
id: "text-processing",
|
|
399
|
-
name: "Text Processor",
|
|
400
|
-
description: "Processes text requests and generates reports.",
|
|
401
|
-
},
|
|
402
|
-
],
|
|
403
|
-
// Add provider, auth details etc. as needed
|
|
326
|
+
capabilities: { streaming: true },
|
|
327
|
+
skills: [{ id: "processor", name: "Text Processor" }],
|
|
404
328
|
},
|
|
405
329
|
});
|
|
406
330
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
logger.info(
|
|
410
|
-
`A2A Server started on http://localhost:3000${server.options.basePath}`
|
|
411
|
-
);
|
|
412
|
-
logger.info(
|
|
413
|
-
`Agent Card available at http://localhost:3000/.well-known/agent.json`
|
|
414
|
-
);
|
|
331
|
+
myServer.start();
|
|
332
|
+
console.log("A2A Server running on http://localhost:3000/a2a");
|
|
415
333
|
```
|
|
416
334
|
|
|
417
|
-
|
|
335
|
+
#### Persistent Storage
|
|
418
336
|
|
|
419
|
-
|
|
337
|
+
Use `FileStore` for file-based persistence. Ensure the directory exists.
|
|
420
338
|
|
|
421
339
|
```typescript
|
|
422
|
-
import { A2AServer, FileStore, logger } from "@artinet/sdk";
|
|
423
340
|
import path from "path";
|
|
424
341
|
import fs from "fs";
|
|
425
342
|
|
|
426
|
-
// Assume myAgentLogic is defined as above
|
|
427
|
-
|
|
428
343
|
const dataDir = path.join(process.cwd(), "a2a-data");
|
|
429
|
-
// Ensure the directory exists
|
|
430
344
|
if (!fs.existsSync(dataDir)) {
|
|
431
345
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
432
|
-
logger.info(`Created data directory: ${dataDir}`);
|
|
433
346
|
}
|
|
434
347
|
|
|
435
|
-
|
|
436
|
-
const store = new FileStore(dataDir);
|
|
437
|
-
logger.info(`Using FileStore at ${dataDir}`);
|
|
348
|
+
const myStore = new FileStore(dataDir);
|
|
438
349
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
port: 3001,
|
|
443
|
-
basePath: "/a2a-persistent",
|
|
444
|
-
card: {
|
|
445
|
-
/* ... configure card ... */ name: "Persistent Agent",
|
|
446
|
-
url: "http://localhost:3001/a2a-persistent",
|
|
447
|
-
version: "1.0.1",
|
|
448
|
-
capabilities: { streaming: true },
|
|
449
|
-
skills: [{ id: "persistent-skill", name: "Persistent Task Handler" }],
|
|
450
|
-
},
|
|
350
|
+
const myServer = new A2AServer({
|
|
351
|
+
taskStore: myStore,
|
|
352
|
+
...
|
|
451
353
|
});
|
|
452
|
-
server.start();
|
|
453
|
-
logger.info(
|
|
454
|
-
`Persistent A2A Server started on http://localhost:3001${server.options.basePath}`
|
|
455
|
-
);
|
|
456
354
|
```
|
|
457
355
|
|
|
458
|
-
|
|
356
|
+
#### Logging
|
|
459
357
|
|
|
460
|
-
|
|
358
|
+
Use the built-in `pino`-based logger. Configure with `configureLogger`.
|
|
461
359
|
|
|
462
360
|
```typescript
|
|
463
|
-
import { logger, configureLogger, LogLevel } from "@artinet/sdk";
|
|
361
|
+
import { logger, configureLogger, LogLevel, logDebug } from "@artinet/sdk";
|
|
464
362
|
|
|
465
|
-
// Configure logging (optional
|
|
466
|
-
configureLogger({
|
|
467
|
-
level: "debug", // 'silent', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'
|
|
468
|
-
name: "MyA2AApp", // Optional logger name
|
|
469
|
-
});
|
|
363
|
+
// Configure logging level (optional)
|
|
364
|
+
configureLogger({ level: "debug" });
|
|
470
365
|
|
|
471
|
-
// Use the logger throughout your application
|
|
472
366
|
logger.info("Server starting...");
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
logger.
|
|
367
|
+
//use helper functions
|
|
368
|
+
logDebug("LoggerTest", { taskId: "task-123" }, "Task status updated.");
|
|
369
|
+
|
|
370
|
+
// Create child logger with bound context
|
|
371
|
+
const taskLogger = logger.child({ taskId: "abc" });
|
|
372
|
+
taskLogger.info("Processing step X");
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
#### Server Registration & Discovery
|
|
478
376
|
|
|
479
|
-
|
|
480
|
-
const clientLogger = logger.child({ component: "A2AClient" });
|
|
481
|
-
clientLogger.info("Sending request to agent...");
|
|
377
|
+
The SDK includes features to help make your agent discoverable:
|
|
482
378
|
|
|
483
|
-
|
|
484
|
-
// configureLogger(); // Reads from process.env.LOG_LEVEL if set
|
|
379
|
+
- **Automatic Registration:** You can configure your `A2AServer` to automatically register your `AgentCard` with the [A2A Registry](https://artinet.io) upon startup by setting `register: true` (default: `false`) in the server parameters.
|
|
485
380
|
|
|
486
|
-
|
|
487
|
-
|
|
381
|
+
```typescript
|
|
382
|
+
const myServer = new A2AServer({
|
|
383
|
+
...
|
|
384
|
+
card: {
|
|
385
|
+
...
|
|
386
|
+
url: "http://my-public-domain:3000/my-agent", // Publicly accessible URL
|
|
387
|
+
...
|
|
388
|
+
},
|
|
389
|
+
register: true, // Enable automatic registration on start
|
|
390
|
+
});
|
|
488
391
|
```
|
|
489
392
|
|
|
490
|
-
|
|
393
|
+
- **Custom Agent Card Path:** By default, the server exposes its `AgentCard` at `/.well-known/agent.json` [RFC8615](https://datatracker.ietf.org/doc/html/rfc8615) and a fallback at `/agent-card`. You can specify a different fallback path using the `fallbackPath` option in `A2AServerParams`.
|
|
491
394
|
|
|
492
|
-
|
|
395
|
+
```typescript
|
|
396
|
+
const myServer = new A2AServer({
|
|
397
|
+
...
|
|
398
|
+
basePath: "/apiV2"
|
|
399
|
+
fallbackPath: "/apiV2/custom-card-info", // Agent card available here
|
|
400
|
+
...
|
|
401
|
+
});
|
|
402
|
+
// The AgentCard is now accessible at http://localhost:3001/apiV2/custom-card-info
|
|
403
|
+
```
|
|
493
404
|
|
|
494
|
-
|
|
495
|
-
- Adding non-A2A custom JSON-RPC methods alongside the standard A2A ones.
|
|
496
|
-
- Using a different JSON-RPC library or implementation.
|
|
405
|
+
#### Advanced Server Customization
|
|
497
406
|
|
|
498
|
-
|
|
407
|
+
Provide a custom `createJSONRPCServer` function (implementing `JSONRPCServerFactory`) for fine-grained control over the underlying RPC server.
|
|
499
408
|
|
|
500
|
-
|
|
409
|
+
This factory function receives objects of type `CreateJSONRPCServerParams` & `RequestParams` containing the necessary SDK dependencies (`taskHandler`, `taskStore`, `agentCard`, `activeCancellations`, `createTaskContext`, `closeStreamsForTask`) and the specific method paramaters (i.e. `SendTaskRequest["params"]`). You can use these dependencies to configure the standard A2A methods and add your own custom JSON-RPC methods.
|
|
501
410
|
|
|
502
|
-
|
|
411
|
+
The SDK exports default handlers for the standard A2A methods (e.g., `defaultSendTaskMethod`), create your own using dedicated A2A method types(`SendTaskMethod`) and use `createJSONRPCMethod` to easily wrap these methods with dependency injection and error handling.
|
|
412
|
+
|
|
413
|
+
See `src/server/lib/middleware/factory.ts` and `src/server/lib/middleware/a2a-methods.ts` for implementation details.
|
|
414
|
+
|
|
415
|
+
**Example:**
|
|
503
416
|
|
|
504
417
|
```typescript
|
|
505
|
-
import {
|
|
506
|
-
CreateJSONRPCServerParams,
|
|
507
|
-
JSONRPCServerType,
|
|
508
|
-
JSONRPCServerFactory, // Import the factory type
|
|
509
|
-
} from "@artinet/sdk";
|
|
510
|
-
import jayson from "jayson"; // Assuming Jayson is used
|
|
511
418
|
|
|
512
|
-
|
|
513
|
-
|
|
419
|
+
const myCustomSendMethod: SendTaskMethod = (
|
|
420
|
+
deps,
|
|
421
|
+
requestParams,
|
|
422
|
+
callback
|
|
423
|
+
) => {
|
|
424
|
+
const { taskStore, taskHandler, createTaskContext } = deps;
|
|
425
|
+
const taskId = extractTaskId(requestParams.id);
|
|
426
|
+
const { message, sessionId, metadata } = requestParams;
|
|
427
|
+
...
|
|
428
|
+
callback(null, ...);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const myCustomRPCServer: JSONRPCServerFactory = (
|
|
514
432
|
params: CreateJSONRPCServerParams
|
|
515
433
|
): JSONRPCServerType => {
|
|
516
|
-
//
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
/* ... */
|
|
533
|
-
},
|
|
534
|
-
"tasks/cancel": (/* ... */) => {
|
|
535
|
-
/* ... */
|
|
536
|
-
},
|
|
537
|
-
// ... other A2A methods ...
|
|
538
|
-
|
|
539
|
-
// 3. Add any custom methods
|
|
540
|
-
myCustomMethod: (args: any, callback: jayson.JSONRPCCallback) => {
|
|
541
|
-
console.log("My custom method called with:", args);
|
|
542
|
-
callback(null, { result: "Custom success!" });
|
|
543
|
-
},
|
|
544
|
-
},
|
|
545
|
-
{
|
|
546
|
-
// Jayson server options
|
|
547
|
-
}
|
|
548
|
-
);
|
|
434
|
+
//Use a custom task/send method
|
|
435
|
+
const taskSendMethod = createJSONRPCMethod(params, myCustomSendMethod, "tasks/send");
|
|
436
|
+
const taskGetMethod = createJSONRPCMethod(params, defaultGetTaskMethod, "tasks/get");
|
|
437
|
+
const taskCancelMethod = createJSONRPCMethod(params, defaultCancelTaskMethod, "tasks/cancel");
|
|
438
|
+
|
|
439
|
+
// Note: Push notifications are not fully implemented yet
|
|
440
|
+
const taskPushNotificationSetMethod = createJSONRPCMethod(params, defaultSetTaskPushNotificationMethod, "tasks/pushNotification/set");
|
|
441
|
+
const taskPushNotificationGetMethod = createJSONRPCMethod(params, defaultGetTaskPushNotificationMethod, "tasks/pushNotification/get");
|
|
442
|
+
|
|
443
|
+
const rpcServer = new JSONRPCServer({
|
|
444
|
+
"tasks/send": taskSendMethod,
|
|
445
|
+
"tasks/get": taskGetMethod,
|
|
446
|
+
"tasks/cancel": taskCancelMethod,
|
|
447
|
+
"tasks/pushNotification/set": taskPushNotificationSetMethod,
|
|
448
|
+
"tasks/pushNotification/get": taskPushNotificationGetMethod,
|
|
449
|
+
});
|
|
549
450
|
|
|
550
|
-
return
|
|
451
|
+
return rpcServer;
|
|
551
452
|
};
|
|
552
|
-
```
|
|
553
|
-
|
|
554
|
-
Refer to the `src/server/lib/json-middleware.ts` file for the default implementation (`defaultCreateJSONRPCServer`) as a reference.
|
|
555
|
-
|
|
556
|
-
### Using the Custom Function
|
|
557
|
-
|
|
558
|
-
Pass your function during `A2AServer` initialization:
|
|
559
|
-
|
|
560
|
-
```typescript
|
|
561
|
-
import {
|
|
562
|
-
A2AServer,
|
|
563
|
-
InMemoryTaskStore /* ... other imports ... */,
|
|
564
|
-
} from "@artinet/sdk";
|
|
565
|
-
// Assume myAgentLogic and myCustomCreateServer are defined as above
|
|
566
453
|
|
|
567
|
-
const store = new InMemoryTaskStore();
|
|
568
454
|
|
|
569
455
|
const server = new A2AServer({
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
port: 3002,
|
|
573
|
-
basePath: "/a2a-custom",
|
|
574
|
-
createJSONRPCServer: myCustomCreateServer, // Pass your custom function here
|
|
575
|
-
card: {
|
|
576
|
-
/* ... configure card ... */ name: "Custom RPC Agent",
|
|
577
|
-
url: "http://localhost:3002/a2a-custom",
|
|
578
|
-
version: "1.1.0",
|
|
579
|
-
capabilities: { streaming: true }, // Ensure capabilities match your implementation
|
|
580
|
-
skills: [{ id: "custom-skill", name: "Custom RPC Handler" }],
|
|
581
|
-
},
|
|
456
|
+
createJSONRPCServer: myCustomRPCServer,
|
|
457
|
+
...
|
|
582
458
|
});
|
|
583
|
-
|
|
584
|
-
server.start(); // This will now use your custom server setup
|
|
585
459
|
```
|
|
586
460
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
## API Reference
|
|
461
|
+
**Using the Custom Factory**
|
|
590
462
|
|
|
591
|
-
|
|
463
|
+
Pass your factory function via the `createJSONRPCServer` option during `A2AServer` initialization.
|
|
592
464
|
|
|
593
|
-
|
|
594
|
-
- `A2AServer`: Express-based server implementation.
|
|
595
|
-
- `RpcError`: Client-side A2A protocol errors.
|
|
596
|
-
- `A2AError`: Server-side A2A protocol errors (used internally).
|
|
597
|
-
- `InMemoryTaskStore`: Simple in-memory task persistence.
|
|
598
|
-
- `FileStore`: File-based task persistence.
|
|
599
|
-
|
|
600
|
-
### Key Types & Interfaces
|
|
601
|
-
|
|
602
|
-
- `TaskHandler`: Async generator function defining agent logic (`async function*(context: TaskContext): AsyncGenerator<TaskYieldUpdate>`).
|
|
603
|
-
- `TaskContext`: Provides task details (`taskId`, `userMessage`, `isCancelled`, `metadata`, `store`) to the `TaskHandler`.
|
|
604
|
-
- `TaskYieldUpdate`: Union type for updates yielded by `TaskHandler` (status changes or artifacts).
|
|
605
|
-
- `A2AServerOptions`: Configuration for `A2AServer` (port, store, card, basePath, etc.).
|
|
606
|
-
- `AgentCard`, `Message`, `Part`, `Artifact`, `Task`, `TaskStatus`, etc.: Types mirroring the A2A JSON Schema.
|
|
465
|
+
**Important:** The default `A2AServer` setup automatically adds Express middleware to handle Server-Sent Events (SSE) for `tasks/sendSubscribe` and `tasks/resubscribe`, as well as the `/agent/card` (and `/.well-known/agent.json`) GET endpoints. If you are **not** using `A2AServer` and integrating the Jayson server middleware into your own Express application, you **must** implement these SSE and card endpoints yourself to maintain full A2A compliance, especially for streaming functionality. See `src/server/lib/express-server.ts` for how the default server handles these routes.
|
|
607
466
|
|
|
608
467
|
## Contributing
|
|
609
468
|
|
|
610
|
-
Contributions are welcome! Please open an issue or submit a Pull Request.
|
|
469
|
+
Contributions are welcome! Please open an issue or submit a Pull Request on [GitHub](https://github.com/the-artinet-project/artinet-sdk).
|
|
470
|
+
|
|
471
|
+
Ensure code adheres to the project style and passes linting (`npm run lint`) and tests (`npm test`).
|
|
611
472
|
|
|
612
473
|
## License
|
|
613
474
|
|
|
614
|
-
This project is licensed under the
|
|
475
|
+
This project is licensed under the Apache License 2.0 - see the `LICENSE` file for details.
|
|
615
476
|
|
|
616
477
|
## Acknowledgements
|
|
617
478
|
|
|
618
|
-
This SDK builds upon the [
|
|
479
|
+
This SDK builds upon the concepts and specifications of the [Agent2Agent (A2A) Protocol](https://github.com/google/A2A) initiated by Google. It utilizes the official [A2A JSON Schema](https://github.com/google/A2A/tree/main/specification/json) for protocol compliance.
|
|
480
|
+
|
|
481
|
+
Libraries used include:
|
|
482
|
+
|
|
483
|
+
- [Express.js](https://expressjs.com/) for the server framework.
|
|
484
|
+
- [Jayson](https://github.com/tedeh/jayson) for JSON-RPC handling.
|
|
485
|
+
- [Pino](https://getpino.io/) for logging.
|
|
486
|
+
- [EventSource Parser](https://github.com/rexxars/eventsource-parser) for SSE streaming.
|