agenthub-multiagent-mcp 1.1.2 → 1.1.4
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/README.md +170 -151
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +2 -1
- package/dist/client.js.map +1 -1
- package/dist/client.test.d.ts +2 -0
- package/dist/client.test.d.ts.map +1 -1
- package/dist/client.test.js +16 -1
- package/dist/client.test.js.map +1 -1
- package/dist/e2e.test.d.ts +1 -0
- package/dist/e2e.test.d.ts.map +1 -1
- package/dist/e2e.test.js +3 -1
- package/dist/e2e.test.js.map +1 -1
- package/dist/heartbeat.d.ts +11 -1
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +41 -3
- package/dist/heartbeat.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +13 -8
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tools.test.js +6 -1
- package/dist/tools/tools.test.js.map +1 -1
- package/package.json +53 -53
- package/src/client.test.ts +223 -208
- package/src/client.ts +293 -286
- package/src/e2e.test.ts +300 -298
- package/src/heartbeat.ts +45 -3
- package/src/index.ts +123 -123
- package/src/tools/index.ts +672 -666
- package/src/tools/tools.test.ts +522 -517
- package/vitest.config.ts +8 -0
package/src/client.ts
CHANGED
|
@@ -1,286 +1,293 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP client for AgentHub API
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface Agent {
|
|
6
|
-
id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
owner: string;
|
|
9
|
-
model: string;
|
|
10
|
-
model_provider: string;
|
|
11
|
-
status: "online" | "busy" | "offline";
|
|
12
|
-
working_dir: string;
|
|
13
|
-
current_task: string;
|
|
14
|
-
task_started_at?: string;
|
|
15
|
-
channels: string[];
|
|
16
|
-
registered_at: string;
|
|
17
|
-
last_heartbeat: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface PendingTask {
|
|
21
|
-
id: string;
|
|
22
|
-
assigned_to: string;
|
|
23
|
-
assigned_by: string;
|
|
24
|
-
assigned_by_name?: string;
|
|
25
|
-
task: string;
|
|
26
|
-
priority: string;
|
|
27
|
-
status: string;
|
|
28
|
-
created_at: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface TaskCompletionResponse {
|
|
32
|
-
task_id: string;
|
|
33
|
-
elapsed_time: string;
|
|
34
|
-
slack_posted: boolean;
|
|
35
|
-
agents_notified: number;
|
|
36
|
-
pending_tasks_count: number;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface Message {
|
|
40
|
-
id: string;
|
|
41
|
-
from_agent: string;
|
|
42
|
-
to_agent?: string;
|
|
43
|
-
to_channel?: string;
|
|
44
|
-
broadcast: boolean;
|
|
45
|
-
type: "task" | "status" | "question" | "response" | "context";
|
|
46
|
-
subject: string;
|
|
47
|
-
body: string;
|
|
48
|
-
reply_to?: string;
|
|
49
|
-
status: "pending" | "delivered" | "read" | "responded";
|
|
50
|
-
priority: "normal" | "high" | "urgent";
|
|
51
|
-
created_at: string;
|
|
52
|
-
delivered_at?: string;
|
|
53
|
-
read_at?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface Channel {
|
|
57
|
-
name: string;
|
|
58
|
-
description: string;
|
|
59
|
-
members: string[];
|
|
60
|
-
member_count: number;
|
|
61
|
-
created_at: string;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export class ApiClient {
|
|
65
|
-
private baseUrl: string;
|
|
66
|
-
private apiKey: string;
|
|
67
|
-
|
|
68
|
-
constructor(baseUrl: string, apiKey: string) {
|
|
69
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
70
|
-
this.apiKey = apiKey;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
private async request<T>(
|
|
74
|
-
method: string,
|
|
75
|
-
path: string,
|
|
76
|
-
body?: unknown
|
|
77
|
-
): Promise<T> {
|
|
78
|
-
const url = `${this.baseUrl}${path}`;
|
|
79
|
-
|
|
80
|
-
const response = await fetch(url, {
|
|
81
|
-
method,
|
|
82
|
-
headers: {
|
|
83
|
-
"Content-Type": "application/json",
|
|
84
|
-
"X-API-Key": this.apiKey,
|
|
85
|
-
},
|
|
86
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const data = await response.json() as Record<string, unknown>;
|
|
90
|
-
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
const message = typeof data.message === 'string' ? data.message : `Request failed: ${response.status}`;
|
|
93
|
-
throw new Error(message);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return data as T;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Agent methods
|
|
100
|
-
async registerAgent(
|
|
101
|
-
id: string,
|
|
102
|
-
name?: string,
|
|
103
|
-
owner?: string,
|
|
104
|
-
workingDir?: string,
|
|
105
|
-
model?: string
|
|
106
|
-
): Promise<{
|
|
107
|
-
agent_id: string;
|
|
108
|
-
token: string;
|
|
109
|
-
model: string;
|
|
110
|
-
model_provider: string;
|
|
111
|
-
registered_at: string;
|
|
112
|
-
slack_notified: boolean;
|
|
113
|
-
pending_tasks_count: number;
|
|
114
|
-
unread_messages_count: number;
|
|
115
|
-
}> {
|
|
116
|
-
return this.request("POST", "/agents/register", {
|
|
117
|
-
id,
|
|
118
|
-
name,
|
|
119
|
-
owner,
|
|
120
|
-
working_dir: workingDir,
|
|
121
|
-
model,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async reconnectAgent(
|
|
126
|
-
agentId: string,
|
|
127
|
-
token: string,
|
|
128
|
-
owner?: string
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
): Promise<{
|
|
235
|
-
return this.request("
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
agentId: string
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
): Promise<{
|
|
271
|
-
return this.request("
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for AgentHub API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Agent {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
owner: string;
|
|
9
|
+
model: string;
|
|
10
|
+
model_provider: string;
|
|
11
|
+
status: "online" | "busy" | "offline";
|
|
12
|
+
working_dir: string;
|
|
13
|
+
current_task: string;
|
|
14
|
+
task_started_at?: string;
|
|
15
|
+
channels: string[];
|
|
16
|
+
registered_at: string;
|
|
17
|
+
last_heartbeat: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PendingTask {
|
|
21
|
+
id: string;
|
|
22
|
+
assigned_to: string;
|
|
23
|
+
assigned_by: string;
|
|
24
|
+
assigned_by_name?: string;
|
|
25
|
+
task: string;
|
|
26
|
+
priority: string;
|
|
27
|
+
status: string;
|
|
28
|
+
created_at: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface TaskCompletionResponse {
|
|
32
|
+
task_id: string;
|
|
33
|
+
elapsed_time: string;
|
|
34
|
+
slack_posted: boolean;
|
|
35
|
+
agents_notified: number;
|
|
36
|
+
pending_tasks_count: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Message {
|
|
40
|
+
id: string;
|
|
41
|
+
from_agent: string;
|
|
42
|
+
to_agent?: string;
|
|
43
|
+
to_channel?: string;
|
|
44
|
+
broadcast: boolean;
|
|
45
|
+
type: "task" | "status" | "question" | "response" | "context";
|
|
46
|
+
subject: string;
|
|
47
|
+
body: string;
|
|
48
|
+
reply_to?: string;
|
|
49
|
+
status: "pending" | "delivered" | "read" | "responded";
|
|
50
|
+
priority: "normal" | "high" | "urgent";
|
|
51
|
+
created_at: string;
|
|
52
|
+
delivered_at?: string;
|
|
53
|
+
read_at?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Channel {
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
members: string[];
|
|
60
|
+
member_count: number;
|
|
61
|
+
created_at: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class ApiClient {
|
|
65
|
+
private baseUrl: string;
|
|
66
|
+
private apiKey: string;
|
|
67
|
+
|
|
68
|
+
constructor(baseUrl: string, apiKey: string) {
|
|
69
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
70
|
+
this.apiKey = apiKey;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async request<T>(
|
|
74
|
+
method: string,
|
|
75
|
+
path: string,
|
|
76
|
+
body?: unknown
|
|
77
|
+
): Promise<T> {
|
|
78
|
+
const url = `${this.baseUrl}${path}`;
|
|
79
|
+
|
|
80
|
+
const response = await fetch(url, {
|
|
81
|
+
method,
|
|
82
|
+
headers: {
|
|
83
|
+
"Content-Type": "application/json",
|
|
84
|
+
"X-API-Key": this.apiKey,
|
|
85
|
+
},
|
|
86
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const data = await response.json() as Record<string, unknown>;
|
|
90
|
+
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
const message = typeof data.message === 'string' ? data.message : `Request failed: ${response.status}`;
|
|
93
|
+
throw new Error(message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return data as T;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Agent methods
|
|
100
|
+
async registerAgent(
|
|
101
|
+
id: string,
|
|
102
|
+
name?: string,
|
|
103
|
+
owner?: string,
|
|
104
|
+
workingDir?: string,
|
|
105
|
+
model?: string
|
|
106
|
+
): Promise<{
|
|
107
|
+
agent_id: string;
|
|
108
|
+
token: string;
|
|
109
|
+
model: string;
|
|
110
|
+
model_provider: string;
|
|
111
|
+
registered_at: string;
|
|
112
|
+
slack_notified: boolean;
|
|
113
|
+
pending_tasks_count: number;
|
|
114
|
+
unread_messages_count: number;
|
|
115
|
+
}> {
|
|
116
|
+
return this.request("POST", "/agents/register", {
|
|
117
|
+
id,
|
|
118
|
+
name,
|
|
119
|
+
owner,
|
|
120
|
+
working_dir: workingDir,
|
|
121
|
+
model,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async reconnectAgent(
|
|
126
|
+
agentId: string,
|
|
127
|
+
token: string,
|
|
128
|
+
owner?: string,
|
|
129
|
+
model?: string
|
|
130
|
+
): Promise<{
|
|
131
|
+
reconnected: boolean;
|
|
132
|
+
agent_id: string;
|
|
133
|
+
was_offline: boolean;
|
|
134
|
+
pending_tasks_count: number;
|
|
135
|
+
unread_messages_count: number;
|
|
136
|
+
slack_notified: boolean;
|
|
137
|
+
}> {
|
|
138
|
+
return this.request("POST", "/agents/reconnect", {
|
|
139
|
+
agent_id: agentId,
|
|
140
|
+
token,
|
|
141
|
+
owner,
|
|
142
|
+
model,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async startWork(
|
|
147
|
+
agentId: string,
|
|
148
|
+
task: string,
|
|
149
|
+
project?: string
|
|
150
|
+
): Promise<{ acknowledged: boolean; slack_posted: boolean }> {
|
|
151
|
+
return this.request("POST", `/agents/${agentId}/start-work`, {
|
|
152
|
+
task,
|
|
153
|
+
project,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async heartbeat(
|
|
158
|
+
agentId: string,
|
|
159
|
+
status?: "online" | "busy"
|
|
160
|
+
): Promise<{
|
|
161
|
+
acknowledged: boolean;
|
|
162
|
+
timestamp: string;
|
|
163
|
+
pending_tasks_count: number;
|
|
164
|
+
unread_messages_count: number;
|
|
165
|
+
}> {
|
|
166
|
+
return this.request("POST", `/agents/${agentId}/heartbeat`, { status });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async disconnect(agentId: string): Promise<{ unregistered: boolean }> {
|
|
170
|
+
return this.request("DELETE", `/agents/${agentId}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async getAgent(agentId: string): Promise<Agent> {
|
|
174
|
+
return this.request("GET", `/agents/${agentId}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async listAgents(status?: string): Promise<{ agents: Agent[]; total: number }> {
|
|
178
|
+
const query = status ? `?status=${status}` : "";
|
|
179
|
+
return this.request("GET", `/agents${query}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Message methods
|
|
183
|
+
async sendMessage(params: {
|
|
184
|
+
from_agent?: string;
|
|
185
|
+
to_agent?: string;
|
|
186
|
+
to_channel?: string;
|
|
187
|
+
broadcast?: boolean;
|
|
188
|
+
type: string;
|
|
189
|
+
subject: string;
|
|
190
|
+
body: string;
|
|
191
|
+
reply_to?: string;
|
|
192
|
+
priority?: string;
|
|
193
|
+
}): Promise<{ message_id: string; status: string; created_at: string }> {
|
|
194
|
+
return this.request("POST", "/messages", params);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getInbox(
|
|
198
|
+
agentId: string,
|
|
199
|
+
unreadOnly?: boolean,
|
|
200
|
+
limit?: number
|
|
201
|
+
): Promise<{ messages: Message[]; total: number }> {
|
|
202
|
+
const params = new URLSearchParams();
|
|
203
|
+
if (unreadOnly) params.set("unread_only", "true");
|
|
204
|
+
if (limit) params.set("limit", String(limit));
|
|
205
|
+
const query = params.toString() ? `?${params}` : "";
|
|
206
|
+
return this.request("GET", `/messages/inbox/${agentId}${query}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async markRead(messageId: string): Promise<{ updated: boolean }> {
|
|
210
|
+
return this.request("PATCH", `/messages/${messageId}/status`, {
|
|
211
|
+
status: "read",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async reply(
|
|
216
|
+
messageId: string,
|
|
217
|
+
fromAgent: string,
|
|
218
|
+
body: string
|
|
219
|
+
): Promise<{ message_id: string }> {
|
|
220
|
+
// Get original message to find recipient
|
|
221
|
+
const original = await this.request<Message>("GET", `/messages/${messageId}`);
|
|
222
|
+
|
|
223
|
+
return this.request("POST", "/messages", {
|
|
224
|
+
from_agent: fromAgent,
|
|
225
|
+
to_agent: original.from_agent,
|
|
226
|
+
type: "response",
|
|
227
|
+
subject: `Re: ${original.subject}`,
|
|
228
|
+
body,
|
|
229
|
+
reply_to: messageId,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Channel methods
|
|
234
|
+
async listChannels(): Promise<{ channels: Channel[]; total: number }> {
|
|
235
|
+
return this.request("GET", "/channels");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async joinChannel(
|
|
239
|
+
channelName: string,
|
|
240
|
+
agentId: string
|
|
241
|
+
): Promise<{ subscribed: boolean }> {
|
|
242
|
+
return this.request("POST", `/channels/${channelName}/subscribe`, {
|
|
243
|
+
agent_id: agentId,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async leaveChannel(
|
|
248
|
+
channelName: string,
|
|
249
|
+
agentId: string
|
|
250
|
+
): Promise<{ unsubscribed: boolean }> {
|
|
251
|
+
return this.request("DELETE", `/channels/${channelName}/unsubscribe/${agentId}`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Task completion methods
|
|
255
|
+
async completeTask(
|
|
256
|
+
agentId: string,
|
|
257
|
+
params: {
|
|
258
|
+
summary: string;
|
|
259
|
+
outcome: "completed" | "blocked" | "partial" | "needs_review" | "handed_off";
|
|
260
|
+
files_changed?: string[];
|
|
261
|
+
time_spent?: string;
|
|
262
|
+
next_steps?: string;
|
|
263
|
+
}
|
|
264
|
+
): Promise<TaskCompletionResponse> {
|
|
265
|
+
return this.request("POST", `/agents/${agentId}/complete-task`, params);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async getPendingTasks(
|
|
269
|
+
agentId: string
|
|
270
|
+
): Promise<{ tasks: PendingTask[]; total: number }> {
|
|
271
|
+
return this.request("GET", `/agents/${agentId}/pending-tasks`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async acceptTask(
|
|
275
|
+
agentId: string,
|
|
276
|
+
taskId: string
|
|
277
|
+
): Promise<{ acknowledged: boolean; task: string; started_at: string }> {
|
|
278
|
+
return this.request("POST", `/agents/${agentId}/accept-task`, {
|
|
279
|
+
task_id: taskId,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async declineTask(
|
|
284
|
+
agentId: string,
|
|
285
|
+
taskId: string,
|
|
286
|
+
reason: string
|
|
287
|
+
): Promise<{ acknowledged: boolean }> {
|
|
288
|
+
return this.request("POST", `/agents/${agentId}/decline-task`, {
|
|
289
|
+
task_id: taskId,
|
|
290
|
+
reason,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|