@artinet/sdk 0.2.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.
Files changed (120) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/README.md +618 -0
  4. package/dist/client/a2a-client.d.ts +99 -0
  5. package/dist/client/a2a-client.d.ts.map +1 -0
  6. package/dist/client/a2a-client.js +171 -0
  7. package/dist/client/a2a-client.js.map +1 -0
  8. package/dist/client/index.d.ts +3 -0
  9. package/dist/client/index.d.ts.map +1 -0
  10. package/dist/client/index.js +3 -0
  11. package/dist/client/index.js.map +1 -0
  12. package/dist/client/interfaces/client.d.ts +19 -0
  13. package/dist/client/interfaces/client.d.ts.map +1 -0
  14. package/dist/client/interfaces/client.js +2 -0
  15. package/dist/client/interfaces/client.js.map +1 -0
  16. package/dist/index.d.ts +6 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +6 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/server/a2a-server.d.ts +144 -0
  21. package/dist/server/a2a-server.d.ts.map +1 -0
  22. package/dist/server/a2a-server.js +384 -0
  23. package/dist/server/a2a-server.js.map +1 -0
  24. package/dist/server/index.d.ts +9 -0
  25. package/dist/server/index.d.ts.map +1 -0
  26. package/dist/server/index.js +9 -0
  27. package/dist/server/index.js.map +1 -0
  28. package/dist/server/interfaces/context.d.ts +41 -0
  29. package/dist/server/interfaces/context.d.ts.map +1 -0
  30. package/dist/server/interfaces/context.js +2 -0
  31. package/dist/server/interfaces/context.js.map +1 -0
  32. package/dist/server/interfaces/params.d.ts +72 -0
  33. package/dist/server/interfaces/params.d.ts.map +1 -0
  34. package/dist/server/interfaces/params.js +3 -0
  35. package/dist/server/interfaces/params.js.map +1 -0
  36. package/dist/server/interfaces/server.d.ts +34 -0
  37. package/dist/server/interfaces/server.d.ts.map +1 -0
  38. package/dist/server/interfaces/server.js +2 -0
  39. package/dist/server/interfaces/server.js.map +1 -0
  40. package/dist/server/interfaces/store.d.ts +30 -0
  41. package/dist/server/interfaces/store.d.ts.map +1 -0
  42. package/dist/server/interfaces/store.js +2 -0
  43. package/dist/server/interfaces/store.js.map +1 -0
  44. package/dist/server/lib/express-server.d.ts +13 -0
  45. package/dist/server/lib/express-server.d.ts.map +1 -0
  46. package/dist/server/lib/express-server.js +51 -0
  47. package/dist/server/lib/express-server.js.map +1 -0
  48. package/dist/server/lib/json-middleware.d.ts +19 -0
  49. package/dist/server/lib/json-middleware.d.ts.map +1 -0
  50. package/dist/server/lib/json-middleware.js +180 -0
  51. package/dist/server/lib/json-middleware.js.map +1 -0
  52. package/dist/server/lib/state.d.ts +28 -0
  53. package/dist/server/lib/state.d.ts.map +1 -0
  54. package/dist/server/lib/state.js +108 -0
  55. package/dist/server/lib/state.js.map +1 -0
  56. package/dist/server/lib/storage/file.d.ts +62 -0
  57. package/dist/server/lib/storage/file.d.ts.map +1 -0
  58. package/dist/server/lib/storage/file.js +148 -0
  59. package/dist/server/lib/storage/file.js.map +1 -0
  60. package/dist/server/lib/storage/memory.d.ts +21 -0
  61. package/dist/server/lib/storage/memory.d.ts.map +1 -0
  62. package/dist/server/lib/storage/memory.js +40 -0
  63. package/dist/server/lib/storage/memory.js.map +1 -0
  64. package/dist/transport/index.d.ts +5 -0
  65. package/dist/transport/index.d.ts.map +1 -0
  66. package/dist/transport/index.js +5 -0
  67. package/dist/transport/index.js.map +1 -0
  68. package/dist/transport/rpc/parser.d.ts +12 -0
  69. package/dist/transport/rpc/parser.d.ts.map +1 -0
  70. package/dist/transport/rpc/parser.js +37 -0
  71. package/dist/transport/rpc/parser.js.map +1 -0
  72. package/dist/transport/rpc/rpc-client.d.ts +77 -0
  73. package/dist/transport/rpc/rpc-client.d.ts.map +1 -0
  74. package/dist/transport/rpc/rpc-client.js +182 -0
  75. package/dist/transport/rpc/rpc-client.js.map +1 -0
  76. package/dist/transport/streaming/event-stream.d.ts +22 -0
  77. package/dist/transport/streaming/event-stream.d.ts.map +1 -0
  78. package/dist/transport/streaming/event-stream.js +79 -0
  79. package/dist/transport/streaming/event-stream.js.map +1 -0
  80. package/dist/transport/streaming/stream.d.ts +42 -0
  81. package/dist/transport/streaming/stream.d.ts.map +1 -0
  82. package/dist/transport/streaming/stream.js +128 -0
  83. package/dist/transport/streaming/stream.js.map +1 -0
  84. package/dist/types/extended-schema.d.ts +52 -0
  85. package/dist/types/extended-schema.d.ts.map +1 -0
  86. package/dist/types/extended-schema.js +7 -0
  87. package/dist/types/extended-schema.js.map +1 -0
  88. package/dist/types/index.d.ts +2 -0
  89. package/dist/types/index.d.ts.map +1 -0
  90. package/dist/types/index.js +2 -0
  91. package/dist/types/index.js.map +1 -0
  92. package/dist/types/schema.d.ts +729 -0
  93. package/dist/types/schema.d.ts.map +1 -0
  94. package/dist/types/schema.js +23 -0
  95. package/dist/types/schema.js.map +1 -0
  96. package/dist/utils/common/constants.d.ts +6 -0
  97. package/dist/utils/common/constants.d.ts.map +1 -0
  98. package/dist/utils/common/constants.js +28 -0
  99. package/dist/utils/common/constants.js.map +1 -0
  100. package/dist/utils/common/errors.d.ts +26 -0
  101. package/dist/utils/common/errors.d.ts.map +1 -0
  102. package/dist/utils/common/errors.js +59 -0
  103. package/dist/utils/common/errors.js.map +1 -0
  104. package/dist/utils/common/utils.d.ts +30 -0
  105. package/dist/utils/common/utils.d.ts.map +1 -0
  106. package/dist/utils/common/utils.js +67 -0
  107. package/dist/utils/common/utils.js.map +1 -0
  108. package/dist/utils/index.d.ts +6 -0
  109. package/dist/utils/index.d.ts.map +1 -0
  110. package/dist/utils/index.js +6 -0
  111. package/dist/utils/index.js.map +1 -0
  112. package/dist/utils/logging/log.d.ts +30 -0
  113. package/dist/utils/logging/log.d.ts.map +1 -0
  114. package/dist/utils/logging/log.js +58 -0
  115. package/dist/utils/logging/log.js.map +1 -0
  116. package/dist/utils/logging/logger.d.ts +16 -0
  117. package/dist/utils/logging/logger.d.ts.map +1 -0
  118. package/dist/utils/logging/logger.js +38 -0
  119. package/dist/utils/logging/logger.js.map +1 -0
  120. package/package.json +83 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,45 @@
1
+ # Changelog
2
+
3
+ All notable changes to the @artinet/sdk package will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.0] - 2025-04-25
11
+
12
+ ### Fixed
13
+
14
+ - Fixed TypeScript errors related to the `eventsource-parser` package imports
15
+ - Updated imports to use the correct types from `eventsource-parser` v1.1.1
16
+ - Properly typed the `EventSourceMessage` as `ParsedEvent`
17
+ - Refactored `createParser` implementation to match the package's API
18
+ - Fixed streaming response handler to use the correct event type checking
19
+
20
+ ### Changed
21
+
22
+ - Updated `tsconfig.json` to add `isolatedModules: true` for better compatibility with `ts-jest`
23
+ - Modified `package.json` test scripts to include `NODE_OPTIONS=--experimental-vm-modules` flag to support ES modules in Jest tests
24
+
25
+ ### Improved
26
+
27
+ - Expanded test suite to achieve 95% code coverage
28
+ - Added tests for all client methods
29
+ - Added robust error handling tests
30
+ - Added tests for streaming functionality
31
+ - Added tests for push notification configuration
32
+ - Added tests for edge cases in agent card fetching and capability detection
33
+
34
+ ## [0.1.0] - 2025-04-22
35
+
36
+ ### Added
37
+
38
+ - Initial release of the @artinet/sdk package
39
+ - Implementation of the Agent2Agent (A2A) Protocol client
40
+ - Support for sending tasks, retrieving statuses, and canceling operations
41
+ - Support for streaming responses and push notifications
42
+ - Comprehensive test suite and documentation
43
+
44
+ [0.2.0]: https://github.com/artinet/sdk/compare/v0.1.0...v0.2.0
45
+ [0.1.0]: https://github.com/artinet/sdk/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Artinet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,618 @@
1
+ # Artinet SDK: A Production-Ready A2A Protocol Implementation
2
+
3
+ A robust, feature-rich TypeScript client and server library for the [Agent2Agent (A2A) Protocol](https://github.com/google/A2A) - an open protocol for communication between AI agents.
4
+
5
+ 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
+
7
+ ## Why Choose Artinet SDK?
8
+
9
+ While the official A2A repository provides basic samples, the Artinet SDK is engineered for real-world applications:
10
+
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 basic configuration for a fully functional A2A endpoint with minimal boilerplate.
12
+ - **Enhanced Client:** Features refined error handling, flexible header management for authentication, and clear separation of concerns.
13
+ - **Comprehensive Testing:** Ensuring reliability and maintainability. Includes tests for core logic, streaming, error conditions, and edge cases.
14
+ - **Simplified Developer Experience:** Start quickly with clear TypeScript types, intuitive APIs, and minimal setup. The SDK handles the underlying protocol details, letting you focus _solely_ on your agent's unique capabilities.
15
+ - **Flexible Storage:** Offers built-in `InMemoryTaskStore` for development/testing and `FileStore` for persistent task storage, easily extensible for custom storage solutions.
16
+ - **Full Protocol Compliance:** Implements the complete A2A specification using the official JSON schema, ensuring interoperability.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @artinet/sdk
22
+ ```
23
+
24
+ ## Features
25
+
26
+ - Full implementation of the A2A Protocol using the official Google schema
27
+ - Strongly-typed TypeScript interfaces for all protocol structures
28
+ - **Client Features:**
29
+ - Reliable streaming support (`tasks/sendSubscribe`) with robust handling of Server-Sent Events (SSE) using `eventsource-parser`.
30
+ - Push notification configuration support (`tasks/pushNotification/set`, `tasks/pushNotification/get`).
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.
49
+
50
+ **1. Server (`quick-server.ts`)**
51
+
52
+ ```typescript
53
+ import {
54
+ A2AServer,
55
+ TaskContext,
56
+ TaskHandler,
57
+ InMemoryTaskStore,
58
+ logger,
59
+ configureLogger,
60
+ } from "@artinet/sdk";
61
+
62
+ // Set the log level to info to see startup messages
63
+ configureLogger({ level: "info" });
64
+
65
+ // Define the simplest possible agent logic
66
+ const quickAgentLogic: TaskHandler = async function* (context: TaskContext) {
67
+ const userInput =
68
+ context.userMessage.parts[0].type === "text"
69
+ ? context.userMessage.parts[0].text
70
+ : "";
71
+ logger.info(`Quick server received: ${userInput}`);
72
+ yield {
73
+ state: "working",
74
+ message: { role: "agent", parts: [{ type: "text", text: "Thinking..." }] },
75
+ };
76
+ await new Promise((resolve) => setTimeout(resolve, 500)); // Simulate work
77
+ yield {
78
+ state: "completed",
79
+ message: {
80
+ role: "agent",
81
+ parts: [{ type: "text", text: `You said: ${userInput}` }],
82
+ },
83
+ };
84
+ logger.info(`Quick server responded.`);
85
+ };
86
+
87
+ // Configure and start the server
88
+ const server = new A2AServer({
89
+ taskHandler: quickAgentLogic,
90
+ taskStore: new InMemoryTaskStore(),
91
+ port: 4000,
92
+ basePath: "/a2a",
93
+ card: {
94
+ name: "QuickStart Agent",
95
+ url: "http://localhost:4000/a2a",
96
+ version: "0.1.0",
97
+ capabilities: { streaming: true }, // Our handler uses yield
98
+ skills: [{ id: "echo", name: "Echo Skill" }],
99
+ },
100
+ });
101
+
102
+ server.start();
103
+ logger.info("Quick Start A2A Server running on http://localhost:4000/a2a");
104
+ ```
105
+
106
+ **2. Client (`quick-client.ts`)**
107
+
108
+ ```typescript
109
+ import { A2AClient, logger, configureLogger } from "@artinet/sdk";
110
+
111
+ // Set the log level to info to see client messages
112
+ configureLogger({ level: "info" });
113
+
114
+ async function runClient() {
115
+ const client = new A2AClient("http://localhost:4000/a2a");
116
+ const message = {
117
+ role: "user" as const,
118
+ parts: [{ type: "text" as const, text: "Hello Quick Start!" }],
119
+ };
120
+
121
+ try {
122
+ logger.info("Sending task to quick server...");
123
+ const stream = client.sendTaskSubscribe({ id: "quick-task-1", message });
124
+
125
+ for await (const update of stream) {
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);
137
+ }
138
+ }
139
+
140
+ runClient();
141
+ ```
142
+
143
+ **3. Run It**
144
+
145
+ - Save the files above.
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`
149
+
150
+ You'll see the server start, the client send a message, and the server respond with status updates.
151
+
152
+ ## Client Usage
153
+
154
+ ### Basic Client Usage
155
+
156
+ ```typescript
157
+ import { A2AClient, Message, Part, TaskSendParams } from "@artinet/sdk";
158
+
159
+ // Create a new client instance targeting the agent's A2A endpoint
160
+ const client = new A2AClient("https://your-a2a-server.com/a2a"); // Ensure '/a2a' or your server's basePath is included
161
+
162
+ // Discover agent capabilities via its card (often at /.well-known/agent.json relative to the base URL)
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);
166
+
167
+ // Example: Check if the agent supports streaming before using it
168
+ const supportsStreaming = await client.supports("streaming");
169
+ console.log("Supports Streaming:", supportsStreaming);
170
+
171
+ // Create a message following the A2A schema
172
+ const message: Message = {
173
+ role: "user",
174
+ parts: [{ type: "text", text: "What is the weather in London?" }],
175
+ };
176
+
177
+ // Send a task using the standard request/response pattern
178
+ try {
179
+ const task = await client.sendTask({
180
+ id: "task-" + Date.now(), // Generate a unique task ID
181
+ message,
182
+ });
183
+ console.log(`Task ${task?.id} status: ${task?.status.state}`);
184
+
185
+ // Fetch task status later if needed
186
+ const updatedTask = await client.getTask({ id: task!.id });
187
+ console.log(`Updated Task Status: ${updatedTask?.status.state}`);
188
+ } catch (error) {
189
+ console.error("A2A Client Error:", error);
190
+ if (error instanceof RpcError) {
191
+ console.error(`RPC Error Code: ${error.code}, Message: ${error.message}`);
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### Streaming Updates
197
+
198
+ Leverage real-time updates for long-running tasks if the agent supports it.
199
+
200
+ ```typescript
201
+ import {
202
+ A2AClient,
203
+ Message,
204
+ RpcError,
205
+ TaskStatusUpdateEvent,
206
+ TaskArtifactUpdateEvent,
207
+ } from "@artinet/sdk";
208
+
209
+ const client = new A2AClient("https://your-a2a-server.com/a2a");
210
+
211
+ async function runStreamingTask() {
212
+ if (!(await client.supports("streaming"))) {
213
+ console.log("Agent does not support streaming.");
214
+ return;
215
+ }
216
+
217
+ const message: Message = {
218
+ role: "user",
219
+ parts: [
220
+ { type: "text", text: "Generate a short story and a cover image." },
221
+ ],
222
+ };
223
+
224
+ try {
225
+ const stream = client.sendTaskSubscribe({
226
+ id: "streaming-task-" + Date.now(),
227
+ message,
228
+ });
229
+
230
+ console.log("Subscribed to task updates...");
231
+
232
+ for await (const update of stream) {
233
+ if ((update as TaskStatusUpdateEvent).status) {
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}`);
269
+ }
270
+ }
271
+ }
272
+
273
+ runStreamingTask();
274
+ ```
275
+
276
+ ### Authentication
277
+
278
+ Pass authentication credentials easily.
279
+
280
+ ```typescript
281
+ import { A2AClient } from "@artinet/sdk";
282
+
283
+ const client = new A2AClient("https://your-secure-a2a-server.com/a2a");
284
+
285
+ // Add a single header (e.g., Bearer token)
286
+ client.addHeader("Authorization", "Bearer your-api-token");
287
+
288
+ // Or set multiple headers at once
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(...)
296
+ ```
297
+
298
+ ## Server Usage
299
+
300
+ Setting up an A2A-compliant agent server is remarkably straightforward with the Artinet SDK. The `A2AServer` class abstracts away the complexities of the A2A protocol and underlying JSON-RPC communication, allowing you to focus purely on implementing your agent's behavior.
301
+
302
+ ### Implementing an A2A Agent
303
+
304
+ Define your agent's core logic using an async generator `TaskHandler`.
305
+
306
+ ```typescript
307
+ import {
308
+ A2AServer,
309
+ TaskContext,
310
+ TaskHandler
311
+ InMemoryTaskStore,
312
+ logger, // Use the built-in logger
313
+ } from "@artinet/sdk";
314
+ import path from "path";
315
+
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
+
322
+ // 1. Acknowledge receipt and indicate work is starting
323
+ yield {
324
+ state: "working",
325
+ message: {
326
+ role: "agent",
327
+ parts: [{ type: "text", text: "Got it! Processing your request..." }],
328
+ },
329
+ };
330
+
331
+ // Simulate some work
332
+ await new Promise((resolve) => setTimeout(resolve, 1000));
333
+
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
+ yield {
354
+ // Artifact details
355
+ name: "report.txt",
356
+ mimeType: "text/plain",
357
+ parts: [{ type: "text", text: artifactContent }],
358
+ };
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
+
367
+ // 5. Yield the final response
368
+ const finalResponse = `Finished processing: "${userRequest}". See attached report.`;
369
+ yield {
370
+ state: "completed",
371
+ message: { role: "agent", parts: [{ type: "text", text: finalResponse }] },
372
+ };
373
+ logger.info({ taskId: context.taskId }, "Task completed successfully");
374
+ }
375
+
376
+ // Create a task store (in-memory for this example)
377
+ const store = new InMemoryTaskStore();
378
+
379
+ // Configure and create the A2A server instance
380
+ const server = new A2AServer({
381
+ myAgentLogic,
382
+ taskStore: store,
383
+ port: 3000,
384
+ basePath: "/a2a", // The path where A2A routes will be mounted
385
+ // Customize the agent card served at /.well-known/agent.json
386
+ card: {
387
+ name: "Artinet Example Agent",
388
+ description: "A robust A2A agent built with Artinet SDK",
389
+ url: "http://localhost:3000/a2a", // Must match your server's accessible A2A endpoint
390
+ version: "1.0.0",
391
+ capabilities: {
392
+ streaming: true, // This agent handler supports streaming via yields
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
404
+ },
405
+ });
406
+
407
+ // Start the server
408
+ server.start();
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
+ );
415
+ ```
416
+
417
+ ### Persistent Storage
418
+
419
+ Easily switch to file-based persistence. Ensure the data directory exists and is writable.
420
+
421
+ ```typescript
422
+ import { A2AServer, FileStore, logger } from "@artinet/sdk";
423
+ import path from "path";
424
+ import fs from "fs";
425
+
426
+ // Assume myAgentLogic is defined as above
427
+
428
+ const dataDir = path.join(process.cwd(), "a2a-data");
429
+ // Ensure the directory exists
430
+ if (!fs.existsSync(dataDir)) {
431
+ fs.mkdirSync(dataDir, { recursive: true });
432
+ logger.info(`Created data directory: ${dataDir}`);
433
+ }
434
+
435
+ // Create a file-based store
436
+ const store = new FileStore(dataDir);
437
+ logger.info(`Using FileStore at ${dataDir}`);
438
+
439
+ // Use the file store with your server
440
+ const server = new A2AServer(myAgentLogic, {
441
+ taskStore: store,
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
+ },
451
+ });
452
+ server.start();
453
+ logger.info(
454
+ `Persistent A2A Server started on http://localhost:3001${server.options.basePath}`
455
+ );
456
+ ```
457
+
458
+ ## Logging
459
+
460
+ Leverage the built-in `pino` logger for structured, performant logging.
461
+
462
+ ```typescript
463
+ import { logger, configureLogger, LogLevel } from "@artinet/sdk";
464
+
465
+ // Configure logging (optional, defaults to 'error' level)
466
+ configureLogger({
467
+ level: "debug", // 'silent', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'
468
+ name: "MyA2AApp", // Optional logger name
469
+ });
470
+
471
+ // Use the logger throughout your application
472
+ logger.info("Server starting...");
473
+ logger.error(
474
+ { err: new Error("Config Error"), detail: "Missing API key" },
475
+ "Initialization failed"
476
+ );
477
+ logger.debug({ taskId: "task-123", status: "working" }, "Processing task");
478
+
479
+ // Create child loggers for specific modules
480
+ const clientLogger = logger.child({ component: "A2AClient" });
481
+ clientLogger.info("Sending request to agent...");
482
+
483
+ // Set level via environment variable (e.g., LOG_LEVEL=debug) for flexibility
484
+ // configureLogger(); // Reads from process.env.LOG_LEVEL if set
485
+
486
+ // In production, typically use 'info' or 'warn'
487
+ // configureLogger({ level: "info" });
488
+ ```
489
+
490
+ ## Advanced Server Customization
491
+
492
+ While the `A2AServer` provides a convenient setup using a default [Jayson](https://github.com/tedeh/jayson) JSON-RPC server, you might need more control for specific use cases, such as:
493
+
494
+ - Integrating A2A methods into an existing Express application with custom middleware.
495
+ - Adding non-A2A custom JSON-RPC methods alongside the standard A2A ones.
496
+ - Using a different JSON-RPC library or implementation.
497
+
498
+ The SDK allows you to provide your own function to create the JSON-RPC server instance via the `createJSONRPCServer` option in `A2AServerOptions`.
499
+
500
+ ### Custom `createJSONRPCServer` Function
501
+
502
+ To provide your own server, implement a function that adheres to the `JSONRPCServerFactory` type defined by the SDK:
503
+
504
+ ```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
+
512
+ // Your function implements the JSONRPCServerFactory interface
513
+ const myCustomCreateServer: JSONRPCServerFactory = (
514
+ params: CreateJSONRPCServerParams
515
+ ): JSONRPCServerType => {
516
+ // 'params' contains everything needed: taskStore, card, taskHandler, etc.
517
+ // 'JSONRPCServerType' is expected to be a Jayson server instance by default
518
+
519
+ // 1. Create your Jayson server instance (or adapt for another library)
520
+ const jaysonServer = new jayson.Server(
521
+ {
522
+ // 2. Define A2A methods using the provided params (taskStore, taskHandler, etc.)
523
+ "tasks/send": async (args: any, callback: jayson.JSONRPCCallback) => {
524
+ // Implement tasks/send logic, potentially calling helper functions
525
+ // from the SDK or using params.taskStore, params.taskHandler directly.
526
+ // Remember to handle errors and use the callback.
527
+ console.log("Custom tasks/send called!");
528
+ // ... implementation ...
529
+ callback(null, { id: args.id, status: { state: "submitted" } }); // Example response
530
+ },
531
+ "tasks/get": (/* ... */) => {
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
+ );
549
+
550
+ return jaysonServer;
551
+ };
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
+
567
+ const store = new InMemoryTaskStore();
568
+
569
+ const server = new A2AServer({
570
+ taskHandler: myAgentLogic,
571
+ taskStore: store,
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
+ },
582
+ });
583
+
584
+ server.start(); // This will now use your custom server setup
585
+ ```
586
+
587
+ This provides maximum flexibility for integrating the A2A protocol handling into diverse server environments.
588
+
589
+ ## API Reference
590
+
591
+ ### Core Classes
592
+
593
+ - `A2AClient`: Client for interacting with A2A servers.
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.
607
+
608
+ ## Contributing
609
+
610
+ Contributions are welcome! Please open an issue or submit a Pull Request.
611
+
612
+ ## License
613
+
614
+ This project is licensed under the MIT License - see the `LICENSE` file for details.
615
+
616
+ ## Acknowledgements
617
+
618
+ This SDK builds upon the [official A2A Protocol schema](https://github.com/google/A2A/blob/main/samples/js/src/schema.ts) created by Google, providing a robust and developer-friendly implementation suitable for production environments.