@galdor/a2a 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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * A2A client speaking JSON-RPC 2.0 over HTTP to a single remote agent.
3
+ *
4
+ * Each {@link Client} instance issues auto-incrementing JSON-RPC ids that are
5
+ * unique within that client. Response bodies are size-capped so a hostile
6
+ * agent cannot exhaust memory with an oversized reply, every request is bound
7
+ * by a default deadline, and redirects are confined to the original host so
8
+ * card discovery cannot be turned into a server-side request forgery pivot.
9
+ */
10
+ import type { AgentCard, Task, TaskMessage } from "./types.ts";
11
+ /** A `fetch`-compatible function, so callers can inject a custom HTTP client. */
12
+ export type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
13
+ /** Construction options for {@link Client}. */
14
+ export interface ClientOptions {
15
+ /**
16
+ * Override the `fetch` used for every request — a custom agent, auth wrapper,
17
+ * proxy, or retry layer. Same-host redirect confinement and the body cap still
18
+ * apply. Defaults to the global `fetch`.
19
+ */
20
+ fetch?: FetchLike;
21
+ /**
22
+ * Per-request deadline in milliseconds, combined with any caller signal.
23
+ * Defaults to 60000. Raise it to allow longer requests, or set 0 to disable
24
+ * the built-in deadline entirely so a caller-supplied signal has full control
25
+ * (including extending past the default 60s).
26
+ */
27
+ timeoutMs?: number;
28
+ }
29
+ /** Options for {@link Client.sendTask}. */
30
+ export interface SendOptions {
31
+ /** Id of an existing task to continue (multi-turn conversation). */
32
+ taskId?: string;
33
+ /** Logical session id grouping related tasks together. */
34
+ sessionId?: string;
35
+ /** Metadata to attach to the task on the server. */
36
+ metadata?: Record<string, unknown>;
37
+ /** Abort signal to cancel the in-flight request. */
38
+ signal?: AbortSignal;
39
+ }
40
+ /**
41
+ * Client for one remote A2A agent, identified by its base URL.
42
+ *
43
+ * @example
44
+ * const client = new Client("https://agent.example.com");
45
+ * const task = await client.sendTask(userText("hello"));
46
+ * console.log(task.status.state);
47
+ */
48
+ export declare class Client {
49
+ private readonly baseURL;
50
+ private readonly fetchImpl;
51
+ private readonly timeoutMs;
52
+ private id;
53
+ /**
54
+ * Overrides the default card-discovery URL ({@link AGENT_CARD_PATH} appended
55
+ * to the base URL). Set this when the Agent Card is served from a different
56
+ * host than the JSON-RPC endpoint.
57
+ */
58
+ agentCardURL?: string;
59
+ /**
60
+ * @param baseURL - The agent's base URL; any trailing slashes are stripped.
61
+ * @param opts - Optional custom `fetch` and per-request `timeoutMs`.
62
+ */
63
+ constructor(baseURL: string, opts?: ClientOptions);
64
+ /**
65
+ * Fetches and parses the agent's card from the well-known path.
66
+ *
67
+ * @param signal - Optional abort signal, combined with the default deadline.
68
+ * @returns The parsed {@link AgentCard}.
69
+ * @throws {A2AError} On a non-2xx HTTP status, an oversized body, a
70
+ * cross-host redirect, or invalid JSON.
71
+ */
72
+ fetchAgentCard(signal?: AbortSignal): Promise<AgentCard>;
73
+ /**
74
+ * Posts a `tasks/send` request and returns the resulting task.
75
+ *
76
+ * When `opts.taskId` is omitted the server allocates a fresh id; reuse the
77
+ * returned task's id on follow-up sends to continue a multi-turn
78
+ * conversation.
79
+ *
80
+ * @param message - The user message to send; must contain at least one part.
81
+ * @param opts - Optional task continuation, session, metadata and abort
82
+ * controls.
83
+ * @returns The task after the server has processed this turn.
84
+ * @throws {A2AError} If the message has no parts, on a transport/decoding
85
+ * failure, an oversized response, or a cross-host redirect.
86
+ * @throws {RPCError} If the agent returns a JSON-RPC error envelope.
87
+ * @example
88
+ * const task = await client.sendTask(userText("hi"), { sessionId: "s1" });
89
+ */
90
+ sendTask(message: TaskMessage, opts?: SendOptions): Promise<Task>;
91
+ /**
92
+ * Fetches the current state of a task by id.
93
+ *
94
+ * @param taskId - Id of the task to fetch; required.
95
+ * @param historyLength - When greater than 0, truncates the returned message
96
+ * log to the most-recent N messages.
97
+ * @param signal - Optional abort signal, combined with the default deadline.
98
+ * @returns The task's current {@link Task} snapshot.
99
+ * @throws {A2AError} If `taskId` is empty, on a transport/decoding failure,
100
+ * an oversized response, or a cross-host redirect.
101
+ * @throws {RPCError} If the agent returns a JSON-RPC error (e.g. unknown id).
102
+ */
103
+ getTask(taskId: string, historyLength?: number, signal?: AbortSignal): Promise<Task>;
104
+ /** Issues a JSON-RPC request and decodes the reply. */
105
+ private call;
106
+ }
107
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAe,IAAI,EAAE,WAAW,EAAmC,MAAM,YAAY,CAAC;AA2B7G,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE/E,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,oDAAoD;IACpD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,EAAE,CAAK;IAEf;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;gBACS,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB;IAMrD;;;;;;;OAOG;IACG,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;IAc9D;;;;;;;;;;;;;;;;OAgBG;IACG,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAa3E;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,SAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrF,uDAAuD;YACzC,IAAI;CA6BnB"}
package/dist/client.js ADDED
@@ -0,0 +1,241 @@
1
+ /**
2
+ * A2A client speaking JSON-RPC 2.0 over HTTP to a single remote agent.
3
+ *
4
+ * Each {@link Client} instance issues auto-incrementing JSON-RPC ids that are
5
+ * unique within that client. Response bodies are size-capped so a hostile
6
+ * agent cannot exhaust memory with an oversized reply, every request is bound
7
+ * by a default deadline, and redirects are confined to the original host so
8
+ * card discovery cannot be turned into a server-side request forgery pivot.
9
+ */
10
+ import { A2AError, AGENT_CARD_PATH, METHOD_TASKS_GET, METHOD_TASKS_SEND, RPCError } from "./types.js";
11
+ /**
12
+ * Upper bound, in bytes, on how much of a remote agent's response is buffered
13
+ * or decoded. A hostile or compromised agent could otherwise stream an
14
+ * arbitrarily large body and exhaust client memory; 4 MiB is far larger than
15
+ * any legitimate Agent Card or task reply needs.
16
+ */
17
+ const MAX_RESPONSE_BYTES = 4 << 20;
18
+ /**
19
+ * Default per-request deadline, in milliseconds. A request that has not
20
+ * completed within this window is aborted. Callers can pass their own
21
+ * {@link AbortSignal}; both bounds are honored together.
22
+ */
23
+ const DEFAULT_TIMEOUT_MS = 60_000;
24
+ /**
25
+ * Maximum number of HTTP redirects followed for a single request before the
26
+ * client gives up. Bounds the same-host redirect loop so a server cannot pin
27
+ * the client in an endless redirect cycle.
28
+ */
29
+ const MAX_REDIRECTS = 10;
30
+ /** HTTP status codes that request a redirect to the `Location` header. */
31
+ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
32
+ /**
33
+ * Client for one remote A2A agent, identified by its base URL.
34
+ *
35
+ * @example
36
+ * const client = new Client("https://agent.example.com");
37
+ * const task = await client.sendTask(userText("hello"));
38
+ * console.log(task.status.state);
39
+ */
40
+ export class Client {
41
+ baseURL;
42
+ fetchImpl;
43
+ timeoutMs;
44
+ id = 0;
45
+ /**
46
+ * Overrides the default card-discovery URL ({@link AGENT_CARD_PATH} appended
47
+ * to the base URL). Set this when the Agent Card is served from a different
48
+ * host than the JSON-RPC endpoint.
49
+ */
50
+ agentCardURL;
51
+ /**
52
+ * @param baseURL - The agent's base URL; any trailing slashes are stripped.
53
+ * @param opts - Optional custom `fetch` and per-request `timeoutMs`.
54
+ */
55
+ constructor(baseURL, opts = {}) {
56
+ this.baseURL = baseURL.replace(/\/+$/, "");
57
+ this.fetchImpl = opts.fetch ?? ((url, init) => fetch(url, init));
58
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
59
+ }
60
+ /**
61
+ * Fetches and parses the agent's card from the well-known path.
62
+ *
63
+ * @param signal - Optional abort signal, combined with the default deadline.
64
+ * @returns The parsed {@link AgentCard}.
65
+ * @throws {A2AError} On a non-2xx HTTP status, an oversized body, a
66
+ * cross-host redirect, or invalid JSON.
67
+ */
68
+ async fetchAgentCard(signal) {
69
+ const url = this.agentCardURL ?? this.baseURL + AGENT_CARD_PATH;
70
+ const resp = await sameHostFetch(url, { method: "GET" }, signal, this.fetchImpl, this.timeoutMs);
71
+ const raw = await readCapped(resp);
72
+ if (resp.status < 200 || resp.status >= 300) {
73
+ throw new A2AError(`a2a: agent card HTTP ${resp.status}: ${raw}`);
74
+ }
75
+ try {
76
+ return JSON.parse(raw);
77
+ }
78
+ catch (e) {
79
+ throw new A2AError(`a2a: decode agent card: ${String(e)}`);
80
+ }
81
+ }
82
+ /**
83
+ * Posts a `tasks/send` request and returns the resulting task.
84
+ *
85
+ * When `opts.taskId` is omitted the server allocates a fresh id; reuse the
86
+ * returned task's id on follow-up sends to continue a multi-turn
87
+ * conversation.
88
+ *
89
+ * @param message - The user message to send; must contain at least one part.
90
+ * @param opts - Optional task continuation, session, metadata and abort
91
+ * controls.
92
+ * @returns The task after the server has processed this turn.
93
+ * @throws {A2AError} If the message has no parts, on a transport/decoding
94
+ * failure, an oversized response, or a cross-host redirect.
95
+ * @throws {RPCError} If the agent returns a JSON-RPC error envelope.
96
+ * @example
97
+ * const task = await client.sendTask(userText("hi"), { sessionId: "s1" });
98
+ */
99
+ async sendTask(message, opts = {}) {
100
+ if (!message.parts || message.parts.length === 0) {
101
+ throw new A2AError("a2a: sendTask: message has no parts");
102
+ }
103
+ const params = {
104
+ message,
105
+ ...(opts.taskId !== undefined ? { id: opts.taskId } : {}),
106
+ ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}),
107
+ ...(opts.metadata !== undefined ? { metadata: opts.metadata } : {}),
108
+ };
109
+ return this.call(METHOD_TASKS_SEND, params, opts.signal);
110
+ }
111
+ /**
112
+ * Fetches the current state of a task by id.
113
+ *
114
+ * @param taskId - Id of the task to fetch; required.
115
+ * @param historyLength - When greater than 0, truncates the returned message
116
+ * log to the most-recent N messages.
117
+ * @param signal - Optional abort signal, combined with the default deadline.
118
+ * @returns The task's current {@link Task} snapshot.
119
+ * @throws {A2AError} If `taskId` is empty, on a transport/decoding failure,
120
+ * an oversized response, or a cross-host redirect.
121
+ * @throws {RPCError} If the agent returns a JSON-RPC error (e.g. unknown id).
122
+ */
123
+ async getTask(taskId, historyLength = 0, signal) {
124
+ if (!taskId) {
125
+ throw new A2AError("a2a: getTask: id is required");
126
+ }
127
+ const params = {
128
+ id: taskId,
129
+ ...(historyLength > 0 ? { historyLength } : {}),
130
+ };
131
+ return this.call(METHOD_TASKS_GET, params, signal);
132
+ }
133
+ /** Issues a JSON-RPC request and decodes the reply. */
134
+ async call(method, params, signal) {
135
+ this.id += 1;
136
+ const body = JSON.stringify({ jsonrpc: "2.0", id: this.id, method, params });
137
+ const resp = await sameHostFetch(this.baseURL, {
138
+ method: "POST",
139
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
140
+ body,
141
+ }, signal, this.fetchImpl, this.timeoutMs);
142
+ const raw = await readCapped(resp);
143
+ if (resp.status < 200 || resp.status >= 300) {
144
+ throw new A2AError(`a2a: HTTP ${resp.status}: ${raw}`);
145
+ }
146
+ let msg;
147
+ try {
148
+ msg = JSON.parse(raw);
149
+ }
150
+ catch (e) {
151
+ throw new A2AError(`a2a: decode response: ${String(e)}`);
152
+ }
153
+ if (msg.error) {
154
+ throw new RPCError(msg.error.code, msg.error.message, msg.error.data);
155
+ }
156
+ return msg.result;
157
+ }
158
+ }
159
+ /**
160
+ * Issues a fetch that follows only same-host 3xx redirects and is bound by the
161
+ * default deadline together with any caller-supplied signal.
162
+ *
163
+ * Redirects are resolved manually: a redirect whose target host differs from
164
+ * the previous request's host is refused, so a semi-trusted discovery URL
165
+ * cannot bounce the client onto an internal address (a 302 to 169.254.169.254
166
+ * or localhost). Same-host redirects are followed, up to {@link MAX_REDIRECTS}.
167
+ *
168
+ * @param url - The initial request URL.
169
+ * @param init - Request init, applied to every hop; `redirect` is forced to
170
+ * `"manual"`.
171
+ * @param signal - Optional caller signal, combined with the default deadline
172
+ * via {@link AbortSignal.any}.
173
+ * @returns The first non-redirect {@link Response}.
174
+ * @throws {A2AError} On a cross-host redirect or after too many redirects.
175
+ */
176
+ async function sameHostFetch(url, init, signal, fetchImpl = (u, i) => fetch(u, i), timeoutMs = DEFAULT_TIMEOUT_MS) {
177
+ // A positive timeout installs a default deadline; 0 disables it so a
178
+ // caller-supplied signal alone controls the deadline (and can extend it).
179
+ const timeout = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
180
+ const combined = signal && timeout ? AbortSignal.any([signal, timeout]) : (signal ?? timeout);
181
+ let current = url;
182
+ for (let hop = 0;; hop++) {
183
+ const resp = await fetchImpl(current, { ...init, redirect: "manual", ...(combined ? { signal: combined } : {}) });
184
+ if (!REDIRECT_STATUSES.has(resp.status)) {
185
+ return resp;
186
+ }
187
+ const location = resp.headers.get("location");
188
+ if (location === null || location === "") {
189
+ // A redirect status without a usable target: treat as final.
190
+ return resp;
191
+ }
192
+ if (hop >= MAX_REDIRECTS) {
193
+ throw new A2AError(`a2a: too many redirects (>${MAX_REDIRECTS})`);
194
+ }
195
+ const next = new URL(location, current);
196
+ if (next.host !== new URL(current).host) {
197
+ throw new A2AError(`a2a: refusing cross-host redirect to "${next.host}"`);
198
+ }
199
+ current = next.toString();
200
+ }
201
+ }
202
+ /**
203
+ * Reads a response body, counting bytes as they arrive and aborting once the
204
+ * size cap is exceeded — so an oversized body is rejected without first being
205
+ * fully buffered.
206
+ *
207
+ * @param resp - The response whose body to read.
208
+ * @returns The decoded body text.
209
+ * @throws {A2AError} If the body exceeds {@link MAX_RESPONSE_BYTES}.
210
+ */
211
+ async function readCapped(resp) {
212
+ const stream = resp.body;
213
+ if (stream === null) {
214
+ return "";
215
+ }
216
+ const reader = stream.getReader();
217
+ const chunks = [];
218
+ let total = 0;
219
+ for (;;) {
220
+ const { done, value } = await reader.read();
221
+ if (done) {
222
+ break;
223
+ }
224
+ if (value) {
225
+ total += value.byteLength;
226
+ if (total > MAX_RESPONSE_BYTES) {
227
+ await reader.cancel();
228
+ throw new A2AError(`a2a: response exceeds ${MAX_RESPONSE_BYTES} bytes`);
229
+ }
230
+ chunks.push(value);
231
+ }
232
+ }
233
+ const buf = new Uint8Array(total);
234
+ let offset = 0;
235
+ for (const chunk of chunks) {
236
+ buf.set(chunk, offset);
237
+ offset += chunk.byteLength;
238
+ }
239
+ return new TextDecoder().decode(buf);
240
+ }
241
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @galdor/a2a — Agent-to-Agent (A2A) protocol over JSON-RPC 2.0 and HTTP.
3
+ *
4
+ * Expose an agent over HTTP with {@link newServer}, which returns a
5
+ * Bun.serve-compatible fetch handler, and call a remote agent with
6
+ * {@link Client}. Shared wire types, constants and helpers live alongside.
7
+ */
8
+ export { AGENT_CARD_PATH, PROTOCOL_VERSION, METHOD_TASKS_SEND, METHOD_TASKS_GET, ERR_PARSE_ERROR, ERR_INVALID_REQUEST, ERR_METHOD_NOT_FOUND, ERR_INVALID_PARAMS, ERR_INTERNAL_ERROR, ERR_TASK_NOT_FOUND, ERR_INVALID_TASK_STATE, textPart, userText, agentText, messageText, appendMessage, isTerminalState, A2AError, RPCError, } from "./types.ts";
9
+ export type { AgentCard, AgentProvider, AgentCapabilities, AgentSkill, Part, Role, TaskMessage, TaskState, TaskStatus, Artifact, Task, RPCId, RPCRequest, RPCResponse, RPCErrorObject, TasksSendParams, TasksGetParams, } from "./types.ts";
10
+ export { Server, newServer, handlerFunc } from "./server.ts";
11
+ export type { Handler, HandlerFn } from "./server.ts";
12
+ export { Client } from "./client.ts";
13
+ export type { SendOptions } from "./client.ts";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,aAAa,EACb,eAAe,EACf,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,WAAW,EACX,SAAS,EACT,UAAU,EACV,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEtD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @galdor/a2a — Agent-to-Agent (A2A) protocol over JSON-RPC 2.0 and HTTP.
3
+ *
4
+ * Expose an agent over HTTP with {@link newServer}, which returns a
5
+ * Bun.serve-compatible fetch handler, and call a remote agent with
6
+ * {@link Client}. Shared wire types, constants and helpers live alongside.
7
+ */
8
+ export { AGENT_CARD_PATH, PROTOCOL_VERSION, METHOD_TASKS_SEND, METHOD_TASKS_GET, ERR_PARSE_ERROR, ERR_INVALID_REQUEST, ERR_METHOD_NOT_FOUND, ERR_INVALID_PARAMS, ERR_INTERNAL_ERROR, ERR_TASK_NOT_FOUND, ERR_INVALID_TASK_STATE, textPart, userText, agentText, messageText, appendMessage, isTerminalState, A2AError, RPCError, } from "./types.js";
9
+ export { Server, newServer, handlerFunc } from "./server.js";
10
+ export { Client } from "./client.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,WAAW,EACX,aAAa,EACb,eAAe,EACf,QAAQ,EACR,QAAQ,GACT,MAAM,YAAY,CAAC;AAqBpB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG7D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The A2A HTTP surface as a Bun.serve-compatible fetch handler.
3
+ *
4
+ * Two routes are served:
5
+ * - GET /.well-known/agent.json → the Agent Card
6
+ * - POST <any other path> → JSON-RPC (tasks/send, tasks/get)
7
+ *
8
+ * Tasks live in an in-memory store keyed by id. Sends targeting the same id are
9
+ * serialized through a per-entry promise chain, while the task handler itself
10
+ * runs without holding any data lock — so a concurrent `tasks/get` observes the
11
+ * "working" state mid-flight. The single-threaded event loop keeps each
12
+ * synchronous section atomic.
13
+ */
14
+ import type { AgentCard, Task } from "./types.ts";
15
+ /**
16
+ * Processes a single task. The handler receives the task with the user's
17
+ * incoming message already appended and is expected to mutate it in place:
18
+ * append agent turns, set artifacts, and set `status.state` to a terminal value
19
+ * (`completed`/`failed`/`canceled`) — or to `input-required` when the agent
20
+ * needs more input from the user. Throwing transitions the task to `"failed"`,
21
+ * with the thrown message recorded in `status.message`.
22
+ */
23
+ export interface Handler {
24
+ /**
25
+ * @param task - The task to advance; mutate it in place.
26
+ * @param signal - Aborts when the inbound request is cancelled.
27
+ */
28
+ handle(task: Task, signal?: AbortSignal): Promise<void>;
29
+ }
30
+ /** Plain-function form of a {@link Handler}. */
31
+ export type HandlerFn = (task: Task, signal?: AbortSignal) => Promise<void>;
32
+ /**
33
+ * Adapts a plain function into a {@link Handler}.
34
+ *
35
+ * @param fn - The task-handling function.
36
+ * @returns A {@link Handler} that delegates to `fn`.
37
+ */
38
+ export declare function handlerFunc(fn: HandlerFn): Handler;
39
+ /**
40
+ * Exposes an agent over the A2A HTTP surface.
41
+ *
42
+ * Prefer {@link newServer} for construction. Wire {@link Server.fetch} into
43
+ * Bun.serve to start listening.
44
+ *
45
+ * @example
46
+ * const server = newServer(card, async (task) => {
47
+ * appendMessage(task, agentText("hi"));
48
+ * task.status.state = "completed";
49
+ * });
50
+ * Bun.serve({ port: 8080, fetch: server.fetch });
51
+ */
52
+ export declare class Server {
53
+ /** The Agent Card served verbatim at the well-known path. */
54
+ readonly card: AgentCard;
55
+ private readonly handler;
56
+ /** Maximum number of tasks retained in the in-memory store. */
57
+ maxTasks: number;
58
+ private readonly tasks;
59
+ /**
60
+ * @param card - The Agent Card to advertise.
61
+ * @param handler - The handler that processes each task.
62
+ */
63
+ constructor(card: AgentCard, handler: Handler);
64
+ /**
65
+ * Bun.serve-compatible request handler, pre-bound so it can be passed by
66
+ * reference. Routes GETs of the well-known path to the Agent Card, POSTs to
67
+ * the JSON-RPC dispatcher, and rejects other methods with HTTP 405.
68
+ *
69
+ * @param req - The incoming HTTP request.
70
+ * @returns The HTTP response.
71
+ */
72
+ fetch: (req: Request) => Promise<Response>;
73
+ private serveAgentCard;
74
+ private serveJSONRPC;
75
+ private handleTasksSend;
76
+ private processSend;
77
+ private evictOne;
78
+ private handleTasksGet;
79
+ }
80
+ /**
81
+ * Constructs a {@link Server}, accepting either a {@link Handler} object or a
82
+ * plain {@link HandlerFn}. The card is served verbatim at the well-known path.
83
+ *
84
+ * @param card - The Agent Card to advertise.
85
+ * @param handler - The task handler, as an object or a function.
86
+ * @returns A ready-to-serve {@link Server}.
87
+ */
88
+ export declare function newServer(card: AgentCard, handler: Handler | HandlerFn): Server;
89
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAiBH,OAAO,KAAK,EACV,SAAS,EAKT,IAAI,EAIL,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,gDAAgD;AAChD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE5E;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAElD;AAeD;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAM;IACjB,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,+DAA+D;IAC/D,QAAQ,SAAqB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IAEtD;;;OAGG;gBACS,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO;IAK7C;;;;;;;OAOG;IACH,KAAK,GAAU,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAS7C;IAEF,OAAO,CAAC,cAAc;YAUR,YAAY;YA0CZ,eAAe;YAoDf,WAAW;IA6DzB,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,cAAc;CAuBvB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAG/E"}