@mantyx/sdk 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/README.md +213 -14
- package/dist/a2a-server.cjs +404 -0
- package/dist/a2a-server.cjs.map +1 -0
- package/dist/a2a-server.d.cts +170 -0
- package/dist/a2a-server.d.ts +170 -0
- package/dist/a2a-server.js +344 -0
- package/dist/a2a-server.js.map +1 -0
- package/dist/chunk-ZJINVTHD.js +1080 -0
- package/dist/chunk-ZJINVTHD.js.map +1 -0
- package/dist/client-Ce02_fV8.d.cts +591 -0
- package/dist/client-Ce02_fV8.d.ts +591 -0
- package/dist/index.cjs +591 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -261
- package/dist/index.d.ts +3 -261
- package/dist/index.js +30 -587
- package/dist/index.js.map +1 -1
- package/docs/agent-runs-protocol.md +370 -18
- package/package.json +24 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { AgentCard } from '@a2a-js/sdk';
|
|
2
|
+
export { AgentCard, Message, MessageSendParams, Task, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from '@a2a-js/sdk';
|
|
3
|
+
import { AgentExecutor, RequestContext, ExecutionEventBus } from '@a2a-js/sdk/server';
|
|
4
|
+
export { AgentExecutor, ExecutionEventBus, RequestContext } from '@a2a-js/sdk/server';
|
|
5
|
+
import { M as MantyxClient, T as ToolRef, R as ReasoningLevel } from './client-Ce02_fV8.js';
|
|
6
|
+
import 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Expose a MANTYX agent over the [Agent2Agent (A2A)](https://google-a2a.github.io/A2A/)
|
|
10
|
+
* protocol so other agents can talk to it as a peer.
|
|
11
|
+
*
|
|
12
|
+
* This module is loaded from a separate sub-export (`@mantyx/sdk/a2a-server`) so
|
|
13
|
+
* apps that don't need it never pay the bundle cost of the official A2A SDK or
|
|
14
|
+
* Express. To use it, install the optional peer deps:
|
|
15
|
+
*
|
|
16
|
+
* npm install @a2a-js/sdk express
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* import { MantyxClient } from "@mantyx/sdk";
|
|
20
|
+
* import { serveAgentOverA2A } from "@mantyx/sdk/a2a-server";
|
|
21
|
+
*
|
|
22
|
+
* const client = new MantyxClient({
|
|
23
|
+
* apiKey: process.env.MANTYX_API_KEY!,
|
|
24
|
+
* workspaceSlug: process.env.MANTYX_WORKSPACE_SLUG!,
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* const server = await serveAgentOverA2A({
|
|
28
|
+
* client,
|
|
29
|
+
* port: 4000,
|
|
30
|
+
* agent: { agentId: "agent_cm6abc123" },
|
|
31
|
+
* agentCard: {
|
|
32
|
+
* name: "Acme Support",
|
|
33
|
+
* description: "Answers billing and account questions.",
|
|
34
|
+
* protocolVersion: "0.3.0",
|
|
35
|
+
* version: "1.0.0",
|
|
36
|
+
* url: "http://localhost:4000",
|
|
37
|
+
* skills: [{ id: "support", name: "Support", description: "Customer support",
|
|
38
|
+
* tags: ["support"] }],
|
|
39
|
+
* capabilities: { streaming: true, pushNotifications: false },
|
|
40
|
+
* defaultInputModes: ["text"],
|
|
41
|
+
* defaultOutputModes: ["text"],
|
|
42
|
+
* },
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* console.log(`Listening on ${server.url}`);
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Description of the MANTYX agent that should answer A2A requests.
|
|
50
|
+
*
|
|
51
|
+
* Mirrors the existing `runAgent` / `createSession` argument shape:
|
|
52
|
+
* - `agentId` triggers a persisted workspace agent.
|
|
53
|
+
* - `systemPrompt` (with optional `modelId`, `tools`, …) defines an ephemeral
|
|
54
|
+
* agent inline.
|
|
55
|
+
*
|
|
56
|
+
* Either `agentId` or `systemPrompt` is required.
|
|
57
|
+
*/
|
|
58
|
+
interface MantyxAgentSpec {
|
|
59
|
+
/** Reference to a persisted MANTYX agent. Mutually exclusive with `systemPrompt`. */
|
|
60
|
+
agentId?: string;
|
|
61
|
+
/** System prompt for an inline / ephemeral agent. Mutually exclusive with `agentId`. */
|
|
62
|
+
systemPrompt?: string;
|
|
63
|
+
modelId?: string;
|
|
64
|
+
tools?: ToolRef[];
|
|
65
|
+
reasoningLevel?: ReasoningLevel;
|
|
66
|
+
metadata?: Record<string, string>;
|
|
67
|
+
budgets?: {
|
|
68
|
+
maxToolTurns?: number;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Optional human-readable display name for runs created against MANTYX.
|
|
72
|
+
* Visible in the dashboard. Has no effect on the A2A side.
|
|
73
|
+
*/
|
|
74
|
+
name?: string;
|
|
75
|
+
}
|
|
76
|
+
interface MantyxAgentExecutorOptions {
|
|
77
|
+
client: MantyxClient;
|
|
78
|
+
agent: MantyxAgentSpec;
|
|
79
|
+
/**
|
|
80
|
+
* How to map an incoming A2A `contextId` onto a MANTYX session.
|
|
81
|
+
*
|
|
82
|
+
* - `"auto"` (default): each unique `contextId` opens a MANTYX session on
|
|
83
|
+
* first contact and reuses it for subsequent messages with the same
|
|
84
|
+
* `contextId`. Gives you multi-turn out of the box.
|
|
85
|
+
* - `"stateless"`: every A2A message becomes an independent `runAgent`. No
|
|
86
|
+
* conversational memory; simpler resource model.
|
|
87
|
+
*/
|
|
88
|
+
conversation?: "auto" | "stateless";
|
|
89
|
+
/**
|
|
90
|
+
* LRU cap on the in-memory `contextId -> AgentSession` table. When the cap
|
|
91
|
+
* is exceeded the oldest session is `end()`-ed and evicted. Default: 1024.
|
|
92
|
+
* Only consulted when `conversation: "auto"`.
|
|
93
|
+
*/
|
|
94
|
+
maxSessions?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Receives streaming MANTYX `assistant_delta` text. The default behaviour
|
|
97
|
+
* forwards every delta as a `TaskStatusUpdateEvent` (state: "working")
|
|
98
|
+
* containing the delta as a `text` part — this is what enables A2A
|
|
99
|
+
* `message/stream` clients to see real-time tokens. Override only if you
|
|
100
|
+
* need to swallow them or transform the wire shape.
|
|
101
|
+
*/
|
|
102
|
+
onAssistantDelta?: (delta: string, ctx: RequestContext, eventBus: ExecutionEventBus) => void;
|
|
103
|
+
}
|
|
104
|
+
interface ServeAgentOverA2AOptions extends MantyxAgentExecutorOptions {
|
|
105
|
+
/** A2A Agent Card published at `/.well-known/agent-card.json`. */
|
|
106
|
+
agentCard: AgentCard;
|
|
107
|
+
/** TCP port to listen on. Default: 0 (let the OS pick). */
|
|
108
|
+
port?: number;
|
|
109
|
+
/** Bind address. Default: `"0.0.0.0"`. */
|
|
110
|
+
host?: string;
|
|
111
|
+
/** Path that serves the Agent Card JSON. Default: `"/.well-known/agent-card.json"`. */
|
|
112
|
+
agentCardPath?: string;
|
|
113
|
+
/** Path that serves the JSON-RPC endpoint. Default: `"/"`. */
|
|
114
|
+
jsonRpcPath?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Path that serves the HTTP+JSON/REST endpoint. Default: `"/v1"`.
|
|
117
|
+
* Set to `false` to disable the REST mount entirely.
|
|
118
|
+
*/
|
|
119
|
+
restPath?: string | false;
|
|
120
|
+
}
|
|
121
|
+
interface ServeAgentOverA2AHandle {
|
|
122
|
+
/** Origin of the running server, e.g. `"http://localhost:4000"`. */
|
|
123
|
+
url: string;
|
|
124
|
+
/** Resolved port number (useful when you let the OS pick one). */
|
|
125
|
+
port: number;
|
|
126
|
+
/** Stop the HTTP server, end every cached MANTYX session, and free MCP transports. */
|
|
127
|
+
close: () => Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Implementation of `@a2a-js/sdk`'s `AgentExecutor` that backs a MANTYX agent.
|
|
131
|
+
*
|
|
132
|
+
* Most callers want `serveAgentOverA2A` instead; reach for this class directly
|
|
133
|
+
* when you need to mount the executor inside an existing Express, Fastify, or
|
|
134
|
+
* Connect app.
|
|
135
|
+
*/
|
|
136
|
+
declare class MantyxAgentExecutor implements AgentExecutor {
|
|
137
|
+
readonly client: MantyxClient;
|
|
138
|
+
readonly agent: MantyxAgentSpec;
|
|
139
|
+
readonly conversation: "auto" | "stateless";
|
|
140
|
+
readonly maxSessions: number;
|
|
141
|
+
readonly onAssistantDelta?: MantyxAgentExecutorOptions["onAssistantDelta"];
|
|
142
|
+
/** contextId -> live MANTYX session. Maintained as an LRU map. */
|
|
143
|
+
private readonly sessions;
|
|
144
|
+
/** taskIds we've been asked to cancel; checked between turns. */
|
|
145
|
+
private readonly cancelled;
|
|
146
|
+
/** Pending AbortControllers per task, used for cooperative cancel. */
|
|
147
|
+
private readonly inFlight;
|
|
148
|
+
constructor(options: MantyxAgentExecutorOptions);
|
|
149
|
+
execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void>;
|
|
150
|
+
cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void>;
|
|
151
|
+
/**
|
|
152
|
+
* Close every cached session. Idempotent. Safe to call from server shutdown
|
|
153
|
+
* paths.
|
|
154
|
+
*/
|
|
155
|
+
close(): Promise<void>;
|
|
156
|
+
private runOnce;
|
|
157
|
+
private getOrCreateSession;
|
|
158
|
+
private evictIfNeeded;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Spin up a small HTTP server that exposes a MANTYX agent as an A2A peer.
|
|
162
|
+
* Mounts the Agent Card, JSON-RPC, and (optionally) REST endpoints from the
|
|
163
|
+
* official `@a2a-js/sdk` library.
|
|
164
|
+
*
|
|
165
|
+
* Throws if `express` / `@a2a-js/sdk` aren't installed; install them as peer
|
|
166
|
+
* deps with `npm install express @a2a-js/sdk`.
|
|
167
|
+
*/
|
|
168
|
+
declare function serveAgentOverA2A(options: ServeAgentOverA2AOptions): Promise<ServeAgentOverA2AHandle>;
|
|
169
|
+
|
|
170
|
+
export { MantyxAgentExecutor, type MantyxAgentExecutorOptions, type MantyxAgentSpec, type ServeAgentOverA2AHandle, type ServeAgentOverA2AOptions, serveAgentOverA2A };
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MantyxError,
|
|
3
|
+
MantyxRunError
|
|
4
|
+
} from "./chunk-ZJINVTHD.js";
|
|
5
|
+
|
|
6
|
+
// src/a2a-server.ts
|
|
7
|
+
var MantyxAgentExecutor = class {
|
|
8
|
+
client;
|
|
9
|
+
agent;
|
|
10
|
+
conversation;
|
|
11
|
+
maxSessions;
|
|
12
|
+
onAssistantDelta;
|
|
13
|
+
/** contextId -> live MANTYX session. Maintained as an LRU map. */
|
|
14
|
+
sessions = /* @__PURE__ */ new Map();
|
|
15
|
+
/** taskIds we've been asked to cancel; checked between turns. */
|
|
16
|
+
cancelled = /* @__PURE__ */ new Set();
|
|
17
|
+
/** Pending AbortControllers per task, used for cooperative cancel. */
|
|
18
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
19
|
+
constructor(options) {
|
|
20
|
+
if (!options.client) {
|
|
21
|
+
throw new MantyxError("MantyxAgentExecutor: `client` is required");
|
|
22
|
+
}
|
|
23
|
+
validateAgentSpec(options.agent);
|
|
24
|
+
this.client = options.client;
|
|
25
|
+
this.agent = options.agent;
|
|
26
|
+
this.conversation = options.conversation ?? "auto";
|
|
27
|
+
this.maxSessions = options.maxSessions ?? 1024;
|
|
28
|
+
if (options.onAssistantDelta) this.onAssistantDelta = options.onAssistantDelta;
|
|
29
|
+
}
|
|
30
|
+
async execute(requestContext, eventBus) {
|
|
31
|
+
const { userMessage, taskId, contextId, task } = requestContext;
|
|
32
|
+
const userText = extractText(userMessage);
|
|
33
|
+
const abort = new AbortController();
|
|
34
|
+
this.inFlight.set(taskId, abort);
|
|
35
|
+
try {
|
|
36
|
+
if (!task) {
|
|
37
|
+
eventBus.publish({
|
|
38
|
+
kind: "task",
|
|
39
|
+
id: taskId,
|
|
40
|
+
contextId,
|
|
41
|
+
status: { state: "submitted", timestamp: (/* @__PURE__ */ new Date()).toISOString() },
|
|
42
|
+
history: [userMessage]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
eventBus.publish(statusUpdate(taskId, contextId, "working", false));
|
|
46
|
+
if (this.cancelled.has(taskId)) {
|
|
47
|
+
eventBus.publish(statusUpdate(taskId, contextId, "canceled", true));
|
|
48
|
+
eventBus.finished();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const onDelta = (delta) => {
|
|
52
|
+
if (this.onAssistantDelta) {
|
|
53
|
+
this.onAssistantDelta(delta, requestContext, eventBus);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
eventBus.publish(deltaStatusUpdate(taskId, contextId, delta));
|
|
57
|
+
};
|
|
58
|
+
let result;
|
|
59
|
+
try {
|
|
60
|
+
result = await this.runOnce(contextId, userText, onDelta, abort.signal);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
eventBus.publish(
|
|
63
|
+
completedStatusUpdate(
|
|
64
|
+
taskId,
|
|
65
|
+
contextId,
|
|
66
|
+
this.cancelled.has(taskId) ? "canceled" : "failed",
|
|
67
|
+
errorText(err)
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
eventBus.finished();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
eventBus.publish(completedStatusUpdate(taskId, contextId, "completed", result.text ?? ""));
|
|
74
|
+
eventBus.finished();
|
|
75
|
+
} finally {
|
|
76
|
+
this.inFlight.delete(taskId);
|
|
77
|
+
this.cancelled.delete(taskId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async cancelTask(taskId, eventBus) {
|
|
81
|
+
this.cancelled.add(taskId);
|
|
82
|
+
const ctrl = this.inFlight.get(taskId);
|
|
83
|
+
if (ctrl) ctrl.abort();
|
|
84
|
+
void eventBus;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Close every cached session. Idempotent. Safe to call from server shutdown
|
|
88
|
+
* paths.
|
|
89
|
+
*/
|
|
90
|
+
async close() {
|
|
91
|
+
const sessions = Array.from(this.sessions.values());
|
|
92
|
+
this.sessions.clear();
|
|
93
|
+
await Promise.allSettled(sessions.map((s) => s.end()));
|
|
94
|
+
}
|
|
95
|
+
// -------------------------------------------------- private session helpers
|
|
96
|
+
async runOnce(contextId, prompt, onAssistantDelta, signal) {
|
|
97
|
+
if (this.conversation === "stateless") {
|
|
98
|
+
const runSpec = {
|
|
99
|
+
...specForRun(this.agent),
|
|
100
|
+
prompt,
|
|
101
|
+
onAssistantDelta,
|
|
102
|
+
signal
|
|
103
|
+
};
|
|
104
|
+
return this.client.runAgent(runSpec);
|
|
105
|
+
}
|
|
106
|
+
const session = await this.getOrCreateSession(contextId);
|
|
107
|
+
return session.send(prompt, { onAssistantDelta, signal });
|
|
108
|
+
}
|
|
109
|
+
async getOrCreateSession(contextId) {
|
|
110
|
+
const existing = this.sessions.get(contextId);
|
|
111
|
+
if (existing) {
|
|
112
|
+
this.sessions.delete(contextId);
|
|
113
|
+
this.sessions.set(contextId, existing);
|
|
114
|
+
return existing;
|
|
115
|
+
}
|
|
116
|
+
const sessionSpec = specForSession(this.agent, contextId);
|
|
117
|
+
const session = await this.client.createSession(sessionSpec);
|
|
118
|
+
this.sessions.set(contextId, session);
|
|
119
|
+
await this.evictIfNeeded();
|
|
120
|
+
return session;
|
|
121
|
+
}
|
|
122
|
+
async evictIfNeeded() {
|
|
123
|
+
while (this.sessions.size > this.maxSessions) {
|
|
124
|
+
const oldestKey = this.sessions.keys().next().value;
|
|
125
|
+
if (!oldestKey) break;
|
|
126
|
+
const oldest = this.sessions.get(oldestKey);
|
|
127
|
+
this.sessions.delete(oldestKey);
|
|
128
|
+
try {
|
|
129
|
+
await oldest.end();
|
|
130
|
+
} catch {
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
async function serveAgentOverA2A(options) {
|
|
136
|
+
const a2a = await loadServerSdk();
|
|
137
|
+
const expressMod = await loadExpress();
|
|
138
|
+
const executor = new MantyxAgentExecutor(options);
|
|
139
|
+
const requestHandler = new a2a.DefaultRequestHandler(
|
|
140
|
+
options.agentCard,
|
|
141
|
+
new a2a.InMemoryTaskStore(),
|
|
142
|
+
executor
|
|
143
|
+
);
|
|
144
|
+
const app = expressMod();
|
|
145
|
+
app.use(expressMod.json());
|
|
146
|
+
const cardPath = options.agentCardPath ?? "/.well-known/agent-card.json";
|
|
147
|
+
const jsonRpcPath = options.jsonRpcPath ?? "/";
|
|
148
|
+
const restPath = options.restPath === void 0 ? "/v1" : options.restPath;
|
|
149
|
+
app.use(
|
|
150
|
+
cardPath,
|
|
151
|
+
a2a.expressApp.agentCardHandler({ agentCardProvider: requestHandler })
|
|
152
|
+
);
|
|
153
|
+
if (restPath !== false) {
|
|
154
|
+
app.use(
|
|
155
|
+
restPath,
|
|
156
|
+
a2a.expressApp.restHandler({
|
|
157
|
+
requestHandler,
|
|
158
|
+
userBuilder: a2a.expressApp.UserBuilder.noAuthentication
|
|
159
|
+
})
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
app.use(
|
|
163
|
+
jsonRpcPath,
|
|
164
|
+
a2a.expressApp.jsonRpcHandler({
|
|
165
|
+
requestHandler,
|
|
166
|
+
userBuilder: a2a.expressApp.UserBuilder.noAuthentication
|
|
167
|
+
})
|
|
168
|
+
);
|
|
169
|
+
const port = options.port ?? 0;
|
|
170
|
+
const host = options.host ?? "0.0.0.0";
|
|
171
|
+
const server = app.listen(port, host);
|
|
172
|
+
await new Promise((resolve, reject) => {
|
|
173
|
+
server.once("listening", resolve);
|
|
174
|
+
server.once("error", reject);
|
|
175
|
+
});
|
|
176
|
+
const address = server.address();
|
|
177
|
+
if (!address || typeof address === "string") {
|
|
178
|
+
server.close();
|
|
179
|
+
throw new MantyxError("serveAgentOverA2A: failed to bind HTTP listener");
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
port: address.port,
|
|
183
|
+
url: `http://${displayHost(host)}:${address.port}`,
|
|
184
|
+
close: async () => {
|
|
185
|
+
await new Promise(
|
|
186
|
+
(resolve, reject) => server.close((err) => err ? reject(err) : resolve())
|
|
187
|
+
);
|
|
188
|
+
await executor.close();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function statusUpdate(taskId, contextId, state, final) {
|
|
193
|
+
return {
|
|
194
|
+
kind: "status-update",
|
|
195
|
+
taskId,
|
|
196
|
+
contextId,
|
|
197
|
+
status: { state, timestamp: (/* @__PURE__ */ new Date()).toISOString() },
|
|
198
|
+
final
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function deltaStatusUpdate(taskId, contextId, delta) {
|
|
202
|
+
return {
|
|
203
|
+
kind: "status-update",
|
|
204
|
+
taskId,
|
|
205
|
+
contextId,
|
|
206
|
+
status: {
|
|
207
|
+
state: "working",
|
|
208
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
209
|
+
message: {
|
|
210
|
+
kind: "message",
|
|
211
|
+
messageId: randomMessageId(),
|
|
212
|
+
role: "agent",
|
|
213
|
+
parts: [{ kind: "text", text: delta }],
|
|
214
|
+
contextId,
|
|
215
|
+
taskId
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
final: false
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function completedStatusUpdate(taskId, contextId, state, text) {
|
|
222
|
+
return {
|
|
223
|
+
kind: "status-update",
|
|
224
|
+
taskId,
|
|
225
|
+
contextId,
|
|
226
|
+
status: {
|
|
227
|
+
state,
|
|
228
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
229
|
+
message: {
|
|
230
|
+
kind: "message",
|
|
231
|
+
messageId: randomMessageId(),
|
|
232
|
+
role: "agent",
|
|
233
|
+
parts: [{ kind: "text", text }],
|
|
234
|
+
contextId,
|
|
235
|
+
taskId
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
final: true
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function extractText(message) {
|
|
242
|
+
if (!message) return "";
|
|
243
|
+
const parts = message.parts ?? [];
|
|
244
|
+
const out = [];
|
|
245
|
+
for (const p of parts) {
|
|
246
|
+
if (p.kind === "text") {
|
|
247
|
+
const t = p.text;
|
|
248
|
+
if (typeof t === "string") out.push(t);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return out.join("\n");
|
|
252
|
+
}
|
|
253
|
+
function specForRun(spec) {
|
|
254
|
+
const out = {};
|
|
255
|
+
if (spec.agentId) out.agentId = spec.agentId;
|
|
256
|
+
if (spec.systemPrompt) out.systemPrompt = spec.systemPrompt;
|
|
257
|
+
if (spec.modelId) out.modelId = spec.modelId;
|
|
258
|
+
if (spec.tools) out.tools = spec.tools;
|
|
259
|
+
if (spec.reasoningLevel !== void 0) out.reasoningLevel = spec.reasoningLevel;
|
|
260
|
+
if (spec.metadata) out.metadata = spec.metadata;
|
|
261
|
+
if (spec.budgets) out.budgets = spec.budgets;
|
|
262
|
+
if (spec.name) out.name = spec.name;
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
function specForSession(spec, contextId) {
|
|
266
|
+
const out = {};
|
|
267
|
+
if (spec.agentId) out.agentId = spec.agentId;
|
|
268
|
+
if (spec.systemPrompt) out.systemPrompt = spec.systemPrompt;
|
|
269
|
+
if (spec.modelId) out.modelId = spec.modelId;
|
|
270
|
+
if (spec.tools) out.tools = spec.tools;
|
|
271
|
+
if (spec.reasoningLevel !== void 0) out.reasoningLevel = spec.reasoningLevel;
|
|
272
|
+
const meta = { ...spec.metadata ?? {} };
|
|
273
|
+
if (!meta.a2a_context_id) meta.a2a_context_id = contextId;
|
|
274
|
+
out.metadata = meta;
|
|
275
|
+
if (spec.budgets) out.budgets = spec.budgets;
|
|
276
|
+
if (spec.name) out.name = spec.name;
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
function validateAgentSpec(spec) {
|
|
280
|
+
if (!spec.agentId && (!spec.systemPrompt || spec.systemPrompt.length === 0)) {
|
|
281
|
+
throw new MantyxError(
|
|
282
|
+
"MantyxAgentExecutor: `agent.agentId` or `agent.systemPrompt` is required"
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function errorText(err) {
|
|
287
|
+
if (err instanceof MantyxRunError) {
|
|
288
|
+
return `MANTYX run failed (${err.subtype ?? "unknown"}): ${err.message}`;
|
|
289
|
+
}
|
|
290
|
+
if (err instanceof Error) return err.message;
|
|
291
|
+
try {
|
|
292
|
+
return String(err);
|
|
293
|
+
} catch {
|
|
294
|
+
return "unknown error";
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function randomMessageId() {
|
|
298
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
299
|
+
return globalThis.crypto.randomUUID();
|
|
300
|
+
}
|
|
301
|
+
return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
302
|
+
}
|
|
303
|
+
function displayHost(host) {
|
|
304
|
+
if (host === "0.0.0.0" || host === "::") return "localhost";
|
|
305
|
+
return host;
|
|
306
|
+
}
|
|
307
|
+
async function loadExpress() {
|
|
308
|
+
try {
|
|
309
|
+
const mod = await import("express");
|
|
310
|
+
return "default" in mod ? mod.default : mod;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
throw new MantyxError(
|
|
313
|
+
"serveAgentOverA2A: `express` is required but not installed. Run `npm install express @a2a-js/sdk` to enable the A2A server."
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function loadServerSdk() {
|
|
318
|
+
let server;
|
|
319
|
+
let express;
|
|
320
|
+
try {
|
|
321
|
+
server = await import("@a2a-js/sdk/server");
|
|
322
|
+
} catch (err) {
|
|
323
|
+
throw new MantyxError(
|
|
324
|
+
"serveAgentOverA2A: `@a2a-js/sdk` is required but not installed. Run `npm install @a2a-js/sdk express` to enable the A2A server."
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
express = await import("@a2a-js/sdk/server/express");
|
|
329
|
+
} catch (err) {
|
|
330
|
+
throw new MantyxError(
|
|
331
|
+
"serveAgentOverA2A: `@a2a-js/sdk/server/express` could not be loaded; ensure the installed `@a2a-js/sdk` is at least v0.3."
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
DefaultRequestHandler: server.DefaultRequestHandler,
|
|
336
|
+
InMemoryTaskStore: server.InMemoryTaskStore,
|
|
337
|
+
expressApp: express
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
export {
|
|
341
|
+
MantyxAgentExecutor,
|
|
342
|
+
serveAgentOverA2A
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=a2a-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/a2a-server.ts"],"sourcesContent":["/**\n * Expose a MANTYX agent over the [Agent2Agent (A2A)](https://google-a2a.github.io/A2A/)\n * protocol so other agents can talk to it as a peer.\n *\n * This module is loaded from a separate sub-export (`@mantyx/sdk/a2a-server`) so\n * apps that don't need it never pay the bundle cost of the official A2A SDK or\n * Express. To use it, install the optional peer deps:\n *\n * npm install @a2a-js/sdk express\n *\n * @example\n * import { MantyxClient } from \"@mantyx/sdk\";\n * import { serveAgentOverA2A } from \"@mantyx/sdk/a2a-server\";\n *\n * const client = new MantyxClient({\n * apiKey: process.env.MANTYX_API_KEY!,\n * workspaceSlug: process.env.MANTYX_WORKSPACE_SLUG!,\n * });\n *\n * const server = await serveAgentOverA2A({\n * client,\n * port: 4000,\n * agent: { agentId: \"agent_cm6abc123\" },\n * agentCard: {\n * name: \"Acme Support\",\n * description: \"Answers billing and account questions.\",\n * protocolVersion: \"0.3.0\",\n * version: \"1.0.0\",\n * url: \"http://localhost:4000\",\n * skills: [{ id: \"support\", name: \"Support\", description: \"Customer support\",\n * tags: [\"support\"] }],\n * capabilities: { streaming: true, pushNotifications: false },\n * defaultInputModes: [\"text\"],\n * defaultOutputModes: [\"text\"],\n * },\n * });\n *\n * console.log(`Listening on ${server.url}`);\n */\n\nimport type {\n AgentCard,\n Message,\n MessageSendParams,\n Part,\n Task,\n TaskArtifactUpdateEvent,\n TaskStatusUpdateEvent,\n} from \"@a2a-js/sdk\";\nimport type {\n AgentExecutor,\n ExecutionEventBus,\n RequestContext,\n} from \"@a2a-js/sdk/server\";\n\nimport {\n AgentSession,\n type MantyxClient,\n type RunResult,\n type SessionSpec,\n type RunSpec,\n} from \"./client.js\";\nimport { MantyxError, MantyxRunError } from \"./errors.js\";\nimport type { ReasoningLevel, ToolRef } from \"./tools.js\";\n\n// --------------------------------------------------------------- Public API\n\n/**\n * Description of the MANTYX agent that should answer A2A requests.\n *\n * Mirrors the existing `runAgent` / `createSession` argument shape:\n * - `agentId` triggers a persisted workspace agent.\n * - `systemPrompt` (with optional `modelId`, `tools`, …) defines an ephemeral\n * agent inline.\n *\n * Either `agentId` or `systemPrompt` is required.\n */\nexport interface MantyxAgentSpec {\n /** Reference to a persisted MANTYX agent. Mutually exclusive with `systemPrompt`. */\n agentId?: string;\n /** System prompt for an inline / ephemeral agent. Mutually exclusive with `agentId`. */\n systemPrompt?: string;\n modelId?: string;\n tools?: ToolRef[];\n reasoningLevel?: ReasoningLevel;\n metadata?: Record<string, string>;\n budgets?: { maxToolTurns?: number };\n /**\n * Optional human-readable display name for runs created against MANTYX.\n * Visible in the dashboard. Has no effect on the A2A side.\n */\n name?: string;\n}\n\nexport interface MantyxAgentExecutorOptions {\n client: MantyxClient;\n agent: MantyxAgentSpec;\n /**\n * How to map an incoming A2A `contextId` onto a MANTYX session.\n *\n * - `\"auto\"` (default): each unique `contextId` opens a MANTYX session on\n * first contact and reuses it for subsequent messages with the same\n * `contextId`. Gives you multi-turn out of the box.\n * - `\"stateless\"`: every A2A message becomes an independent `runAgent`. No\n * conversational memory; simpler resource model.\n */\n conversation?: \"auto\" | \"stateless\";\n /**\n * LRU cap on the in-memory `contextId -> AgentSession` table. When the cap\n * is exceeded the oldest session is `end()`-ed and evicted. Default: 1024.\n * Only consulted when `conversation: \"auto\"`.\n */\n maxSessions?: number;\n /**\n * Receives streaming MANTYX `assistant_delta` text. The default behaviour\n * forwards every delta as a `TaskStatusUpdateEvent` (state: \"working\")\n * containing the delta as a `text` part — this is what enables A2A\n * `message/stream` clients to see real-time tokens. Override only if you\n * need to swallow them or transform the wire shape.\n */\n onAssistantDelta?: (delta: string, ctx: RequestContext, eventBus: ExecutionEventBus) => void;\n}\n\nexport interface ServeAgentOverA2AOptions extends MantyxAgentExecutorOptions {\n /** A2A Agent Card published at `/.well-known/agent-card.json`. */\n agentCard: AgentCard;\n /** TCP port to listen on. Default: 0 (let the OS pick). */\n port?: number;\n /** Bind address. Default: `\"0.0.0.0\"`. */\n host?: string;\n /** Path that serves the Agent Card JSON. Default: `\"/.well-known/agent-card.json\"`. */\n agentCardPath?: string;\n /** Path that serves the JSON-RPC endpoint. Default: `\"/\"`. */\n jsonRpcPath?: string;\n /**\n * Path that serves the HTTP+JSON/REST endpoint. Default: `\"/v1\"`.\n * Set to `false` to disable the REST mount entirely.\n */\n restPath?: string | false;\n}\n\nexport interface ServeAgentOverA2AHandle {\n /** Origin of the running server, e.g. `\"http://localhost:4000\"`. */\n url: string;\n /** Resolved port number (useful when you let the OS pick one). */\n port: number;\n /** Stop the HTTP server, end every cached MANTYX session, and free MCP transports. */\n close: () => Promise<void>;\n}\n\n// --------------------------------------------------------- Implementation\n\n/**\n * Implementation of `@a2a-js/sdk`'s `AgentExecutor` that backs a MANTYX agent.\n *\n * Most callers want `serveAgentOverA2A` instead; reach for this class directly\n * when you need to mount the executor inside an existing Express, Fastify, or\n * Connect app.\n */\nexport class MantyxAgentExecutor implements AgentExecutor {\n readonly client: MantyxClient;\n readonly agent: MantyxAgentSpec;\n readonly conversation: \"auto\" | \"stateless\";\n readonly maxSessions: number;\n readonly onAssistantDelta?: MantyxAgentExecutorOptions[\"onAssistantDelta\"];\n\n /** contextId -> live MANTYX session. Maintained as an LRU map. */\n private readonly sessions = new Map<string, AgentSession>();\n /** taskIds we've been asked to cancel; checked between turns. */\n private readonly cancelled = new Set<string>();\n /** Pending AbortControllers per task, used for cooperative cancel. */\n private readonly inFlight = new Map<string, AbortController>();\n\n constructor(options: MantyxAgentExecutorOptions) {\n if (!options.client) {\n throw new MantyxError(\"MantyxAgentExecutor: `client` is required\");\n }\n validateAgentSpec(options.agent);\n this.client = options.client;\n this.agent = options.agent;\n this.conversation = options.conversation ?? \"auto\";\n this.maxSessions = options.maxSessions ?? 1024;\n if (options.onAssistantDelta) this.onAssistantDelta = options.onAssistantDelta;\n }\n\n async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise<void> {\n const { userMessage, taskId, contextId, task } = requestContext;\n const userText = extractText(userMessage);\n\n const abort = new AbortController();\n this.inFlight.set(taskId, abort);\n\n try {\n // Publish initial Task object on the first turn so streaming clients see\n // a stable id; reusing an existing task otherwise.\n if (!task) {\n eventBus.publish({\n kind: \"task\",\n id: taskId,\n contextId,\n status: { state: \"submitted\", timestamp: new Date().toISOString() },\n history: [userMessage],\n } satisfies Task);\n }\n\n eventBus.publish(statusUpdate(taskId, contextId, \"working\", false));\n\n if (this.cancelled.has(taskId)) {\n eventBus.publish(statusUpdate(taskId, contextId, \"canceled\", true));\n eventBus.finished();\n return;\n }\n\n const onDelta = (delta: string) => {\n if (this.onAssistantDelta) {\n this.onAssistantDelta(delta, requestContext, eventBus);\n return;\n }\n eventBus.publish(deltaStatusUpdate(taskId, contextId, delta));\n };\n\n let result: RunResult;\n try {\n result = await this.runOnce(contextId, userText, onDelta, abort.signal);\n } catch (err) {\n eventBus.publish(\n completedStatusUpdate(\n taskId,\n contextId,\n this.cancelled.has(taskId) ? \"canceled\" : \"failed\",\n errorText(err),\n ),\n );\n eventBus.finished();\n return;\n }\n\n eventBus.publish(completedStatusUpdate(taskId, contextId, \"completed\", result.text ?? \"\"));\n eventBus.finished();\n } finally {\n this.inFlight.delete(taskId);\n this.cancelled.delete(taskId);\n }\n }\n\n async cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise<void> {\n this.cancelled.add(taskId);\n const ctrl = this.inFlight.get(taskId);\n if (ctrl) ctrl.abort();\n // The active `execute()` call publishes the final 'canceled' status\n // itself; we only need to mark the intent here.\n void eventBus;\n }\n\n /**\n * Close every cached session. Idempotent. Safe to call from server shutdown\n * paths.\n */\n async close(): Promise<void> {\n const sessions = Array.from(this.sessions.values());\n this.sessions.clear();\n await Promise.allSettled(sessions.map((s) => s.end()));\n }\n\n // -------------------------------------------------- private session helpers\n\n private async runOnce(\n contextId: string,\n prompt: string,\n onAssistantDelta: (delta: string) => void,\n signal: AbortSignal,\n ): Promise<RunResult> {\n if (this.conversation === \"stateless\") {\n const runSpec: RunSpec = {\n ...specForRun(this.agent),\n prompt,\n onAssistantDelta,\n signal,\n };\n return this.client.runAgent(runSpec);\n }\n\n const session = await this.getOrCreateSession(contextId);\n return session.send(prompt, { onAssistantDelta, signal });\n }\n\n private async getOrCreateSession(contextId: string): Promise<AgentSession> {\n const existing = this.sessions.get(contextId);\n if (existing) {\n // LRU: bump to most-recently-used.\n this.sessions.delete(contextId);\n this.sessions.set(contextId, existing);\n return existing;\n }\n const sessionSpec: SessionSpec = specForSession(this.agent, contextId);\n const session = await this.client.createSession(sessionSpec);\n this.sessions.set(contextId, session);\n await this.evictIfNeeded();\n return session;\n }\n\n private async evictIfNeeded(): Promise<void> {\n while (this.sessions.size > this.maxSessions) {\n const oldestKey = this.sessions.keys().next().value as string | undefined;\n if (!oldestKey) break;\n const oldest = this.sessions.get(oldestKey)!;\n this.sessions.delete(oldestKey);\n try {\n await oldest.end();\n } catch {\n // Eviction is best-effort; swallow errors so the next request still works.\n }\n }\n }\n}\n\n/**\n * Spin up a small HTTP server that exposes a MANTYX agent as an A2A peer.\n * Mounts the Agent Card, JSON-RPC, and (optionally) REST endpoints from the\n * official `@a2a-js/sdk` library.\n *\n * Throws if `express` / `@a2a-js/sdk` aren't installed; install them as peer\n * deps with `npm install express @a2a-js/sdk`.\n */\nexport async function serveAgentOverA2A(\n options: ServeAgentOverA2AOptions,\n): Promise<ServeAgentOverA2AHandle> {\n const a2a = await loadServerSdk();\n const expressMod = await loadExpress();\n\n const executor = new MantyxAgentExecutor(options);\n const requestHandler = new a2a.DefaultRequestHandler(\n options.agentCard,\n new a2a.InMemoryTaskStore(),\n executor,\n );\n\n const app = expressMod();\n app.use(expressMod.json());\n\n const cardPath = options.agentCardPath ?? \"/.well-known/agent-card.json\";\n const jsonRpcPath = options.jsonRpcPath ?? \"/\";\n const restPath = options.restPath === undefined ? \"/v1\" : options.restPath;\n\n app.use(\n cardPath,\n a2a.expressApp.agentCardHandler({ agentCardProvider: requestHandler }),\n );\n if (restPath !== false) {\n app.use(\n restPath,\n a2a.expressApp.restHandler({\n requestHandler,\n userBuilder: a2a.expressApp.UserBuilder.noAuthentication,\n }),\n );\n }\n // Mount JSON-RPC last so it doesn't shadow the well-known and REST paths.\n app.use(\n jsonRpcPath,\n a2a.expressApp.jsonRpcHandler({\n requestHandler,\n userBuilder: a2a.expressApp.UserBuilder.noAuthentication,\n }),\n );\n\n const port = options.port ?? 0;\n const host = options.host ?? \"0.0.0.0\";\n const server = app.listen(port, host);\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"listening\", resolve);\n server.once(\"error\", reject);\n });\n\n const address = server.address();\n if (!address || typeof address === \"string\") {\n server.close();\n throw new MantyxError(\"serveAgentOverA2A: failed to bind HTTP listener\");\n }\n\n return {\n port: address.port,\n url: `http://${displayHost(host)}:${address.port}`,\n close: async () => {\n await new Promise<void>((resolve, reject) =>\n server.close((err) => (err ? reject(err) : resolve())),\n );\n await executor.close();\n },\n };\n}\n\n// ----------------------------------------------------------- A2A event helpers\n\nfunction statusUpdate(\n taskId: string,\n contextId: string,\n state: \"submitted\" | \"working\" | \"completed\" | \"canceled\" | \"failed\",\n final: boolean,\n): TaskStatusUpdateEvent {\n return {\n kind: \"status-update\",\n taskId,\n contextId,\n status: { state, timestamp: new Date().toISOString() },\n final,\n };\n}\n\nfunction deltaStatusUpdate(\n taskId: string,\n contextId: string,\n delta: string,\n): TaskStatusUpdateEvent {\n return {\n kind: \"status-update\",\n taskId,\n contextId,\n status: {\n state: \"working\",\n timestamp: new Date().toISOString(),\n message: {\n kind: \"message\",\n messageId: randomMessageId(),\n role: \"agent\",\n parts: [{ kind: \"text\", text: delta }],\n contextId,\n taskId,\n },\n },\n final: false,\n };\n}\n\nfunction completedStatusUpdate(\n taskId: string,\n contextId: string,\n state: \"completed\" | \"canceled\" | \"failed\",\n text: string,\n): TaskStatusUpdateEvent {\n return {\n kind: \"status-update\",\n taskId,\n contextId,\n status: {\n state,\n timestamp: new Date().toISOString(),\n message: {\n kind: \"message\",\n messageId: randomMessageId(),\n role: \"agent\",\n parts: [{ kind: \"text\", text }],\n contextId,\n taskId,\n },\n },\n final: true,\n };\n}\n\n// --------------------------------------------------------- Utility helpers\n\nfunction extractText(message: Message | undefined): string {\n if (!message) return \"\";\n const parts = (message.parts as Part[] | undefined) ?? [];\n const out: string[] = [];\n for (const p of parts) {\n if ((p as { kind: string }).kind === \"text\") {\n const t = (p as { text?: unknown }).text;\n if (typeof t === \"string\") out.push(t);\n }\n }\n return out.join(\"\\n\");\n}\n\nfunction specForRun(spec: MantyxAgentSpec): RunSpec {\n const out: RunSpec = {};\n if (spec.agentId) out.agentId = spec.agentId;\n if (spec.systemPrompt) out.systemPrompt = spec.systemPrompt;\n if (spec.modelId) out.modelId = spec.modelId;\n if (spec.tools) out.tools = spec.tools;\n if (spec.reasoningLevel !== undefined) out.reasoningLevel = spec.reasoningLevel;\n if (spec.metadata) out.metadata = spec.metadata;\n if (spec.budgets) out.budgets = spec.budgets;\n if (spec.name) out.name = spec.name;\n return out;\n}\n\nfunction specForSession(spec: MantyxAgentSpec, contextId: string): SessionSpec {\n const out: SessionSpec = {};\n if (spec.agentId) out.agentId = spec.agentId;\n if (spec.systemPrompt) out.systemPrompt = spec.systemPrompt;\n if (spec.modelId) out.modelId = spec.modelId;\n if (spec.tools) out.tools = spec.tools;\n if (spec.reasoningLevel !== undefined) out.reasoningLevel = spec.reasoningLevel;\n // Tag the session with the originating A2A contextId so it's filterable\n // in the MANTYX dashboard.\n const meta: Record<string, string> = { ...(spec.metadata ?? {}) };\n if (!meta.a2a_context_id) meta.a2a_context_id = contextId;\n out.metadata = meta;\n if (spec.budgets) out.budgets = spec.budgets;\n if (spec.name) out.name = spec.name;\n return out;\n}\n\nfunction validateAgentSpec(spec: MantyxAgentSpec): void {\n if (!spec.agentId && (!spec.systemPrompt || spec.systemPrompt.length === 0)) {\n throw new MantyxError(\n \"MantyxAgentExecutor: `agent.agentId` or `agent.systemPrompt` is required\",\n );\n }\n}\n\nfunction errorText(err: unknown): string {\n if (err instanceof MantyxRunError) {\n return `MANTYX run failed (${err.subtype ?? \"unknown\"}): ${err.message}`;\n }\n if (err instanceof Error) return err.message;\n try {\n return String(err);\n } catch {\n return \"unknown error\";\n }\n}\n\nfunction randomMessageId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n // Fallback: timestamp + random suffix; A2A only requires uniqueness.\n return `msg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction displayHost(host: string): string {\n if (host === \"0.0.0.0\" || host === \"::\") return \"localhost\";\n return host;\n}\n\n// --------------------------------------------------- Optional-dep loaders\n\ninterface ExpressLoader {\n (): import(\"express\").Express;\n json(): import(\"express\").RequestHandler;\n}\n\ninterface A2AServerSdk {\n DefaultRequestHandler: typeof import(\"@a2a-js/sdk/server\").DefaultRequestHandler;\n InMemoryTaskStore: typeof import(\"@a2a-js/sdk/server\").InMemoryTaskStore;\n expressApp: typeof import(\"@a2a-js/sdk/server/express\");\n}\n\nasync function loadExpress(): Promise<ExpressLoader> {\n try {\n const mod = (await import(\"express\")) as unknown as\n | ExpressLoader\n | { default: ExpressLoader };\n return \"default\" in mod ? mod.default : mod;\n } catch (err) {\n throw new MantyxError(\n \"serveAgentOverA2A: `express` is required but not installed. Run `npm install express @a2a-js/sdk` to enable the A2A server.\",\n );\n }\n}\n\nasync function loadServerSdk(): Promise<A2AServerSdk> {\n let server: typeof import(\"@a2a-js/sdk/server\");\n let express: typeof import(\"@a2a-js/sdk/server/express\");\n try {\n server = (await import(\"@a2a-js/sdk/server\")) as typeof import(\"@a2a-js/sdk/server\");\n } catch (err) {\n throw new MantyxError(\n \"serveAgentOverA2A: `@a2a-js/sdk` is required but not installed. Run `npm install @a2a-js/sdk express` to enable the A2A server.\",\n );\n }\n try {\n express = (await import(\n \"@a2a-js/sdk/server/express\"\n )) as typeof import(\"@a2a-js/sdk/server/express\");\n } catch (err) {\n throw new MantyxError(\n \"serveAgentOverA2A: `@a2a-js/sdk/server/express` could not be loaded; ensure the installed `@a2a-js/sdk` is at least v0.3.\",\n );\n }\n return {\n DefaultRequestHandler: server.DefaultRequestHandler,\n InMemoryTaskStore: server.InMemoryTaskStore,\n expressApp: express,\n };\n}\n\n// Re-export for callers that just want to compose the executor with their own\n// transport stack (e.g. plug it into Fastify or Cloudflare Workers).\nexport type {\n AgentCard,\n Message,\n MessageSendParams,\n Task,\n TaskArtifactUpdateEvent,\n TaskStatusUpdateEvent,\n} from \"@a2a-js/sdk\";\nexport type { AgentExecutor, ExecutionEventBus, RequestContext } from \"@a2a-js/sdk/server\";\n"],"mappings":"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","names":[]}
|