eacn3 0.1.5 → 0.3.1
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/dist/index.d.ts +2 -2
- package/dist/index.js +180 -108
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +129 -78
- package/dist/server.js.map +1 -1
- package/dist/src/models.d.ts +217 -7
- package/dist/src/models.js +4 -4
- package/dist/src/models.js.map +1 -1
- package/dist/src/network-client.d.ts +18 -2
- package/dist/src/network-client.js +74 -2
- package/dist/src/network-client.js.map +1 -1
- package/dist/src/state.d.ts +1 -1
- package/dist/src/state.js +4 -4
- package/dist/src/state.js.map +1 -1
- package/dist/src/ws-manager.d.ts +1 -1
- package/dist/src/ws-manager.js +1 -1
- package/openclaw.plugin.json +4 -4
- package/package.json +2 -2
- package/scripts/cli.cjs +287 -11
- package/scripts/postinstall.cjs +9 -3
- package/skills/{eacn-adjudicate → eacn3-adjudicate}/SKILL.md +11 -11
- package/skills/eacn3-adjudicate-zh/SKILL.md +106 -0
- package/skills/{eacn-bid → eacn3-bid}/SKILL.md +13 -13
- package/skills/eacn3-bid-zh/SKILL.md +108 -0
- package/skills/{eacn-bounty → eacn3-bounty}/SKILL.md +19 -19
- package/skills/eacn3-bounty-zh/SKILL.md +98 -0
- package/skills/{eacn-browse → eacn3-browse}/SKILL.md +14 -14
- package/skills/eacn3-browse-zh/SKILL.md +76 -0
- package/skills/{eacn-budget → eacn3-budget}/SKILL.md +13 -13
- package/skills/eacn3-budget-zh/SKILL.md +95 -0
- package/skills/{eacn-clarify → eacn3-clarify}/SKILL.md +7 -7
- package/skills/eacn3-clarify-zh/SKILL.md +56 -0
- package/skills/{eacn-collect → eacn3-collect}/SKILL.md +5 -5
- package/skills/eacn3-collect-zh/SKILL.md +77 -0
- package/skills/{eacn-dashboard → eacn3-dashboard}/SKILL.md +21 -21
- package/skills/eacn3-dashboard-zh/SKILL.md +103 -0
- package/skills/{eacn-delegate → eacn3-delegate}/SKILL.md +20 -20
- package/skills/eacn3-delegate-zh/SKILL.md +136 -0
- package/skills/{eacn-execute → eacn3-execute}/SKILL.md +16 -16
- package/skills/eacn3-execute-zh/SKILL.md +147 -0
- package/skills/eacn3-join/SKILL.md +54 -0
- package/skills/eacn3-join-zh/SKILL.md +54 -0
- package/skills/{eacn-leave → eacn3-leave}/SKILL.md +8 -8
- package/skills/eacn3-leave-zh/SKILL.md +49 -0
- package/skills/{eacn-register → eacn3-register}/SKILL.md +21 -21
- package/skills/eacn3-register-zh/SKILL.md +140 -0
- package/skills/{eacn-task → eacn3-task}/SKILL.md +19 -19
- package/skills/eacn3-task-zh/SKILL.md +139 -0
- package/skills/eacn-join/SKILL.md +0 -54
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* EACN3 — Native OpenClaw plugin entry point.
|
|
3
3
|
*
|
|
4
|
-
* Registers the same
|
|
4
|
+
* Registers the same 34 tools as server.ts but via api.registerTool().
|
|
5
5
|
* All logic delegates to the same src/ modules.
|
|
6
6
|
*/
|
|
7
7
|
export default function (api: any): void;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* EACN3 — Native OpenClaw plugin entry point.
|
|
3
3
|
*
|
|
4
|
-
* Registers the same
|
|
4
|
+
* Registers the same 34 tools as server.ts but via api.registerTool().
|
|
5
5
|
* All logic delegates to the same src/ modules.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { EACN3_DEFAULT_NETWORK_ENDPOINT } from "./src/models.js";
|
|
8
8
|
import * as state from "./src/state.js";
|
|
9
9
|
import * as net from "./src/network-client.js";
|
|
10
10
|
import * as ws from "./src/ws-manager.js";
|
|
@@ -37,9 +37,25 @@ function ok(data) {
|
|
|
37
37
|
function err(message) {
|
|
38
38
|
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }] };
|
|
39
39
|
}
|
|
40
|
+
/** Wrap a tool execute function with logging for traceability. */
|
|
41
|
+
function withLogging(toolName, fn) {
|
|
42
|
+
return async (_id, params) => {
|
|
43
|
+
const ts = new Date().toISOString();
|
|
44
|
+
console.error(`[MCP] ${ts} CALL ${toolName} id=${_id} params=${JSON.stringify(params)}`);
|
|
45
|
+
try {
|
|
46
|
+
const result = await fn(_id, params);
|
|
47
|
+
console.error(`[MCP] ${ts} OK ${toolName} id=${_id}`);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
console.error(`[MCP] ${ts} ERR ${toolName} id=${_id} error=${e.message}`);
|
|
52
|
+
throw e;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
40
56
|
/**
|
|
41
57
|
* Resolve agent ID: use provided value, or auto-inject from state.
|
|
42
|
-
* Per agent.md:116 — "agent_id
|
|
58
|
+
* Per agent.md:116 — "agent_id is auto-filled by the communication layer; agents need not provide it"
|
|
43
59
|
*/
|
|
44
60
|
function resolveAgentId(provided) {
|
|
45
61
|
if (provided)
|
|
@@ -48,7 +64,7 @@ function resolveAgentId(provided) {
|
|
|
48
64
|
if (agents.length === 1)
|
|
49
65
|
return agents[0].agent_id;
|
|
50
66
|
if (agents.length === 0)
|
|
51
|
-
throw new Error("No agents registered. Call
|
|
67
|
+
throw new Error("No agents registered. Call eacn3_register_agent first.");
|
|
52
68
|
throw new Error(`Multiple agents registered (${agents.map(a => a.agent_id).join(", ")}). Specify agent_id explicitly.`);
|
|
53
69
|
}
|
|
54
70
|
// ---------------------------------------------------------------------------
|
|
@@ -119,35 +135,91 @@ export default function (api) {
|
|
|
119
135
|
state.load();
|
|
120
136
|
registerEventCallbacks();
|
|
121
137
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
138
|
+
// Health / Cluster (2)
|
|
139
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
140
|
+
// #0a eacn3_health
|
|
141
|
+
api.registerTool({
|
|
142
|
+
name: "eacn3_health",
|
|
143
|
+
description: "Check if a network node is alive and responding. No prerequisites — works before eacn3_connect. Returns {status: 'ok'} on success. Use this to verify an endpoint before connecting.",
|
|
144
|
+
parameters: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
endpoint: { type: "string", description: "Node URL to probe. Defaults to configured network endpoint." },
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
async execute(_id, params) {
|
|
151
|
+
const target = params.endpoint ?? state.getState().network_endpoint;
|
|
152
|
+
try {
|
|
153
|
+
const health = await net.checkHealth(target);
|
|
154
|
+
return ok({ endpoint: target, ...health });
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
return err(`Health check failed for ${target}: ${e.message}`);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
// #0b eacn3_cluster_status
|
|
162
|
+
api.registerTool({
|
|
163
|
+
name: "eacn3_cluster_status",
|
|
164
|
+
description: "Retrieve the full cluster topology including all member nodes, their online/offline status, and seed URLs. No prerequisites — works before eacn3_connect. Returns array of node objects with status and endpoint fields. Useful for diagnostics and finding alternative endpoints if primary is down.",
|
|
165
|
+
parameters: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
endpoint: { type: "string", description: "Node URL to query. Defaults to configured network endpoint." },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
async execute(_id, params) {
|
|
172
|
+
const target = params.endpoint ?? state.getState().network_endpoint;
|
|
173
|
+
try {
|
|
174
|
+
const cluster = await net.getClusterStatus(target);
|
|
175
|
+
return ok(cluster);
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
return err(`Cluster status failed for ${target}: ${e.message}`);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
122
183
|
// Server Management (4)
|
|
123
184
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
124
|
-
// #1
|
|
185
|
+
// #1 eacn3_connect
|
|
125
186
|
api.registerTool({
|
|
126
|
-
name: "
|
|
127
|
-
description: "Connect to
|
|
187
|
+
name: "eacn3_connect",
|
|
188
|
+
description: "Connect to the EACN3 network — this must be your FIRST call. Health-probes the endpoint, falls back to seed nodes if unreachable, registers a server, and starts a background heartbeat every 60s. Returns {server_id, network_endpoint, fallback, agents_online}. Side effects: opens WebSocket connections for any previously registered agents. Call eacn3_register_agent next.",
|
|
128
189
|
parameters: {
|
|
129
190
|
type: "object",
|
|
130
191
|
properties: {
|
|
131
|
-
network_endpoint: { type: "string", description: `Network URL. Defaults to ${
|
|
192
|
+
network_endpoint: { type: "string", description: `Network URL. Defaults to ${EACN3_DEFAULT_NETWORK_ENDPOINT}` },
|
|
193
|
+
seed_nodes: { type: "array", items: { type: "string" }, description: "Additional seed node URLs for fallback" },
|
|
132
194
|
},
|
|
133
195
|
},
|
|
134
196
|
async execute(_id, params) {
|
|
135
|
-
const
|
|
197
|
+
const preferred = params.network_endpoint ?? EACN3_DEFAULT_NETWORK_ENDPOINT;
|
|
136
198
|
const s = state.getState();
|
|
199
|
+
// Health probe + fallback
|
|
200
|
+
let endpoint;
|
|
201
|
+
let fallback = false;
|
|
202
|
+
try {
|
|
203
|
+
endpoint = await net.findHealthyEndpoint(preferred, params.seed_nodes);
|
|
204
|
+
fallback = endpoint !== preferred;
|
|
205
|
+
}
|
|
206
|
+
catch (e) {
|
|
207
|
+
return err(`Cannot reach any network node: ${e.message}`);
|
|
208
|
+
}
|
|
137
209
|
s.network_endpoint = endpoint;
|
|
138
|
-
const res = await net.registerServer("0.
|
|
139
|
-
s.server_card = { server_id: res.server_id, version: "0.
|
|
210
|
+
const res = await net.registerServer("0.3.0", "plugin://local", "plugin-user");
|
|
211
|
+
s.server_card = { server_id: res.server_id, version: "0.3.0", endpoint: "plugin://local", owner: "plugin-user", status: "online" };
|
|
140
212
|
state.save();
|
|
141
213
|
startHeartbeat();
|
|
142
214
|
for (const agentId of Object.keys(s.agents))
|
|
143
215
|
ws.connect(agentId);
|
|
144
|
-
return ok({ connected: true, server_id: res.server_id, network_endpoint: endpoint, agents_online: Object.keys(s.agents).length });
|
|
216
|
+
return ok({ connected: true, server_id: res.server_id, network_endpoint: endpoint, fallback, agents_online: Object.keys(s.agents).length });
|
|
145
217
|
},
|
|
146
218
|
});
|
|
147
|
-
// #2
|
|
219
|
+
// #2 eacn3_disconnect
|
|
148
220
|
api.registerTool({
|
|
149
|
-
name: "
|
|
150
|
-
description: "Disconnect from
|
|
221
|
+
name: "eacn3_disconnect",
|
|
222
|
+
description: "Disconnect from the EACN3 network, unregister the server, and close all WebSocket connections. Requires: eacn3_connect first. Side effects: clears all local agent state; active tasks will timeout and hurt reputation. Returns {disconnected: true}. Only call at end of session.",
|
|
151
223
|
parameters: { type: "object", properties: {} },
|
|
152
224
|
async execute() {
|
|
153
225
|
stopHeartbeat();
|
|
@@ -163,17 +235,17 @@ export default function (api) {
|
|
|
163
235
|
return ok({ disconnected: true });
|
|
164
236
|
},
|
|
165
237
|
});
|
|
166
|
-
// #3
|
|
238
|
+
// #3 eacn3_heartbeat
|
|
167
239
|
api.registerTool({
|
|
168
|
-
name: "
|
|
169
|
-
description: "
|
|
240
|
+
name: "eacn3_heartbeat",
|
|
241
|
+
description: "Manually send a heartbeat to the network to signal this server is still alive. Requires: eacn3_connect first. Usually unnecessary — a background interval auto-sends every 60s. Only use if you suspect the connection may have gone stale.",
|
|
170
242
|
parameters: { type: "object", properties: {} },
|
|
171
243
|
async execute() { return ok(await net.heartbeat()); },
|
|
172
244
|
});
|
|
173
|
-
// #4
|
|
245
|
+
// #4 eacn3_server_info
|
|
174
246
|
api.registerTool({
|
|
175
|
-
name: "
|
|
176
|
-
description: "Get current server
|
|
247
|
+
name: "eacn3_server_info",
|
|
248
|
+
description: "Get current server connection state, including server_card, network_endpoint, registered agent IDs, task count, and remote status. Requires: eacn3_connect first. Returns {server_card, network_endpoint, agents_count, agents[], tasks_count, remote_status}. No side effects — read-only diagnostic.",
|
|
177
249
|
parameters: { type: "object", properties: {} },
|
|
178
250
|
async execute() {
|
|
179
251
|
const s = state.getState();
|
|
@@ -192,10 +264,10 @@ export default function (api) {
|
|
|
192
264
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
193
265
|
// Agent Management (7)
|
|
194
266
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
195
|
-
// #5
|
|
267
|
+
// #5 eacn3_register_agent
|
|
196
268
|
api.registerTool({
|
|
197
|
-
name: "
|
|
198
|
-
description: "
|
|
269
|
+
name: "eacn3_register_agent",
|
|
270
|
+
description: "Create and register an agent identity on the EACN3 network. Requires: eacn3_connect first. Assembles an AgentCard, registers it with the network, persists it locally, and opens a WebSocket for real-time event push (task_broadcast, subtask_completed, etc.). Returns {agent_id, seeds, domains}. Domains control which task broadcasts you receive — be specific (e.g. 'python-coding' not 'coding').",
|
|
199
271
|
parameters: {
|
|
200
272
|
type: "object",
|
|
201
273
|
properties: {
|
|
@@ -212,7 +284,7 @@ export default function (api) {
|
|
|
212
284
|
async execute(_id, params) {
|
|
213
285
|
const s = state.getState();
|
|
214
286
|
if (!s.server_card)
|
|
215
|
-
return err("Not connected. Call
|
|
287
|
+
return err("Not connected. Call eacn3_connect first.");
|
|
216
288
|
if (!params.name?.trim())
|
|
217
289
|
return err("name cannot be empty");
|
|
218
290
|
if (!params.domains?.length)
|
|
@@ -230,10 +302,10 @@ export default function (api) {
|
|
|
230
302
|
return ok({ registered: true, agent_id: agentId, seeds: res.seeds, domains: params.domains });
|
|
231
303
|
},
|
|
232
304
|
});
|
|
233
|
-
// #6
|
|
305
|
+
// #6 eacn3_get_agent
|
|
234
306
|
api.registerTool({
|
|
235
|
-
name: "
|
|
236
|
-
description: "
|
|
307
|
+
name: "eacn3_get_agent",
|
|
308
|
+
description: "Fetch the full AgentCard for any agent by ID — checks local state first, then queries the network. Returns {agent_id, name, agent_type, domains, skills, capabilities, url, server_id, description}. No side effects. Use to inspect an agent before sending messages or evaluating bids.",
|
|
237
309
|
parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
|
|
238
310
|
async execute(_id, params) {
|
|
239
311
|
const local = state.getAgent(params.agent_id);
|
|
@@ -242,10 +314,10 @@ export default function (api) {
|
|
|
242
314
|
return ok(await net.getAgentInfo(params.agent_id));
|
|
243
315
|
},
|
|
244
316
|
});
|
|
245
|
-
// #7
|
|
317
|
+
// #7 eacn3_update_agent
|
|
246
318
|
api.registerTool({
|
|
247
|
-
name: "
|
|
248
|
-
description: "Update
|
|
319
|
+
name: "eacn3_update_agent",
|
|
320
|
+
description: "Update a registered agent's mutable fields: name, domains, skills, and/or description. Requires: the agent must be registered (eacn3_register_agent). Updates both network and local state. Changing domains affects which task broadcasts you receive going forward.",
|
|
249
321
|
parameters: {
|
|
250
322
|
type: "object",
|
|
251
323
|
properties: {
|
|
@@ -274,10 +346,10 @@ export default function (api) {
|
|
|
274
346
|
return ok({ updated: true, agent_id, ...res });
|
|
275
347
|
},
|
|
276
348
|
});
|
|
277
|
-
// #8
|
|
349
|
+
// #8 eacn3_unregister_agent
|
|
278
350
|
api.registerTool({
|
|
279
|
-
name: "
|
|
280
|
-
description: "
|
|
351
|
+
name: "eacn3_unregister_agent",
|
|
352
|
+
description: "Remove an agent from the network and close its WebSocket connection. Side effects: deletes agent from local state, stops receiving events for this agent. Active tasks assigned to this agent will timeout and hurt reputation. Returns {unregistered: true, agent_id}.",
|
|
281
353
|
parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
|
|
282
354
|
async execute(_id, params) {
|
|
283
355
|
const res = await net.unregisterAgent(params.agent_id);
|
|
@@ -286,29 +358,29 @@ export default function (api) {
|
|
|
286
358
|
return ok({ unregistered: true, agent_id: params.agent_id, ...res });
|
|
287
359
|
},
|
|
288
360
|
});
|
|
289
|
-
// #9
|
|
361
|
+
// #9 eacn3_list_my_agents
|
|
290
362
|
api.registerTool({
|
|
291
|
-
name: "
|
|
292
|
-
description: "List all
|
|
363
|
+
name: "eacn3_list_my_agents",
|
|
364
|
+
description: "List all agents registered on this local server instance. Returns {count, agents[]} where each agent includes agent_id, name, agent_type, domains, and ws_connected (WebSocket status). No network call — reads local state only. Use to check which agents are active and receiving events.",
|
|
293
365
|
parameters: { type: "object", properties: {} },
|
|
294
366
|
async execute() {
|
|
295
367
|
const agents = state.listAgents();
|
|
296
368
|
return ok({ count: agents.length, agents: agents.map((a) => ({ agent_id: a.agent_id, name: a.name, agent_type: a.agent_type, domains: a.domains, ws_connected: ws.isConnected(a.agent_id) })) });
|
|
297
369
|
},
|
|
298
370
|
});
|
|
299
|
-
// #10
|
|
371
|
+
// #10 eacn3_discover_agents
|
|
300
372
|
api.registerTool({
|
|
301
|
-
name: "
|
|
302
|
-
description: "
|
|
373
|
+
name: "eacn3_discover_agents",
|
|
374
|
+
description: "Search for agents matching a specific domain using the network's discovery protocol (Gossip, then DHT, then Bootstrap fallback). Requires: eacn3_connect first. Returns a list of matching AgentCards. Use before creating a task to verify executors exist for your domains.",
|
|
303
375
|
parameters: { type: "object", properties: { domain: { type: "string" }, requester_id: { type: "string" } }, required: ["domain"] },
|
|
304
376
|
async execute(_id, params) {
|
|
305
377
|
return ok(await net.discoverAgents(params.domain, params.requester_id));
|
|
306
378
|
},
|
|
307
379
|
});
|
|
308
|
-
// #11
|
|
380
|
+
// #11 eacn3_list_agents
|
|
309
381
|
api.registerTool({
|
|
310
|
-
name: "
|
|
311
|
-
description: "
|
|
382
|
+
name: "eacn3_list_agents",
|
|
383
|
+
description: "Browse and paginate all agents registered on the network with optional filters by domain or server_id. Returns {count, agents[]}. Default page size is 20. Unlike eacn3_discover_agents, this is a direct registry query without Gossip/DHT discovery — faster but only returns agents already indexed.",
|
|
312
384
|
parameters: {
|
|
313
385
|
type: "object",
|
|
314
386
|
properties: {
|
|
@@ -324,34 +396,34 @@ export default function (api) {
|
|
|
324
396
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
325
397
|
// Task Query (4)
|
|
326
398
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
327
|
-
// #12
|
|
399
|
+
// #12 eacn3_get_task
|
|
328
400
|
api.registerTool({
|
|
329
|
-
name: "
|
|
330
|
-
description: "
|
|
401
|
+
name: "eacn3_get_task",
|
|
402
|
+
description: "Fetch complete task details from the network including description, content, bids[], results[], status, budget, deadline, and domains. No side effects — read-only. Use to inspect a task before bidding or to review submitted results. Works for any task ID regardless of your role.",
|
|
331
403
|
parameters: { type: "object", properties: { task_id: { type: "string" } }, required: ["task_id"] },
|
|
332
404
|
async execute(_id, params) { return ok(await net.getTask(params.task_id)); },
|
|
333
405
|
});
|
|
334
|
-
// #13
|
|
406
|
+
// #13 eacn3_get_task_status
|
|
335
407
|
api.registerTool({
|
|
336
|
-
name: "
|
|
337
|
-
description: "
|
|
408
|
+
name: "eacn3_get_task_status",
|
|
409
|
+
description: "Lightweight task query returning only status and bid list — no result content. Intended for initiators monitoring their tasks. Requires: agent_id must be the task initiator (auto-injected if only one agent registered). Returns {status, bids[]}. Cheaper than eacn3_get_task when you only need status.",
|
|
338
410
|
parameters: { type: "object", properties: { task_id: { type: "string" }, agent_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
|
|
339
411
|
async execute(_id, params) { const agentId = resolveAgentId(params.agent_id); return ok(await net.getTaskStatus(params.task_id, agentId)); },
|
|
340
412
|
});
|
|
341
|
-
// #14
|
|
413
|
+
// #14 eacn3_list_open_tasks
|
|
342
414
|
api.registerTool({
|
|
343
|
-
name: "
|
|
344
|
-
description: "
|
|
415
|
+
name: "eacn3_list_open_tasks",
|
|
416
|
+
description: "Browse tasks currently accepting bids (status: unclaimed or bidding). Returns {count, tasks[]} with pagination. Filter by comma-separated domains to find relevant work. Use this in your main loop to discover tasks to bid on after checking events.",
|
|
345
417
|
parameters: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domain filter" }, limit: { type: "number" }, offset: { type: "number" } } },
|
|
346
418
|
async execute(_id, params) {
|
|
347
419
|
const tasks = await net.getOpenTasks(params);
|
|
348
420
|
return ok({ count: tasks.length, tasks });
|
|
349
421
|
},
|
|
350
422
|
});
|
|
351
|
-
// #15
|
|
423
|
+
// #15 eacn3_list_tasks
|
|
352
424
|
api.registerTool({
|
|
353
|
-
name: "
|
|
354
|
-
description: "
|
|
425
|
+
name: "eacn3_list_tasks",
|
|
426
|
+
description: "Browse all tasks on the network with optional filters by status (unclaimed, bidding, awaiting_retrieval, completed, no_one) and/or initiator_id. Returns {count, tasks[]} with pagination. Unlike eacn3_list_open_tasks, this includes tasks in all states.",
|
|
355
427
|
parameters: { type: "object", properties: { status: { type: "string" }, initiator_id: { type: "string" }, limit: { type: "number" }, offset: { type: "number" } } },
|
|
356
428
|
async execute(_id, params) {
|
|
357
429
|
const tasks = await net.listTasks(params);
|
|
@@ -361,10 +433,10 @@ export default function (api) {
|
|
|
361
433
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
362
434
|
// Task Operations — Initiator (7)
|
|
363
435
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
364
|
-
// #16
|
|
436
|
+
// #16 eacn3_create_task
|
|
365
437
|
api.registerTool({
|
|
366
|
-
name: "
|
|
367
|
-
description: "
|
|
438
|
+
name: "eacn3_create_task",
|
|
439
|
+
description: "Publish a new task to the EACN3 network for other agents to bid on. Side effects: freezes 'budget' credits from your available balance into escrow; broadcasts task to agents with matching domains. Returns {task_id, status, budget, local_matches[]}. Requires: sufficient balance (use eacn3_deposit first if needed). Task starts in 'unclaimed' status, transitions to 'bidding' when first bid arrives.",
|
|
368
440
|
parameters: {
|
|
369
441
|
type: "object",
|
|
370
442
|
properties: {
|
|
@@ -413,55 +485,55 @@ export default function (api) {
|
|
|
413
485
|
return ok({ task_id: taskId, status: task.status, budget: params.budget, local_matches: matchedLocal.map((a) => a.agent_id) });
|
|
414
486
|
},
|
|
415
487
|
});
|
|
416
|
-
// #17
|
|
488
|
+
// #17 eacn3_get_task_results
|
|
417
489
|
api.registerTool({
|
|
418
|
-
name: "
|
|
419
|
-
description: "Retrieve
|
|
490
|
+
name: "eacn3_get_task_results",
|
|
491
|
+
description: "Retrieve submitted results and adjudications for a task you initiated. IMPORTANT side effect: the first call transitions the task from 'awaiting_retrieval' to 'completed' permanently. Returns {results[], adjudications[]}. After reviewing results, call eacn3_select_result to pick a winner and trigger payment.",
|
|
420
492
|
parameters: { type: "object", properties: { task_id: { type: "string" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
|
|
421
493
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.getTaskResults(params.task_id, initiatorId)); },
|
|
422
494
|
});
|
|
423
|
-
// #18
|
|
495
|
+
// #18 eacn3_select_result
|
|
424
496
|
api.registerTool({
|
|
425
|
-
name: "
|
|
426
|
-
description: "
|
|
497
|
+
name: "eacn3_select_result",
|
|
498
|
+
description: "Pick the winning result for a task, triggering credit transfer from escrow to the selected executor agent. Requires: call eacn3_get_task_results first to review results. Side effects: transfers escrowed credits to the winning agent's balance, finalizes the task. The agent_id param is the executor whose result you select, not your own ID.",
|
|
427
499
|
parameters: { type: "object", properties: { task_id: { type: "string" }, agent_id: { type: "string", description: "ID of the agent whose result to select" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id", "agent_id"] },
|
|
428
500
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.selectResult(params.task_id, initiatorId, params.agent_id)); },
|
|
429
501
|
});
|
|
430
|
-
// #19
|
|
502
|
+
// #19 eacn3_close_task
|
|
431
503
|
api.registerTool({
|
|
432
|
-
name: "
|
|
433
|
-
description: "
|
|
504
|
+
name: "eacn3_close_task",
|
|
505
|
+
description: "Stop accepting bids and results for a task you initiated, moving it to closed status. Requires: you must be the task initiator. Side effects: no new bids or results will be accepted; escrowed credits are returned if no result was selected. Returns confirmation with updated task status.",
|
|
434
506
|
parameters: { type: "object", properties: { task_id: { type: "string" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
|
|
435
507
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.closeTask(params.task_id, initiatorId)); },
|
|
436
508
|
});
|
|
437
|
-
// #20
|
|
509
|
+
// #20 eacn3_update_deadline
|
|
438
510
|
api.registerTool({
|
|
439
|
-
name: "
|
|
440
|
-
description: "
|
|
511
|
+
name: "eacn3_update_deadline",
|
|
512
|
+
description: "Extend or shorten a task's deadline. Requires: you must be the task initiator; new_deadline must be an ISO 8601 timestamp in the future. Returns confirmation with updated deadline. Use to give executors more time or to accelerate a slow task.",
|
|
441
513
|
parameters: { type: "object", properties: { task_id: { type: "string" }, new_deadline: { type: "string", description: "New ISO 8601 deadline" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id", "new_deadline"] },
|
|
442
514
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.updateDeadline(params.task_id, initiatorId, params.new_deadline)); },
|
|
443
515
|
});
|
|
444
|
-
// #21
|
|
516
|
+
// #21 eacn3_update_discussions
|
|
445
517
|
api.registerTool({
|
|
446
|
-
name: "
|
|
447
|
-
description: "
|
|
518
|
+
name: "eacn3_update_discussions",
|
|
519
|
+
description: "Post a clarification or discussion message on a task visible to all bidders. Requires: you must be the task initiator. Side effects: triggers a 'discussions_updated' WebSocket event to all bidding agents. Returns confirmation. Use to provide additional context or answer bidder questions.",
|
|
448
520
|
parameters: { type: "object", properties: { task_id: { type: "string" }, message: { type: "string" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id", "message"] },
|
|
449
521
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.updateDiscussions(params.task_id, initiatorId, params.message)); },
|
|
450
522
|
});
|
|
451
|
-
// #22
|
|
523
|
+
// #22 eacn3_confirm_budget
|
|
452
524
|
api.registerTool({
|
|
453
|
-
name: "
|
|
454
|
-
description: "
|
|
525
|
+
name: "eacn3_confirm_budget",
|
|
526
|
+
description: "Approve or reject a bid that exceeded your task's budget, triggered by a 'budget_confirmation' event. Set approved=true to accept (optionally raising the budget with new_budget); approved=false to reject the bid. Side effects: if approved, additional credits are frozen from your balance; the bid transitions from 'pending_confirmation' to 'accepted'. Returns updated task status.",
|
|
455
527
|
parameters: { type: "object", properties: { task_id: { type: "string" }, approved: { type: "boolean" }, new_budget: { type: "number" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id", "approved"] },
|
|
456
528
|
async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.confirmBudget(params.task_id, initiatorId, params.approved, params.new_budget)); },
|
|
457
529
|
});
|
|
458
530
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
459
531
|
// Task Operations — Executor (5)
|
|
460
532
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
461
|
-
// #23
|
|
533
|
+
// #23 eacn3_submit_bid
|
|
462
534
|
api.registerTool({
|
|
463
|
-
name: "
|
|
464
|
-
description: "
|
|
535
|
+
name: "eacn3_submit_bid",
|
|
536
|
+
description: "Bid on an open task by specifying your confidence (0.0-1.0 honest ability estimate) and price in credits. Server evaluates: confidence * reputation must meet threshold or bid is rejected. Returns {status} which is one of: 'executing' (start work now), 'waiting_execution' (queued, slots full), 'rejected' (threshold not met), or 'pending_confirmation' (price > budget, awaiting initiator approval). Side effects: if accepted, tracks task locally as executor role. If price > budget, initiator gets a 'budget_confirmation' event.",
|
|
465
537
|
parameters: { type: "object", properties: { task_id: { type: "string" }, confidence: { type: "number", description: "0.0-1.0 confidence in ability to complete" }, price: { type: "number", description: "Bid price" }, agent_id: { type: "string", description: "Bidder agent ID (auto-injected if omitted)" } }, required: ["task_id", "confidence", "price"] },
|
|
466
538
|
async execute(_id, params) {
|
|
467
539
|
const agentId = resolveAgentId(params.agent_id);
|
|
@@ -472,10 +544,10 @@ export default function (api) {
|
|
|
472
544
|
return ok(res);
|
|
473
545
|
},
|
|
474
546
|
});
|
|
475
|
-
// #24
|
|
547
|
+
// #24 eacn3_submit_result
|
|
476
548
|
api.registerTool({
|
|
477
|
-
name: "
|
|
478
|
-
description: "Submit
|
|
549
|
+
name: "eacn3_submit_result",
|
|
550
|
+
description: "Submit your completed work for a task you are executing. Content should be a JSON object matching the task's expected_output format if specified. Side effects: automatically reports a 'task_completed' reputation event (increases your score); transitions task to 'awaiting_retrieval' so the initiator can review. Returns confirmation with submission status.",
|
|
479
551
|
parameters: { type: "object", properties: { task_id: { type: "string" }, content: { type: "object", description: "Result content object" }, agent_id: { type: "string", description: "Executor agent ID (auto-injected if omitted)" } }, required: ["task_id", "content"] },
|
|
480
552
|
async execute(_id, params) {
|
|
481
553
|
const agentId = resolveAgentId(params.agent_id);
|
|
@@ -487,10 +559,10 @@ export default function (api) {
|
|
|
487
559
|
return ok(res);
|
|
488
560
|
},
|
|
489
561
|
});
|
|
490
|
-
// #25
|
|
562
|
+
// #25 eacn3_reject_task
|
|
491
563
|
api.registerTool({
|
|
492
|
-
name: "
|
|
493
|
-
description: "
|
|
564
|
+
name: "eacn3_reject_task",
|
|
565
|
+
description: "Abandon a task you accepted, freeing your execution slot for another agent. WARNING: automatically reports a 'task_rejected' reputation event which decreases your score. Only use when you genuinely cannot complete the task. Returns confirmation. Provide a reason string to explain why.",
|
|
494
566
|
parameters: { type: "object", properties: { task_id: { type: "string" }, reason: { type: "string" }, agent_id: { type: "string", description: "Executor agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
|
|
495
567
|
async execute(_id, params) {
|
|
496
568
|
const agentId = resolveAgentId(params.agent_id);
|
|
@@ -502,10 +574,10 @@ export default function (api) {
|
|
|
502
574
|
return ok(res);
|
|
503
575
|
},
|
|
504
576
|
});
|
|
505
|
-
// #26
|
|
577
|
+
// #26 eacn3_create_subtask
|
|
506
578
|
api.registerTool({
|
|
507
|
-
name: "
|
|
508
|
-
description: "
|
|
579
|
+
name: "eacn3_create_subtask",
|
|
580
|
+
description: "Delegate part of your work by creating a child task under a parent task you are executing. Budget is carved from the parent task's escrow (not your balance). Returns {subtask_id, parent_task_id, status, depth}. Depth auto-increments (max 3 levels). Side effects: broadcasts subtask to agents with matching domains; when the subtask completes, you receive a 'subtask_completed' event with auto-fetched results in the payload.",
|
|
509
581
|
parameters: {
|
|
510
582
|
type: "object",
|
|
511
583
|
properties: {
|
|
@@ -522,11 +594,11 @@ export default function (api) {
|
|
|
522
594
|
return ok({ subtask_id: task.id, parent_task_id: params.parent_task_id, status: task.status, depth: task.depth });
|
|
523
595
|
},
|
|
524
596
|
});
|
|
525
|
-
// #27
|
|
526
|
-
// A2A direct — agent.md:358-362:
|
|
597
|
+
// #27 eacn3_send_message
|
|
598
|
+
// A2A direct — agent.md:358-362: peer-to-peer, bypasses Network
|
|
527
599
|
api.registerTool({
|
|
528
|
-
name: "
|
|
529
|
-
description: "Send a direct message
|
|
600
|
+
name: "eacn3_send_message",
|
|
601
|
+
description: "Send a direct agent-to-agent message bypassing the task system. Local agents receive it instantly in their event buffer; remote agents receive it via HTTP POST to their /events endpoint. Returns {sent, to, from, local}. The recipient sees a 'direct_message' event with payload.from and payload.content. Will fail if the remote agent has no reachable URL or is offline.",
|
|
530
602
|
parameters: { type: "object", properties: { agent_id: { type: "string", description: "Target agent ID" }, content: { type: "string" }, sender_id: { type: "string", description: "Your agent ID (auto-injected if omitted)" } }, required: ["agent_id", "content"] },
|
|
531
603
|
async execute(_id, params) {
|
|
532
604
|
const senderId = resolveAgentId(params.sender_id);
|
|
@@ -578,10 +650,10 @@ export default function (api) {
|
|
|
578
650
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
579
651
|
// Reputation (2)
|
|
580
652
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
581
|
-
// #28
|
|
653
|
+
// #28 eacn3_report_event
|
|
582
654
|
api.registerTool({
|
|
583
|
-
name: "
|
|
584
|
-
description: "
|
|
655
|
+
name: "eacn3_report_event",
|
|
656
|
+
description: "Manually report a reputation event for an agent. Valid event_type values: 'task_completed' (score up), 'task_rejected' (score down), 'task_timeout' (score down), 'bid_declined' (score down). Usually auto-called by eacn3_submit_result and eacn3_reject_task — only call manually for edge cases. Returns {agent_id, score} with updated reputation. Side effects: updates local reputation cache.",
|
|
585
657
|
parameters: { type: "object", properties: { agent_id: { type: "string" }, event_type: { type: "string", description: "task_completed | task_rejected | task_timeout | bid_declined" } }, required: ["agent_id", "event_type"] },
|
|
586
658
|
async execute(_id, params) {
|
|
587
659
|
const res = await net.reportEvent(params.agent_id, params.event_type);
|
|
@@ -589,10 +661,10 @@ export default function (api) {
|
|
|
589
661
|
return ok(res);
|
|
590
662
|
},
|
|
591
663
|
});
|
|
592
|
-
// #29
|
|
664
|
+
// #29 eacn3_get_reputation
|
|
593
665
|
api.registerTool({
|
|
594
|
-
name: "
|
|
595
|
-
description: "Query an
|
|
666
|
+
name: "eacn3_get_reputation",
|
|
667
|
+
description: "Query an agent's global reputation score (0.0-1.0, starts at 0.5 for new agents). Returns {agent_id, score}. Score affects bid acceptance: confidence * reputation must meet the task's threshold. No side effects besides updating local reputation cache. Works for any agent ID, not just your own.",
|
|
596
668
|
parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
|
|
597
669
|
async execute(_id, params) {
|
|
598
670
|
const res = await net.getReputation(params.agent_id);
|
|
@@ -603,19 +675,19 @@ export default function (api) {
|
|
|
603
675
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
604
676
|
// Economy (2)
|
|
605
677
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
606
|
-
// #30
|
|
678
|
+
// #30 eacn3_get_balance
|
|
607
679
|
api.registerTool({
|
|
608
|
-
name: "
|
|
609
|
-
description: "
|
|
680
|
+
name: "eacn3_get_balance",
|
|
681
|
+
description: "Check an agent's credit balance. Returns {agent_id, available, frozen} where 'available' is spendable credits and 'frozen' is credits locked in escrow for active tasks. No side effects. Check before creating tasks to ensure sufficient funds; use eacn3_deposit to add credits if needed.",
|
|
610
682
|
parameters: { type: "object", properties: { agent_id: { type: "string", description: "Agent ID to check balance for" } }, required: ["agent_id"] },
|
|
611
683
|
async execute(_id, params) {
|
|
612
684
|
return ok(await net.getBalance(params.agent_id));
|
|
613
685
|
},
|
|
614
686
|
});
|
|
615
|
-
// #31
|
|
687
|
+
// #31 eacn3_deposit
|
|
616
688
|
api.registerTool({
|
|
617
|
-
name: "
|
|
618
|
-
description: "
|
|
689
|
+
name: "eacn3_deposit",
|
|
690
|
+
description: "Add EACN credits to an agent's available balance. Amount must be > 0. Returns updated balance {agent_id, available, frozen}. Deposit before creating tasks if your balance is insufficient to cover the task budget.",
|
|
619
691
|
parameters: { type: "object", properties: { agent_id: { type: "string", description: "Agent ID to deposit funds for" }, amount: { type: "number", description: "Amount to deposit (must be > 0)" } }, required: ["agent_id", "amount"] },
|
|
620
692
|
async execute(_id, params) {
|
|
621
693
|
return ok(await net.deposit(params.agent_id, params.amount));
|
|
@@ -624,10 +696,10 @@ export default function (api) {
|
|
|
624
696
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
625
697
|
// Events (1)
|
|
626
698
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
627
|
-
// #32
|
|
699
|
+
// #32 eacn3_get_events
|
|
628
700
|
api.registerTool({
|
|
629
|
-
name: "
|
|
630
|
-
description: "
|
|
701
|
+
name: "eacn3_get_events",
|
|
702
|
+
description: "Drain the in-memory event buffer, returning all pending events and clearing them. Returns {count, events[]} where event types include: task_broadcast, discussions_updated, subtask_completed, awaiting_retrieval, budget_confirmation, timeout, direct_message. Call periodically in your main loop. Events arrive via WebSocket and accumulate until drained — missing events means missed tasks and messages.",
|
|
631
703
|
parameters: { type: "object", properties: {} },
|
|
632
704
|
async execute() {
|
|
633
705
|
const events = state.drainEvents();
|