nexus-ai-pro 2.3.0 → 2.5.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 (248) hide show
  1. package/API_STABILITY.md +271 -0
  2. package/CHANGELOG.md +330 -1
  3. package/README.md +18 -4
  4. package/SECURITY.md +209 -0
  5. package/dist/adapter-kit/contracts.d.ts +87 -0
  6. package/dist/adapter-kit/contracts.js +237 -0
  7. package/dist/adapter-kit/errors.d.ts +39 -0
  8. package/dist/adapter-kit/errors.js +52 -0
  9. package/dist/adapter-kit/index.d.ts +144 -0
  10. package/dist/adapter-kit/index.js +178 -0
  11. package/dist/adapter-kit/semver.d.ts +12 -0
  12. package/dist/adapter-kit/semver.js +55 -0
  13. package/dist/agent/middleware/pii.js +2 -33
  14. package/dist/context-hub/hub.d.ts +53 -0
  15. package/dist/context-hub/hub.js +108 -3
  16. package/dist/context-hub/index.d.ts +2 -1
  17. package/dist/context-hub/index.js +2 -1
  18. package/dist/context-hub/signing.d.ts +95 -0
  19. package/dist/context-hub/signing.js +88 -0
  20. package/dist/core/secure-stream.js +4 -3
  21. package/dist/doctor/index.d.ts +4 -2
  22. package/dist/doctor/index.js +29 -0
  23. package/dist/embeddings/adapters.js +2 -1
  24. package/dist/evals/judge.js +2 -5
  25. package/dist/evaluate/cache.d.ts +0 -14
  26. package/dist/evaluate/cache.js +0 -30
  27. package/dist/evaluate/file-cache.d.ts +15 -0
  28. package/dist/evaluate/file-cache.js +30 -0
  29. package/dist/evaluate/index.d.ts +2 -1
  30. package/dist/evaluate/index.js +2 -1
  31. package/dist/evaluate/review.js +2 -2
  32. package/dist/evaluate/run.js +2 -2
  33. package/dist/evaluate/threads.d.ts +187 -0
  34. package/dist/evaluate/threads.js +187 -0
  35. package/dist/evaluate/version.js +3 -3
  36. package/dist/graph/graph.js +2 -2
  37. package/dist/graph/node-cache.js +3 -3
  38. package/dist/hallucination/rag.d.ts +4 -1
  39. package/dist/hallucination/rag.js +6 -1
  40. package/dist/images/asset-stores.js +2 -3
  41. package/dist/images/asset-support.js +2 -2
  42. package/dist/images/assets.js +1 -2
  43. package/dist/images/evals.js +4 -4
  44. package/dist/insights/proposals.js +1 -2
  45. package/dist/insights/regressions.d.ts +26 -2
  46. package/dist/insights/regressions.js +129 -3
  47. package/dist/loaders/git.js +8 -1
  48. package/dist/loaders/web.js +7 -1
  49. package/dist/mcp/client.d.ts +14 -0
  50. package/dist/mcp/client.js +59 -1
  51. package/dist/mcp/registry.d.ts +6 -0
  52. package/dist/mcp/registry.js +7 -0
  53. package/dist/operations/adapters.d.ts +2 -1
  54. package/dist/operations/runner.js +2 -0
  55. package/dist/operations/stats.js +11 -4
  56. package/dist/postgres/index.d.ts +2 -1
  57. package/dist/postgres/index.js +4 -0
  58. package/dist/postgres/operations.js +11 -4
  59. package/dist/postgres/store.d.ts +7 -0
  60. package/dist/postgres/store.js +21 -0
  61. package/dist/postgres/tenancy.d.ts +43 -0
  62. package/dist/postgres/tenancy.js +71 -0
  63. package/dist/prompts/file.js +28 -11
  64. package/dist/protocols/a2a.d.ts +235 -0
  65. package/dist/protocols/a2a.js +386 -0
  66. package/dist/protocols/acp.d.ts +87 -0
  67. package/dist/protocols/acp.js +322 -0
  68. package/dist/protocols/ag-ui.d.ts +120 -0
  69. package/dist/protocols/ag-ui.js +180 -0
  70. package/dist/protocols/shared.d.ts +80 -0
  71. package/dist/protocols/shared.js +49 -0
  72. package/dist/providers/anthropic.js +1 -0
  73. package/dist/providers/cohere.js +2 -2
  74. package/dist/providers/google.js +2 -11
  75. package/dist/providers/openai.js +1 -0
  76. package/dist/rag/file-ingestion.js +15 -9
  77. package/dist/runtime/detect.d.ts +17 -0
  78. package/dist/runtime/detect.js +16 -0
  79. package/dist/runtime/index.d.ts +19 -0
  80. package/dist/runtime/index.js +7 -0
  81. package/dist/security/injection-detector.d.ts +2 -0
  82. package/dist/security/injection-detector.js +7 -1
  83. package/dist/security/input-guard.js +5 -2
  84. package/dist/security/output-guard.js +8 -4
  85. package/dist/security/pii-detector.js +22 -6
  86. package/dist/security/prompt-hardening.js +6 -2
  87. package/dist/security/semantic-injection-classifier.d.ts +2 -1
  88. package/dist/security/semantic-injection-classifier.js +31 -35
  89. package/dist/security/upload-scanner.d.ts +8 -0
  90. package/dist/security/upload-scanner.js +51 -9
  91. package/dist/server/deployments.d.ts +188 -11
  92. package/dist/server/deployments.js +474 -36
  93. package/dist/server/metrics.d.ts +2 -0
  94. package/dist/server/metrics.js +7 -0
  95. package/dist/server/runs.d.ts +18 -1
  96. package/dist/server/runs.js +66 -7
  97. package/dist/server/server.js +9 -1
  98. package/dist/server/state.d.ts +12 -2
  99. package/dist/server/state.js +15 -0
  100. package/dist/sqlite/operations.js +15 -4
  101. package/dist/sqlite/store.d.ts +7 -0
  102. package/dist/sqlite/store.js +28 -0
  103. package/dist/store/memory.d.ts +8 -0
  104. package/dist/store/memory.js +12 -1
  105. package/dist/store/redis.d.ts +13 -0
  106. package/dist/store/redis.js +38 -0
  107. package/dist/testing/record.js +2 -2
  108. package/dist/tracing/instrument.js +1 -0
  109. package/dist/tracing/stores.js +2 -1
  110. package/dist/tracing/tracer.d.ts +4 -0
  111. package/dist/tracing/tracer.js +8 -2
  112. package/dist/types/config.d.ts +20 -0
  113. package/dist/types/operations.d.ts +7 -0
  114. package/dist/types/server.d.ts +67 -2
  115. package/dist/types/store.d.ts +9 -0
  116. package/dist/utils/base64.d.ts +4 -0
  117. package/dist/utils/base64.js +21 -0
  118. package/dist/utils/checksums.d.ts +22 -0
  119. package/dist/utils/checksums.js +39 -0
  120. package/dist/utils/ids.d.ts +8 -0
  121. package/dist/utils/ids.js +26 -2
  122. package/dist/utils/sha256.d.ts +22 -0
  123. package/dist/utils/sha256.js +120 -0
  124. package/dist/version.d.ts +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist-cjs/adapter-kit/contracts.d.ts +87 -0
  127. package/dist-cjs/adapter-kit/contracts.js +243 -0
  128. package/dist-cjs/adapter-kit/errors.d.ts +39 -0
  129. package/dist-cjs/adapter-kit/errors.js +57 -0
  130. package/dist-cjs/adapter-kit/index.d.ts +144 -0
  131. package/dist-cjs/adapter-kit/index.js +194 -0
  132. package/dist-cjs/adapter-kit/semver.d.ts +12 -0
  133. package/dist-cjs/adapter-kit/semver.js +59 -0
  134. package/dist-cjs/agent/middleware/pii.js +4 -35
  135. package/dist-cjs/context-hub/hub.d.ts +53 -0
  136. package/dist-cjs/context-hub/hub.js +110 -4
  137. package/dist-cjs/context-hub/index.d.ts +2 -1
  138. package/dist-cjs/context-hub/index.js +7 -1
  139. package/dist-cjs/context-hub/signing.d.ts +95 -0
  140. package/dist-cjs/context-hub/signing.js +96 -0
  141. package/dist-cjs/core/secure-stream.js +4 -3
  142. package/dist-cjs/doctor/index.d.ts +4 -2
  143. package/dist-cjs/doctor/index.js +29 -0
  144. package/dist-cjs/embeddings/adapters.js +2 -1
  145. package/dist-cjs/evals/judge.js +2 -5
  146. package/dist-cjs/evaluate/cache.d.ts +0 -14
  147. package/dist-cjs/evaluate/cache.js +1 -65
  148. package/dist-cjs/evaluate/file-cache.d.ts +15 -0
  149. package/dist-cjs/evaluate/file-cache.js +67 -0
  150. package/dist-cjs/evaluate/index.d.ts +2 -1
  151. package/dist-cjs/evaluate/index.js +3 -2
  152. package/dist-cjs/evaluate/review.js +2 -2
  153. package/dist-cjs/evaluate/run.js +2 -2
  154. package/dist-cjs/evaluate/threads.d.ts +187 -0
  155. package/dist-cjs/evaluate/threads.js +198 -0
  156. package/dist-cjs/evaluate/version.js +3 -3
  157. package/dist-cjs/graph/graph.js +2 -2
  158. package/dist-cjs/graph/node-cache.js +3 -3
  159. package/dist-cjs/hallucination/rag.d.ts +4 -1
  160. package/dist-cjs/hallucination/rag.js +6 -1
  161. package/dist-cjs/images/asset-stores.js +2 -3
  162. package/dist-cjs/images/asset-support.js +2 -2
  163. package/dist-cjs/images/assets.js +1 -2
  164. package/dist-cjs/images/evals.js +4 -4
  165. package/dist-cjs/insights/proposals.js +1 -2
  166. package/dist-cjs/insights/regressions.d.ts +26 -2
  167. package/dist-cjs/insights/regressions.js +129 -3
  168. package/dist-cjs/loaders/git.js +8 -1
  169. package/dist-cjs/loaders/web.js +7 -1
  170. package/dist-cjs/mcp/client.d.ts +14 -0
  171. package/dist-cjs/mcp/client.js +60 -1
  172. package/dist-cjs/mcp/registry.d.ts +6 -0
  173. package/dist-cjs/mcp/registry.js +7 -0
  174. package/dist-cjs/operations/adapters.d.ts +2 -1
  175. package/dist-cjs/operations/runner.js +2 -0
  176. package/dist-cjs/operations/stats.js +11 -4
  177. package/dist-cjs/postgres/index.d.ts +2 -1
  178. package/dist-cjs/postgres/index.js +8 -1
  179. package/dist-cjs/postgres/operations.js +11 -4
  180. package/dist-cjs/postgres/store.d.ts +7 -0
  181. package/dist-cjs/postgres/store.js +21 -0
  182. package/dist-cjs/postgres/tenancy.d.ts +43 -0
  183. package/dist-cjs/postgres/tenancy.js +110 -0
  184. package/dist-cjs/prompts/file.js +28 -11
  185. package/dist-cjs/protocols/a2a.d.ts +235 -0
  186. package/dist-cjs/protocols/a2a.js +393 -0
  187. package/dist-cjs/protocols/acp.d.ts +87 -0
  188. package/dist-cjs/protocols/acp.js +326 -0
  189. package/dist-cjs/protocols/ag-ui.d.ts +120 -0
  190. package/dist-cjs/protocols/ag-ui.js +186 -0
  191. package/dist-cjs/protocols/shared.d.ts +80 -0
  192. package/dist-cjs/protocols/shared.js +59 -0
  193. package/dist-cjs/providers/anthropic.js +1 -0
  194. package/dist-cjs/providers/cohere.js +2 -2
  195. package/dist-cjs/providers/google.js +3 -12
  196. package/dist-cjs/providers/openai.js +1 -0
  197. package/dist-cjs/rag/file-ingestion.js +14 -8
  198. package/dist-cjs/runtime/detect.d.ts +17 -0
  199. package/dist-cjs/runtime/detect.js +19 -0
  200. package/dist-cjs/runtime/index.d.ts +19 -0
  201. package/dist-cjs/runtime/index.js +31 -0
  202. package/dist-cjs/security/injection-detector.d.ts +2 -0
  203. package/dist-cjs/security/injection-detector.js +7 -1
  204. package/dist-cjs/security/input-guard.js +5 -2
  205. package/dist-cjs/security/output-guard.js +8 -4
  206. package/dist-cjs/security/pii-detector.js +22 -6
  207. package/dist-cjs/security/prompt-hardening.js +6 -2
  208. package/dist-cjs/security/semantic-injection-classifier.d.ts +2 -1
  209. package/dist-cjs/security/semantic-injection-classifier.js +31 -35
  210. package/dist-cjs/security/upload-scanner.d.ts +8 -0
  211. package/dist-cjs/security/upload-scanner.js +53 -9
  212. package/dist-cjs/server/deployments.d.ts +188 -11
  213. package/dist-cjs/server/deployments.js +474 -36
  214. package/dist-cjs/server/metrics.d.ts +2 -0
  215. package/dist-cjs/server/metrics.js +8 -0
  216. package/dist-cjs/server/runs.d.ts +18 -1
  217. package/dist-cjs/server/runs.js +65 -6
  218. package/dist-cjs/server/server.js +9 -1
  219. package/dist-cjs/server/state.d.ts +12 -2
  220. package/dist-cjs/server/state.js +15 -0
  221. package/dist-cjs/sqlite/operations.js +15 -4
  222. package/dist-cjs/sqlite/store.d.ts +7 -0
  223. package/dist-cjs/sqlite/store.js +28 -0
  224. package/dist-cjs/store/memory.d.ts +8 -0
  225. package/dist-cjs/store/memory.js +12 -1
  226. package/dist-cjs/store/redis.d.ts +13 -0
  227. package/dist-cjs/store/redis.js +38 -0
  228. package/dist-cjs/testing/record.js +2 -2
  229. package/dist-cjs/tracing/instrument.js +1 -0
  230. package/dist-cjs/tracing/stores.js +2 -1
  231. package/dist-cjs/tracing/tracer.d.ts +4 -0
  232. package/dist-cjs/tracing/tracer.js +8 -2
  233. package/dist-cjs/types/config.d.ts +20 -0
  234. package/dist-cjs/types/operations.d.ts +7 -0
  235. package/dist-cjs/types/server.d.ts +67 -2
  236. package/dist-cjs/types/store.d.ts +9 -0
  237. package/dist-cjs/utils/base64.d.ts +4 -0
  238. package/dist-cjs/utils/base64.js +25 -0
  239. package/dist-cjs/utils/checksums.d.ts +22 -0
  240. package/dist-cjs/utils/checksums.js +46 -0
  241. package/dist-cjs/utils/ids.d.ts +8 -0
  242. package/dist-cjs/utils/ids.js +28 -2
  243. package/dist-cjs/utils/sha256.d.ts +22 -0
  244. package/dist-cjs/utils/sha256.js +125 -0
  245. package/dist-cjs/version.d.ts +1 -1
  246. package/dist-cjs/version.js +1 -1
  247. package/llms.txt +5 -2
  248. package/package.json +102 -6
@@ -0,0 +1,235 @@
1
+ import type { Message, ToolDefinition } from '../types/messages.js';
2
+ import type { Principal } from '../types/principal.js';
3
+ import { type ProtocolGraph, type ProtocolInterrupt } from './shared.js';
4
+ export type { ProtocolGraph, ProtocolInterrupt, ProtocolResult, ProtocolRunOptions } from './shared.js';
5
+ /** The protocol version this module speaks. */
6
+ export declare const A2A_PROTOCOL_VERSION = "1.0";
7
+ /** Who a message is from. */
8
+ export type A2aRole = 'ROLE_USER' | 'ROLE_AGENT';
9
+ /** Where a task stands. */
10
+ export type A2aTaskState = 'TASK_STATE_SUBMITTED' | 'TASK_STATE_WORKING' | 'TASK_STATE_INPUT_REQUIRED' | 'TASK_STATE_AUTH_REQUIRED' | 'TASK_STATE_COMPLETED' | 'TASK_STATE_FAILED' | 'TASK_STATE_CANCELED' | 'TASK_STATE_REJECTED';
11
+ /** One part of a message or an artifact: text, structured data, or a file by URL or bytes. */
12
+ export interface A2aPart {
13
+ /** Text. */
14
+ text?: string;
15
+ /** Structured data. */
16
+ data?: unknown;
17
+ /** A file, by URL. */
18
+ url?: string;
19
+ /** A file, as base64 bytes. */
20
+ raw?: string;
21
+ /** The part's media type. */
22
+ mediaType?: string;
23
+ /** A file's name. */
24
+ filename?: string;
25
+ /** Anything else. */
26
+ metadata?: Record<string, unknown>;
27
+ }
28
+ /** A message between agents. */
29
+ export interface A2aMessage {
30
+ /** Its id. */
31
+ messageId: string;
32
+ /** Who it is from. */
33
+ role: A2aRole;
34
+ /** What it says. */
35
+ parts: A2aPart[];
36
+ /** The conversation it belongs to. */
37
+ contextId?: string;
38
+ /** The task it continues, when it answers one. */
39
+ taskId?: string;
40
+ /** Anything else. */
41
+ metadata?: Record<string, unknown>;
42
+ }
43
+ /** What a task produced. */
44
+ export interface A2aArtifact {
45
+ /** Its id. */
46
+ artifactId: string;
47
+ /** Its name. */
48
+ name?: string;
49
+ /** Its content. */
50
+ parts: A2aPart[];
51
+ }
52
+ /** A unit of work an agent was asked to do. */
53
+ export interface A2aTask {
54
+ /** Its id. */
55
+ id: string;
56
+ /** The conversation it belongs to. */
57
+ contextId: string;
58
+ /** Where it stands, with the agent's latest message. */
59
+ status: {
60
+ state: A2aTaskState;
61
+ message?: A2aMessage;
62
+ timestamp?: string;
63
+ };
64
+ /** What it produced. */
65
+ artifacts?: A2aArtifact[];
66
+ /** The messages exchanged, when asked for. */
67
+ history?: A2aMessage[];
68
+ /** Anything else. */
69
+ metadata?: Record<string, unknown>;
70
+ }
71
+ /** A skill an agent card advertises. */
72
+ export interface A2aSkill {
73
+ /** Its id. */
74
+ id: string;
75
+ /** Its name. */
76
+ name: string;
77
+ /** What it does. */
78
+ description: string;
79
+ /** Labels for discovery. */
80
+ tags: string[];
81
+ /** Example requests. */
82
+ examples?: string[];
83
+ }
84
+ /** What an agent publishes about itself at `/.well-known/agent-card.json`. */
85
+ export interface A2aAgentCard {
86
+ /** Its name. */
87
+ name: string;
88
+ /** What it does. */
89
+ description: string;
90
+ /** Its version. */
91
+ version: string;
92
+ /** Who provides it. */
93
+ provider?: {
94
+ organization: string;
95
+ url?: string;
96
+ };
97
+ /** Where it is reached, and how. */
98
+ supportedInterfaces: Array<{
99
+ url: string;
100
+ protocolBinding: string;
101
+ protocolVersion: string;
102
+ }>;
103
+ /** What it supports. */
104
+ capabilities: {
105
+ streaming: boolean;
106
+ pushNotifications: boolean;
107
+ extendedAgentCard: boolean;
108
+ };
109
+ /** Media types it accepts. */
110
+ defaultInputModes: string[];
111
+ /** Media types it produces. */
112
+ defaultOutputModes: string[];
113
+ /** What it can do. */
114
+ skills: A2aSkill[];
115
+ }
116
+ /** A streamed event: a task, a status change, or a piece of an artifact. */
117
+ export type A2aStreamEvent = {
118
+ task: A2aTask;
119
+ } | {
120
+ message: A2aMessage;
121
+ } | {
122
+ statusUpdate: {
123
+ taskId: string;
124
+ contextId: string;
125
+ status: A2aTask['status'];
126
+ };
127
+ } | {
128
+ artifactUpdate: {
129
+ taskId: string;
130
+ contextId: string;
131
+ artifact: A2aArtifact;
132
+ append?: boolean;
133
+ lastChunk?: boolean;
134
+ };
135
+ };
136
+ /** A2A's error codes, beside JSON-RPC's own. */
137
+ export declare const A2A_ERRORS: {
138
+ readonly taskNotFound: -32001;
139
+ readonly taskNotCancelable: -32002;
140
+ readonly unsupportedOperation: -32004;
141
+ readonly versionNotSupported: -32009;
142
+ };
143
+ /** Raised by the client when an agent answers with a JSON-RPC error. */
144
+ export declare class A2aError extends Error {
145
+ /** The JSON-RPC error code. */
146
+ readonly code: number;
147
+ /** The error's data, when the agent sent any. */
148
+ readonly data?: unknown | undefined;
149
+ constructor(
150
+ /** The JSON-RPC error code. */
151
+ code: number, message: string,
152
+ /** The error's data, when the agent sent any. */
153
+ data?: unknown | undefined);
154
+ }
155
+ /** Options for `a2aHandler()`. */
156
+ export interface A2aServerOptions {
157
+ /** The card's content: the endpoint `url` is where this handler is mounted. */
158
+ card: Omit<A2aAgentCard, 'supportedInterfaces' | 'capabilities' | 'defaultInputModes' | 'defaultOutputModes'> & {
159
+ url: string;
160
+ defaultInputModes?: string[];
161
+ defaultOutputModes?: string[];
162
+ };
163
+ /** The graph input for a task. Defaults to `{ messages }`: the context's conversation and the new message. */
164
+ input?: (message: A2aMessage, history: Message[]) => unknown;
165
+ /**
166
+ * The answer a message gives a paused task's question. Defaults to its first data part's value,
167
+ * such as `{ approved: true }` for a tool approval, or else its text. Text answering an approval is
168
+ * read as words: yes, ok, allow, approve, or confirm allows, and anything else refuses.
169
+ */
170
+ answer?: (message: A2aMessage, interrupt: ProtocolInterrupt) => unknown;
171
+ /** Who the task is for, from the request: the place to authenticate the calling agent. */
172
+ principal?: (request: Request) => Principal | undefined | Promise<Principal | undefined>;
173
+ /** Most tasks and contexts kept in memory. Defaults to 10,000 each. */
174
+ maxTasks?: number;
175
+ }
176
+ /**
177
+ * An HTTP handler that serves a graph as an A2A agent: `GET` the card, `POST` JSON-RPC. Mount it
178
+ * at the card's `url`; it answers the well-known card path under the same origin too.
179
+ *
180
+ * ```ts
181
+ * const handler = a2aHandler(agent, {
182
+ * card: { name: 'Researcher', description: 'Answers research questions', version: '1.0.0', url: 'https://agents.example.com/a2a', skills: [] },
183
+ * });
184
+ * ```
185
+ */
186
+ export declare function a2aHandler(graph: ProtocolGraph, options: A2aServerOptions): (request: Request) => Promise<Response>;
187
+ /** Options for `a2aClient()`. */
188
+ export interface A2aClientOptions {
189
+ /** The agent: its card's URL, or its JSON-RPC endpoint, whose origin serves the card. */
190
+ url: string;
191
+ /** Replaces the global `fetch`, for authentication, a proxy, or tests. */
192
+ fetch?: typeof globalThis.fetch;
193
+ /** Headers added to every request, such as an authorization header. */
194
+ headers?: Record<string, string>;
195
+ }
196
+ /** What to send: text, and optionally data, a conversation, or the paused task it answers. */
197
+ export interface A2aSendOptions {
198
+ /** The conversation to continue. */
199
+ contextId?: string;
200
+ /** The paused task this message answers. */
201
+ taskId?: string;
202
+ /** Structured data sent beside the text, such as `{ approved: true }`. */
203
+ data?: unknown;
204
+ /** Cancels the request. */
205
+ signal?: AbortSignal;
206
+ }
207
+ /** A client for one A2A agent. */
208
+ export interface A2aClient {
209
+ /** The agent's card, fetched once. */
210
+ card(): Promise<A2aAgentCard>;
211
+ /** Sends a message and waits for the task it starts or continues. */
212
+ send(text: string, options?: A2aSendOptions): Promise<A2aTask>;
213
+ /** Sends a message and reads the task's events as they arrive. */
214
+ stream(text: string, options?: A2aSendOptions): AsyncGenerator<A2aStreamEvent>;
215
+ /** Reads a task. */
216
+ getTask(taskId: string, historyLength?: number): Promise<A2aTask>;
217
+ /** Cancels a task. */
218
+ cancel(taskId: string): Promise<A2aTask>;
219
+ }
220
+ /** A client for an A2A agent, by its card or endpoint URL. */
221
+ export declare function a2aClient(options: A2aClientOptions): A2aClient;
222
+ /** Options for `a2aTool()`. */
223
+ export interface A2aToolOptions extends A2aClientOptions {
224
+ /** The tool's name, as the model calls it. */
225
+ name: string;
226
+ /** What the remote agent does, written for the model that decides when to call it. */
227
+ description: string;
228
+ }
229
+ /**
230
+ * A remote A2A agent as a tool another agent can call. Each call is a task, and the remote agent's
231
+ * answer is the result; a remote task that needs input reports its question, which the model can
232
+ * answer by calling again with `taskId`. The tool declares the host it reaches, as
233
+ * `network:<host>`, so a permission policy grants or denies it like any other.
234
+ */
235
+ export declare function a2aTool(options: A2aToolOptions): ToolDefinition;
@@ -0,0 +1,386 @@
1
+ import { answerOf, approvalOf, messageOf, protocolId, toolCallOf, transcriptOf, } from './shared.js';
2
+ export const A2A_PROTOCOL_VERSION = '1.0';
3
+ export const A2A_ERRORS = {
4
+ taskNotFound: -32001,
5
+ taskNotCancelable: -32002,
6
+ unsupportedOperation: -32004,
7
+ versionNotSupported: -32009,
8
+ };
9
+ export class A2aError extends Error {
10
+ code;
11
+ data;
12
+ constructor(code, message, data) {
13
+ super(message);
14
+ this.code = code;
15
+ this.data = data;
16
+ this.name = 'A2aError';
17
+ }
18
+ }
19
+ const TERMINAL = new Set([
20
+ 'TASK_STATE_COMPLETED',
21
+ 'TASK_STATE_FAILED',
22
+ 'TASK_STATE_CANCELED',
23
+ 'TASK_STATE_REJECTED',
24
+ ]);
25
+ export function a2aHandler(graph, options) {
26
+ const card = {
27
+ name: options.card.name,
28
+ description: options.card.description,
29
+ version: options.card.version,
30
+ ...(options.card.provider ? { provider: options.card.provider } : {}),
31
+ supportedInterfaces: [{ url: options.card.url, protocolBinding: 'JSONRPC', protocolVersion: A2A_PROTOCOL_VERSION }],
32
+ capabilities: { streaming: true, pushNotifications: false, extendedAgentCard: false },
33
+ defaultInputModes: options.card.defaultInputModes ?? ['text/plain', 'application/json'],
34
+ defaultOutputModes: options.card.defaultOutputModes ?? ['text/plain'],
35
+ skills: options.card.skills,
36
+ };
37
+ const max = options.maxTasks ?? 10_000;
38
+ const tasks = new Map();
39
+ const contexts = new Map();
40
+ const remember = (map, key, value) => {
41
+ map.delete(key);
42
+ map.set(key, value);
43
+ if (map.size > max)
44
+ map.delete(map.keys().next().value);
45
+ };
46
+ const answer = options.answer ??
47
+ ((message, interrupt) => {
48
+ const data = message.parts.find((part) => part.data !== undefined);
49
+ if (data)
50
+ return data.data;
51
+ const text = message.parts.map((part) => part.text ?? '').join('\n');
52
+ return toolCallOf(interrupt) ? approvalOf(text) : text;
53
+ });
54
+ const execute = async (record, message, principal, onText) => {
55
+ const task = record.task;
56
+ const runOptions = {
57
+ threadId: task.id,
58
+ signal: record.controller.signal,
59
+ ...(principal ? { principal } : {}),
60
+ ...(onText
61
+ ? {
62
+ onEvent: (event) => {
63
+ if (event.type === 'message' && !event.namespace && event.chunk.kind !== 'reasoning')
64
+ onText(event.chunk.content);
65
+ },
66
+ }
67
+ : {}),
68
+ };
69
+ setStatus(task, 'TASK_STATE_WORKING');
70
+ try {
71
+ let result;
72
+ if (record.interrupts?.length) {
73
+ const answers = Object.fromEntries(record.interrupts.map((interrupt) => [interrupt.id, answer(message, interrupt)]));
74
+ record.interrupts = undefined;
75
+ result = await graph.resumeInterruptsWith(task.id, answers, runOptions);
76
+ }
77
+ else {
78
+ const history = contexts.get(task.contextId) ?? [];
79
+ const said = messageOf('user', textOfParts(message.parts));
80
+ result = await graph.invoke(options.input ? options.input(message, history) : { messages: [...history, said] }, runOptions);
81
+ remember(contexts, task.contextId, [...history, said]);
82
+ }
83
+ if (record.controller.signal.aborted)
84
+ return;
85
+ if (result.status === 'awaiting_input' && result.interrupts?.length) {
86
+ record.interrupts = result.interrupts;
87
+ setStatus(task, 'TASK_STATE_INPUT_REQUIRED', agentMessage(task, result.interrupts.map((item) => item.reason ?? 'Input is required').join('\n'), {
88
+ interrupts: result.interrupts,
89
+ }));
90
+ }
91
+ else {
92
+ const text = answerOf(result.state);
93
+ const reply = agentMessage(task, text);
94
+ record.history.push(reply);
95
+ const before = contexts.get(task.contextId) ?? [];
96
+ remember(contexts, task.contextId, transcriptOf(result.state, [...before, messageOf('assistant', text)]));
97
+ task.artifacts = [{ artifactId: `${task.id}:answer`, name: 'answer', parts: [{ text }] }];
98
+ setStatus(task, 'TASK_STATE_COMPLETED', reply);
99
+ }
100
+ }
101
+ catch (error) {
102
+ if (record.controller.signal.aborted)
103
+ return;
104
+ setStatus(task, 'TASK_STATE_FAILED', agentMessage(task, error instanceof Error ? error.message : String(error)));
105
+ }
106
+ };
107
+ const taskFor = (message) => {
108
+ if (message.taskId) {
109
+ const existing = tasks.get(message.taskId);
110
+ if (!existing)
111
+ return { error: A2A_ERRORS.taskNotFound, message: `Task ${message.taskId} was not found` };
112
+ if (existing.task.status.state !== 'TASK_STATE_INPUT_REQUIRED') {
113
+ return { error: A2A_ERRORS.unsupportedOperation, message: `Task ${message.taskId} is not waiting for input` };
114
+ }
115
+ existing.history.push(message);
116
+ existing.controller = new AbortController();
117
+ return existing;
118
+ }
119
+ const contextId = message.contextId ?? protocolId('context');
120
+ const task = {
121
+ id: protocolId('task'),
122
+ contextId,
123
+ status: { state: 'TASK_STATE_SUBMITTED', timestamp: new Date().toISOString() },
124
+ };
125
+ const record = {
126
+ task,
127
+ history: [{ ...message, contextId, taskId: task.id }],
128
+ controller: new AbortController(),
129
+ };
130
+ remember(tasks, task.id, record);
131
+ return record;
132
+ };
133
+ return async (request) => {
134
+ const url = new URL(request.url);
135
+ if (request.method === 'GET') {
136
+ if (url.pathname.endsWith('/.well-known/agent-card.json') || url.pathname.endsWith('/agent-card.json')) {
137
+ return Response.json(card);
138
+ }
139
+ return new Response('Not found', { status: 404 });
140
+ }
141
+ if (request.method !== 'POST')
142
+ return new Response('A2A calls are POSTed', { status: 405, headers: { allow: 'GET, POST' } });
143
+ const version = request.headers.get('a2a-version');
144
+ let call;
145
+ try {
146
+ call = (await request.json());
147
+ }
148
+ catch {
149
+ return rpcError(null, -32700, 'The body is not JSON');
150
+ }
151
+ const id = call.id ?? null;
152
+ if (call.jsonrpc !== '2.0' || typeof call.method !== 'string')
153
+ return rpcError(id, -32600, 'Not a JSON-RPC 2.0 request');
154
+ if (version && !version.startsWith('1.')) {
155
+ return rpcError(id, A2A_ERRORS.versionNotSupported, `This agent speaks A2A ${A2A_PROTOCOL_VERSION}, not ${version}`);
156
+ }
157
+ const params = call.params ?? {};
158
+ const principal = await options.principal?.(request);
159
+ switch (call.method) {
160
+ case 'SendMessage':
161
+ case 'SendStreamingMessage': {
162
+ const message = params.message;
163
+ if (!message || !Array.isArray(message.parts))
164
+ return rpcError(id, -32602, 'params.message with parts is required');
165
+ const record = taskFor(message);
166
+ if ('error' in record)
167
+ return rpcError(id, record.error, record.message);
168
+ if (call.method === 'SendMessage') {
169
+ const configuration = (params.configuration ?? {});
170
+ const running = execute(record, message, principal);
171
+ if (!configuration.returnImmediately)
172
+ await running;
173
+ return rpcResult(id, { task: view(record, configuration.historyLength) });
174
+ }
175
+ return streamTask(id, record, (onText) => execute(record, message, principal, onText));
176
+ }
177
+ case 'GetTask': {
178
+ const record = tasks.get(String(params.id ?? ''));
179
+ if (!record)
180
+ return rpcError(id, A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
181
+ return rpcResult(id, view(record, typeof params.historyLength === 'number' ? params.historyLength : undefined));
182
+ }
183
+ case 'CancelTask': {
184
+ const record = tasks.get(String(params.id ?? ''));
185
+ if (!record)
186
+ return rpcError(id, A2A_ERRORS.taskNotFound, `Task ${String(params.id)} was not found`);
187
+ if (TERMINAL.has(record.task.status.state)) {
188
+ return rpcError(id, A2A_ERRORS.taskNotCancelable, `Task ${record.task.id} has already finished`);
189
+ }
190
+ record.controller.abort(new Error('Cancelled by the calling agent'));
191
+ setStatus(record.task, 'TASK_STATE_CANCELED');
192
+ return rpcResult(id, view(record));
193
+ }
194
+ default:
195
+ return rpcError(id, -32601, `Method ${call.method} is not supported`);
196
+ }
197
+ };
198
+ }
199
+ function view(record, historyLength) {
200
+ return {
201
+ ...record.task,
202
+ ...(historyLength === undefined || historyLength <= 0 ? {} : { history: record.history.slice(-historyLength) }),
203
+ };
204
+ }
205
+ function streamTask(id, record, run) {
206
+ const encoder = new TextEncoder();
207
+ const task = record.task;
208
+ const body = new ReadableStream({
209
+ async start(controller) {
210
+ const send = (result) => controller.enqueue(encoder.encode(`data: ${JSON.stringify({ jsonrpc: '2.0', id, result })}\n\n`));
211
+ send({ task: { ...task } });
212
+ let streamed = false;
213
+ await run((delta) => {
214
+ if (!delta)
215
+ return;
216
+ send({
217
+ artifactUpdate: {
218
+ taskId: task.id,
219
+ contextId: task.contextId,
220
+ artifact: { artifactId: `${task.id}:answer`, name: 'answer', parts: [{ text: delta }] },
221
+ append: streamed,
222
+ },
223
+ });
224
+ streamed = true;
225
+ });
226
+ if (streamed) {
227
+ send({
228
+ artifactUpdate: {
229
+ taskId: task.id,
230
+ contextId: task.contextId,
231
+ artifact: { artifactId: `${task.id}:answer`, name: 'answer', parts: [] },
232
+ append: true,
233
+ lastChunk: true,
234
+ },
235
+ });
236
+ }
237
+ send({ statusUpdate: { taskId: task.id, contextId: task.contextId, status: task.status } });
238
+ controller.close();
239
+ },
240
+ });
241
+ return new Response(body, { headers: { 'content-type': 'text/event-stream', 'cache-control': 'no-cache' } });
242
+ }
243
+ function setStatus(task, state, message) {
244
+ task.status = { state, ...(message ? { message } : {}), timestamp: new Date().toISOString() };
245
+ }
246
+ function agentMessage(task, text, data) {
247
+ return {
248
+ messageId: protocolId('message'),
249
+ role: 'ROLE_AGENT',
250
+ parts: [{ text }, ...(data === undefined ? [] : [{ data }])],
251
+ contextId: task.contextId,
252
+ taskId: task.id,
253
+ };
254
+ }
255
+ function textOfParts(parts) {
256
+ return parts
257
+ .map((part) => (part.text !== undefined ? part.text : part.data !== undefined ? JSON.stringify(part.data) : ''))
258
+ .filter(Boolean)
259
+ .join('\n');
260
+ }
261
+ function rpcResult(id, result) {
262
+ return Response.json({ jsonrpc: '2.0', id, result });
263
+ }
264
+ function rpcError(id, code, message) {
265
+ return Response.json({ jsonrpc: '2.0', id, error: { code, message } });
266
+ }
267
+ export function a2aClient(options) {
268
+ const fetcher = options.fetch ?? globalThis.fetch;
269
+ const isCard = /agent-card\.json$/.test(options.url);
270
+ const cardUrl = isCard ? options.url : `${new URL(options.url).origin}/.well-known/agent-card.json`;
271
+ let cached;
272
+ const card = () => {
273
+ cached ??= fetcher(cardUrl, { headers: { accept: 'application/json', ...options.headers } }).then(async (response) => {
274
+ if (!response.ok)
275
+ throw new A2aError(response.status, `The agent card answered ${response.status}`);
276
+ return (await response.json());
277
+ });
278
+ return cached;
279
+ };
280
+ const endpoint = async () => isCard
281
+ ? ((await card()).supportedInterfaces.find((item) => item.protocolBinding === 'JSONRPC')?.url ?? options.url)
282
+ : options.url;
283
+ let ids = 0;
284
+ const post = async (method, params, signal) => fetcher(await endpoint(), {
285
+ method: 'POST',
286
+ headers: { 'content-type': 'application/json', 'a2a-version': A2A_PROTOCOL_VERSION, ...options.headers },
287
+ body: JSON.stringify({ jsonrpc: '2.0', id: ++ids, method, params }),
288
+ ...(signal ? { signal } : {}),
289
+ });
290
+ const call = async (method, params, signal) => {
291
+ const response = await post(method, params, signal);
292
+ const body = (await response.json());
293
+ if (body.error)
294
+ throw new A2aError(body.error.code, body.error.message, body.error.data);
295
+ return body.result;
296
+ };
297
+ const message = (text, send) => ({
298
+ messageId: protocolId('message'),
299
+ role: 'ROLE_USER',
300
+ parts: [{ text }, ...(send.data === undefined ? [] : [{ data: send.data }])],
301
+ ...(send.contextId ? { contextId: send.contextId } : {}),
302
+ ...(send.taskId ? { taskId: send.taskId } : {}),
303
+ });
304
+ return {
305
+ card,
306
+ async send(text, send = {}) {
307
+ const result = await call('SendMessage', { message: message(text, send), configuration: { returnImmediately: false } }, send.signal);
308
+ if (result.task)
309
+ return result.task;
310
+ const reply = result.message;
311
+ return {
312
+ id: reply.taskId ?? '',
313
+ contextId: reply.contextId ?? send.contextId ?? '',
314
+ status: { state: 'TASK_STATE_COMPLETED', message: reply },
315
+ artifacts: [{ artifactId: 'message', parts: reply.parts }],
316
+ };
317
+ },
318
+ async *stream(text, send = {}) {
319
+ const response = await post('SendStreamingMessage', { message: message(text, send) }, send.signal);
320
+ const reader = response.body.getReader();
321
+ const decoder = new TextDecoder();
322
+ let buffer = '';
323
+ while (true) {
324
+ const { value, done } = await reader.read();
325
+ if (done)
326
+ break;
327
+ buffer += decoder.decode(value, { stream: true });
328
+ let boundary = buffer.indexOf('\n\n');
329
+ while (boundary !== -1) {
330
+ const frame = buffer.slice(0, boundary);
331
+ buffer = buffer.slice(boundary + 2);
332
+ const data = frame
333
+ .split('\n')
334
+ .filter((line) => line.startsWith('data:'))
335
+ .map((line) => line.slice(5).trim())
336
+ .join('');
337
+ if (data) {
338
+ const parsed = JSON.parse(data);
339
+ if (parsed.error)
340
+ throw new A2aError(parsed.error.code, parsed.error.message);
341
+ if (parsed.result)
342
+ yield parsed.result;
343
+ }
344
+ boundary = buffer.indexOf('\n\n');
345
+ }
346
+ }
347
+ },
348
+ getTask: (taskId, historyLength) => call('GetTask', { id: taskId, ...(historyLength ? { historyLength } : {}) }),
349
+ cancel: (taskId) => call('CancelTask', { id: taskId }),
350
+ };
351
+ }
352
+ export function a2aTool(options) {
353
+ const client = a2aClient(options);
354
+ const host = new URL(options.url).host;
355
+ return {
356
+ name: options.name,
357
+ description: options.description,
358
+ parameters: {
359
+ type: 'object',
360
+ properties: {
361
+ task: { type: 'string', description: 'What the agent should do, with everything it needs to know.' },
362
+ contextId: { type: 'string', description: 'Continues an earlier conversation with this agent.' },
363
+ taskId: { type: 'string', description: 'Answers a question the agent asked about this task.' },
364
+ },
365
+ required: ['task'],
366
+ },
367
+ capabilities: [`network:${host}`],
368
+ execute: async (args, context) => {
369
+ const task = await client.send(String(args.task ?? ''), {
370
+ ...(typeof args.contextId === 'string' ? { contextId: args.contextId } : {}),
371
+ ...(typeof args.taskId === 'string' ? { taskId: args.taskId } : {}),
372
+ ...(context?.signal ? { signal: context.signal } : {}),
373
+ });
374
+ const text = (task.artifacts ?? [])
375
+ .flatMap((artifact) => artifact.parts.map((part) => part.text ?? ''))
376
+ .join('\n');
377
+ const said = (task.status.message?.parts ?? []).map((part) => part.text ?? '').join('\n');
378
+ return {
379
+ state: task.status.state,
380
+ taskId: task.id,
381
+ contextId: task.contextId,
382
+ answer: task.status.state === 'TASK_STATE_COMPLETED' ? text || said : said,
383
+ };
384
+ },
385
+ };
386
+ }