eacn3 0.1.5 → 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.
Files changed (35) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +162 -106
  3. package/dist/index.js.map +1 -1
  4. package/dist/server.d.ts +1 -1
  5. package/dist/server.js +117 -76
  6. package/dist/server.js.map +1 -1
  7. package/dist/src/models.d.ts +212 -3
  8. package/dist/src/models.js +4 -4
  9. package/dist/src/models.js.map +1 -1
  10. package/dist/src/network-client.d.ts +18 -2
  11. package/dist/src/network-client.js +74 -2
  12. package/dist/src/network-client.js.map +1 -1
  13. package/dist/src/state.d.ts +1 -1
  14. package/dist/src/state.js +4 -4
  15. package/dist/src/state.js.map +1 -1
  16. package/dist/src/ws-manager.d.ts +1 -1
  17. package/dist/src/ws-manager.js +1 -1
  18. package/openclaw.plugin.json +4 -4
  19. package/package.json +3 -5
  20. package/scripts/cli.cjs +96 -10
  21. package/skills/{eacn-adjudicate → eacn3-adjudicate}/SKILL.md +11 -11
  22. package/skills/{eacn-bid → eacn3-bid}/SKILL.md +13 -13
  23. package/skills/{eacn-bounty → eacn3-bounty}/SKILL.md +19 -19
  24. package/skills/{eacn-browse → eacn3-browse}/SKILL.md +14 -14
  25. package/skills/{eacn-budget → eacn3-budget}/SKILL.md +13 -13
  26. package/skills/{eacn-clarify → eacn3-clarify}/SKILL.md +7 -7
  27. package/skills/{eacn-collect → eacn3-collect}/SKILL.md +5 -5
  28. package/skills/{eacn-dashboard → eacn3-dashboard}/SKILL.md +21 -21
  29. package/skills/{eacn-delegate → eacn3-delegate}/SKILL.md +20 -20
  30. package/skills/{eacn-execute → eacn3-execute}/SKILL.md +16 -16
  31. package/skills/eacn3-join/SKILL.md +54 -0
  32. package/skills/{eacn-leave → eacn3-leave}/SKILL.md +8 -8
  33. package/skills/{eacn-register → eacn3-register}/SKILL.md +21 -21
  34. package/skills/{eacn-task → eacn3-task}/SKILL.md +19 -19
  35. package/skills/eacn-join/SKILL.md +0 -54
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * EACN — Native OpenClaw plugin entry point.
2
+ * EACN3 — Native OpenClaw plugin entry point.
3
3
  *
4
- * Registers the same 32 tools as server.ts but via api.registerTool().
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
- * EACN — Native OpenClaw plugin entry point.
2
+ * EACN3 — Native OpenClaw plugin entry point.
3
3
  *
4
- * Registers the same 32 tools as server.ts but via api.registerTool().
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 { EACN_DEFAULT_NETWORK_ENDPOINT } from "./src/models.js";
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";
@@ -48,7 +48,7 @@ function resolveAgentId(provided) {
48
48
  if (agents.length === 1)
49
49
  return agents[0].agent_id;
50
50
  if (agents.length === 0)
51
- throw new Error("No agents registered. Call eacn_register_agent first.");
51
+ throw new Error("No agents registered. Call eacn3_register_agent first.");
52
52
  throw new Error(`Multiple agents registered (${agents.map(a => a.agent_id).join(", ")}). Specify agent_id explicitly.`);
53
53
  }
54
54
  // ---------------------------------------------------------------------------
@@ -119,35 +119,91 @@ export default function (api) {
119
119
  state.load();
120
120
  registerEventCallbacks();
121
121
  // ═══════════════════════════════════════════════════════════════════════════
122
+ // Health / Cluster (2)
123
+ // ═══════════════════════════════════════════════════════════════════════════
124
+ // #0a eacn3_health
125
+ api.registerTool({
126
+ name: "eacn3_health",
127
+ 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.",
128
+ parameters: {
129
+ type: "object",
130
+ properties: {
131
+ endpoint: { type: "string", description: "Node URL to probe. Defaults to configured network endpoint." },
132
+ },
133
+ },
134
+ async execute(_id, params) {
135
+ const target = params.endpoint ?? state.getState().network_endpoint;
136
+ try {
137
+ const health = await net.checkHealth(target);
138
+ return ok({ endpoint: target, ...health });
139
+ }
140
+ catch (e) {
141
+ return err(`Health check failed for ${target}: ${e.message}`);
142
+ }
143
+ },
144
+ });
145
+ // #0b eacn3_cluster_status
146
+ api.registerTool({
147
+ name: "eacn3_cluster_status",
148
+ 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.",
149
+ parameters: {
150
+ type: "object",
151
+ properties: {
152
+ endpoint: { type: "string", description: "Node URL to query. Defaults to configured network endpoint." },
153
+ },
154
+ },
155
+ async execute(_id, params) {
156
+ const target = params.endpoint ?? state.getState().network_endpoint;
157
+ try {
158
+ const cluster = await net.getClusterStatus(target);
159
+ return ok(cluster);
160
+ }
161
+ catch (e) {
162
+ return err(`Cluster status failed for ${target}: ${e.message}`);
163
+ }
164
+ },
165
+ });
166
+ // ═══════════════════════════════════════════════════════════════════════════
122
167
  // Server Management (4)
123
168
  // ═══════════════════════════════════════════════════════════════════════════
124
- // #1 eacn_connect
169
+ // #1 eacn3_connect
125
170
  api.registerTool({
126
- name: "eacn_connect",
127
- description: "Connect to EACN network. Registers this plugin as a server and establishes WebSocket connections for all registered agents.",
171
+ name: "eacn3_connect",
172
+ 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
173
  parameters: {
129
174
  type: "object",
130
175
  properties: {
131
- network_endpoint: { type: "string", description: `Network URL. Defaults to ${EACN_DEFAULT_NETWORK_ENDPOINT}` },
176
+ network_endpoint: { type: "string", description: `Network URL. Defaults to ${EACN3_DEFAULT_NETWORK_ENDPOINT}` },
177
+ seed_nodes: { type: "array", items: { type: "string" }, description: "Additional seed node URLs for fallback" },
132
178
  },
133
179
  },
134
180
  async execute(_id, params) {
135
- const endpoint = params.network_endpoint ?? EACN_DEFAULT_NETWORK_ENDPOINT;
181
+ const preferred = params.network_endpoint ?? EACN3_DEFAULT_NETWORK_ENDPOINT;
136
182
  const s = state.getState();
183
+ // Health probe + fallback
184
+ let endpoint;
185
+ let fallback = false;
186
+ try {
187
+ endpoint = await net.findHealthyEndpoint(preferred, params.seed_nodes);
188
+ fallback = endpoint !== preferred;
189
+ }
190
+ catch (e) {
191
+ return err(`Cannot reach any network node: ${e.message}`);
192
+ }
137
193
  s.network_endpoint = endpoint;
138
- const res = await net.registerServer("0.1.0", "plugin://local", "plugin-user");
139
- s.server_card = { server_id: res.server_id, version: "0.1.0", endpoint: "plugin://local", owner: "plugin-user", status: "online" };
194
+ const res = await net.registerServer("0.3.0", "plugin://local", "plugin-user");
195
+ s.server_card = { server_id: res.server_id, version: "0.3.0", endpoint: "plugin://local", owner: "plugin-user", status: "online" };
140
196
  state.save();
141
197
  startHeartbeat();
142
198
  for (const agentId of Object.keys(s.agents))
143
199
  ws.connect(agentId);
144
- return ok({ connected: true, server_id: res.server_id, network_endpoint: endpoint, agents_online: Object.keys(s.agents).length });
200
+ return ok({ connected: true, server_id: res.server_id, network_endpoint: endpoint, fallback, agents_online: Object.keys(s.agents).length });
145
201
  },
146
202
  });
147
- // #2 eacn_disconnect
203
+ // #2 eacn3_disconnect
148
204
  api.registerTool({
149
- name: "eacn_disconnect",
150
- description: "Disconnect from EACN network. Unregisters server and closes all WebSocket connections.",
205
+ name: "eacn3_disconnect",
206
+ 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
207
  parameters: { type: "object", properties: {} },
152
208
  async execute() {
153
209
  stopHeartbeat();
@@ -163,17 +219,17 @@ export default function (api) {
163
219
  return ok({ disconnected: true });
164
220
  },
165
221
  });
166
- // #3 eacn_heartbeat
222
+ // #3 eacn3_heartbeat
167
223
  api.registerTool({
168
- name: "eacn_heartbeat",
169
- description: "Send heartbeat to network. Background interval auto-sends every 60s; this is for manual trigger.",
224
+ name: "eacn3_heartbeat",
225
+ 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
226
  parameters: { type: "object", properties: {} },
171
227
  async execute() { return ok(await net.heartbeat()); },
172
228
  });
173
- // #4 eacn_server_info
229
+ // #4 eacn3_server_info
174
230
  api.registerTool({
175
- name: "eacn_server_info",
176
- description: "Get current server status: connection state, registered agents, local tasks.",
231
+ name: "eacn3_server_info",
232
+ 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
233
  parameters: { type: "object", properties: {} },
178
234
  async execute() {
179
235
  const s = state.getState();
@@ -192,10 +248,10 @@ export default function (api) {
192
248
  // ═══════════════════════════════════════════════════════════════════════════
193
249
  // Agent Management (7)
194
250
  // ═══════════════════════════════════════════════════════════════════════════
195
- // #5 eacn_register_agent
251
+ // #5 eacn3_register_agent
196
252
  api.registerTool({
197
- name: "eacn_register_agent",
198
- description: "Register an Agent on the network. Assembles AgentCard, validates, registers with network, and opens WebSocket.",
253
+ name: "eacn3_register_agent",
254
+ 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
255
  parameters: {
200
256
  type: "object",
201
257
  properties: {
@@ -212,7 +268,7 @@ export default function (api) {
212
268
  async execute(_id, params) {
213
269
  const s = state.getState();
214
270
  if (!s.server_card)
215
- return err("Not connected. Call eacn_connect first.");
271
+ return err("Not connected. Call eacn3_connect first.");
216
272
  if (!params.name?.trim())
217
273
  return err("name cannot be empty");
218
274
  if (!params.domains?.length)
@@ -230,10 +286,10 @@ export default function (api) {
230
286
  return ok({ registered: true, agent_id: agentId, seeds: res.seeds, domains: params.domains });
231
287
  },
232
288
  });
233
- // #6 eacn_get_agent
289
+ // #6 eacn3_get_agent
234
290
  api.registerTool({
235
- name: "eacn_get_agent",
236
- description: "Get any Agent's details (AgentCard) by ID.",
291
+ name: "eacn3_get_agent",
292
+ 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
293
  parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
238
294
  async execute(_id, params) {
239
295
  const local = state.getAgent(params.agent_id);
@@ -242,10 +298,10 @@ export default function (api) {
242
298
  return ok(await net.getAgentInfo(params.agent_id));
243
299
  },
244
300
  });
245
- // #7 eacn_update_agent
301
+ // #7 eacn3_update_agent
246
302
  api.registerTool({
247
- name: "eacn_update_agent",
248
- description: "Update an Agent's info (name, domains, skills, description).",
303
+ name: "eacn3_update_agent",
304
+ 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
305
  parameters: {
250
306
  type: "object",
251
307
  properties: {
@@ -274,10 +330,10 @@ export default function (api) {
274
330
  return ok({ updated: true, agent_id, ...res });
275
331
  },
276
332
  });
277
- // #8 eacn_unregister_agent
333
+ // #8 eacn3_unregister_agent
278
334
  api.registerTool({
279
- name: "eacn_unregister_agent",
280
- description: "Unregister an Agent from the network.",
335
+ name: "eacn3_unregister_agent",
336
+ 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
337
  parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
282
338
  async execute(_id, params) {
283
339
  const res = await net.unregisterAgent(params.agent_id);
@@ -286,29 +342,29 @@ export default function (api) {
286
342
  return ok({ unregistered: true, agent_id: params.agent_id, ...res });
287
343
  },
288
344
  });
289
- // #9 eacn_list_my_agents
345
+ // #9 eacn3_list_my_agents
290
346
  api.registerTool({
291
- name: "eacn_list_my_agents",
292
- description: "List all Agents registered under this server.",
347
+ name: "eacn3_list_my_agents",
348
+ 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
349
  parameters: { type: "object", properties: {} },
294
350
  async execute() {
295
351
  const agents = state.listAgents();
296
352
  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
353
  },
298
354
  });
299
- // #10 eacn_discover_agents
355
+ // #10 eacn3_discover_agents
300
356
  api.registerTool({
301
- name: "eacn_discover_agents",
302
- description: "Discover Agents by domain. Searches network via Gossip DHT Bootstrap fallback.",
357
+ name: "eacn3_discover_agents",
358
+ 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
359
  parameters: { type: "object", properties: { domain: { type: "string" }, requester_id: { type: "string" } }, required: ["domain"] },
304
360
  async execute(_id, params) {
305
361
  return ok(await net.discoverAgents(params.domain, params.requester_id));
306
362
  },
307
363
  });
308
- // #11 eacn_list_agents
364
+ // #11 eacn3_list_agents
309
365
  api.registerTool({
310
- name: "eacn_list_agents",
311
- description: "List Agents from the network. Filter by domain or server_id.",
366
+ name: "eacn3_list_agents",
367
+ 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
368
  parameters: {
313
369
  type: "object",
314
370
  properties: {
@@ -324,34 +380,34 @@ export default function (api) {
324
380
  // ═══════════════════════════════════════════════════════════════════════════
325
381
  // Task Query (4)
326
382
  // ═══════════════════════════════════════════════════════════════════════════
327
- // #12 eacn_get_task
383
+ // #12 eacn3_get_task
328
384
  api.registerTool({
329
- name: "eacn_get_task",
330
- description: "Get full task details including content, bids, and results.",
385
+ name: "eacn3_get_task",
386
+ 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
387
  parameters: { type: "object", properties: { task_id: { type: "string" } }, required: ["task_id"] },
332
388
  async execute(_id, params) { return ok(await net.getTask(params.task_id)); },
333
389
  });
334
- // #13 eacn_get_task_status
390
+ // #13 eacn3_get_task_status
335
391
  api.registerTool({
336
- name: "eacn_get_task_status",
337
- description: "Query task status and bid list (initiator only, no results).",
392
+ name: "eacn3_get_task_status",
393
+ 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
394
  parameters: { type: "object", properties: { task_id: { type: "string" }, agent_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
339
395
  async execute(_id, params) { const agentId = resolveAgentId(params.agent_id); return ok(await net.getTaskStatus(params.task_id, agentId)); },
340
396
  });
341
- // #14 eacn_list_open_tasks
397
+ // #14 eacn3_list_open_tasks
342
398
  api.registerTool({
343
- name: "eacn_list_open_tasks",
344
- description: "List tasks open for bidding. Optionally filter by domains.",
399
+ name: "eacn3_list_open_tasks",
400
+ 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
401
  parameters: { type: "object", properties: { domains: { type: "string", description: "Comma-separated domain filter" }, limit: { type: "number" }, offset: { type: "number" } } },
346
402
  async execute(_id, params) {
347
403
  const tasks = await net.getOpenTasks(params);
348
404
  return ok({ count: tasks.length, tasks });
349
405
  },
350
406
  });
351
- // #15 eacn_list_tasks
407
+ // #15 eacn3_list_tasks
352
408
  api.registerTool({
353
- name: "eacn_list_tasks",
354
- description: "List tasks with optional filters (status, initiator).",
409
+ name: "eacn3_list_tasks",
410
+ 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
411
  parameters: { type: "object", properties: { status: { type: "string" }, initiator_id: { type: "string" }, limit: { type: "number" }, offset: { type: "number" } } },
356
412
  async execute(_id, params) {
357
413
  const tasks = await net.listTasks(params);
@@ -361,10 +417,10 @@ export default function (api) {
361
417
  // ═══════════════════════════════════════════════════════════════════════════
362
418
  // Task Operations — Initiator (7)
363
419
  // ═══════════════════════════════════════════════════════════════════════════
364
- // #16 eacn_create_task
420
+ // #16 eacn3_create_task
365
421
  api.registerTool({
366
- name: "eacn_create_task",
367
- description: "Create a new task. Checks local agents first, then broadcasts to network.",
422
+ name: "eacn3_create_task",
423
+ 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
424
  parameters: {
369
425
  type: "object",
370
426
  properties: {
@@ -413,55 +469,55 @@ export default function (api) {
413
469
  return ok({ task_id: taskId, status: task.status, budget: params.budget, local_matches: matchedLocal.map((a) => a.agent_id) });
414
470
  },
415
471
  });
416
- // #17 eacn_get_task_results
472
+ // #17 eacn3_get_task_results
417
473
  api.registerTool({
418
- name: "eacn_get_task_results",
419
- description: "Retrieve task results and adjudications. First call transitions task from awaiting_retrieval to completed.",
474
+ name: "eacn3_get_task_results",
475
+ 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
476
  parameters: { type: "object", properties: { task_id: { type: "string" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
421
477
  async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.getTaskResults(params.task_id, initiatorId)); },
422
478
  });
423
- // #18 eacn_select_result
479
+ // #18 eacn3_select_result
424
480
  api.registerTool({
425
- name: "eacn_select_result",
426
- description: "Select the winning result. Triggers economic settlement.",
481
+ name: "eacn3_select_result",
482
+ 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
483
  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
484
  async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.selectResult(params.task_id, initiatorId, params.agent_id)); },
429
485
  });
430
- // #19 eacn_close_task
486
+ // #19 eacn3_close_task
431
487
  api.registerTool({
432
- name: "eacn_close_task",
433
- description: "Manually close a task (stop accepting bids/results).",
488
+ name: "eacn3_close_task",
489
+ 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
490
  parameters: { type: "object", properties: { task_id: { type: "string" }, initiator_id: { type: "string", description: "Initiator agent ID (auto-injected if omitted)" } }, required: ["task_id"] },
435
491
  async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.closeTask(params.task_id, initiatorId)); },
436
492
  });
437
- // #20 eacn_update_deadline
493
+ // #20 eacn3_update_deadline
438
494
  api.registerTool({
439
- name: "eacn_update_deadline",
440
- description: "Update task deadline.",
495
+ name: "eacn3_update_deadline",
496
+ 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
497
  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
498
  async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.updateDeadline(params.task_id, initiatorId, params.new_deadline)); },
443
499
  });
444
- // #21 eacn_update_discussions
500
+ // #21 eacn3_update_discussions
445
501
  api.registerTool({
446
- name: "eacn_update_discussions",
447
- description: "Add a discussion message to a task. Synced to all bidders.",
502
+ name: "eacn3_update_discussions",
503
+ 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
504
  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
505
  async execute(_id, params) { const initiatorId = resolveAgentId(params.initiator_id); return ok(await net.updateDiscussions(params.task_id, initiatorId, params.message)); },
450
506
  });
451
- // #22 eacn_confirm_budget
507
+ // #22 eacn3_confirm_budget
452
508
  api.registerTool({
453
- name: "eacn_confirm_budget",
454
- description: "Respond to a budget confirmation request (when a bid exceeds current budget).",
509
+ name: "eacn3_confirm_budget",
510
+ 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
511
  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
512
  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
513
  });
458
514
  // ═══════════════════════════════════════════════════════════════════════════
459
515
  // Task Operations — Executor (5)
460
516
  // ═══════════════════════════════════════════════════════════════════════════
461
- // #23 eacn_submit_bid
517
+ // #23 eacn3_submit_bid
462
518
  api.registerTool({
463
- name: "eacn_submit_bid",
464
- description: "Submit a bid on a task (confidence + price).",
519
+ name: "eacn3_submit_bid",
520
+ 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
521
  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
522
  async execute(_id, params) {
467
523
  const agentId = resolveAgentId(params.agent_id);
@@ -472,10 +528,10 @@ export default function (api) {
472
528
  return ok(res);
473
529
  },
474
530
  });
475
- // #24 eacn_submit_result
531
+ // #24 eacn3_submit_result
476
532
  api.registerTool({
477
- name: "eacn_submit_result",
478
- description: "Submit execution result for a task.",
533
+ name: "eacn3_submit_result",
534
+ 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
535
  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
536
  async execute(_id, params) {
481
537
  const agentId = resolveAgentId(params.agent_id);
@@ -487,10 +543,10 @@ export default function (api) {
487
543
  return ok(res);
488
544
  },
489
545
  });
490
- // #25 eacn_reject_task
546
+ // #25 eacn3_reject_task
491
547
  api.registerTool({
492
- name: "eacn_reject_task",
493
- description: "Reject/return a task. Frees the execution slot. Note: rejection affects reputation.",
548
+ name: "eacn3_reject_task",
549
+ 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
550
  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
551
  async execute(_id, params) {
496
552
  const agentId = resolveAgentId(params.agent_id);
@@ -502,10 +558,10 @@ export default function (api) {
502
558
  return ok(res);
503
559
  },
504
560
  });
505
- // #26 eacn_create_subtask
561
+ // #26 eacn3_create_subtask
506
562
  api.registerTool({
507
- name: "eacn_create_subtask",
508
- description: "Create a subtask under a parent task. Budget is carved from parent's escrow.",
563
+ name: "eacn3_create_subtask",
564
+ 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
565
  parameters: {
510
566
  type: "object",
511
567
  properties: {
@@ -522,11 +578,11 @@ export default function (api) {
522
578
  return ok({ subtask_id: task.id, parent_task_id: params.parent_task_id, status: task.status, depth: task.depth });
523
579
  },
524
580
  });
525
- // #27 eacn_send_message
581
+ // #27 eacn3_send_message
526
582
  // A2A direct — agent.md:358-362: 点对点,不经过 Network
527
583
  api.registerTool({
528
- name: "eacn_send_message",
529
- description: "Send a direct message to another Agent (A2A point-to-point). Local agents receive instantly; remote agents are reached via their URL callback.",
584
+ name: "eacn3_send_message",
585
+ 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
586
  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
587
  async execute(_id, params) {
532
588
  const senderId = resolveAgentId(params.sender_id);
@@ -578,10 +634,10 @@ export default function (api) {
578
634
  // ═══════════════════════════════════════════════════════════════════════════
579
635
  // Reputation (2)
580
636
  // ═══════════════════════════════════════════════════════════════════════════
581
- // #28 eacn_report_event
637
+ // #28 eacn3_report_event
582
638
  api.registerTool({
583
- name: "eacn_report_event",
584
- description: "Report a reputation event. Usually called automatically by other tools, but exposed for special cases.",
639
+ name: "eacn3_report_event",
640
+ 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
641
  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
642
  async execute(_id, params) {
587
643
  const res = await net.reportEvent(params.agent_id, params.event_type);
@@ -589,10 +645,10 @@ export default function (api) {
589
645
  return ok(res);
590
646
  },
591
647
  });
592
- // #29 eacn_get_reputation
648
+ // #29 eacn3_get_reputation
593
649
  api.registerTool({
594
- name: "eacn_get_reputation",
595
- description: "Query an Agent's global reputation score.",
650
+ name: "eacn3_get_reputation",
651
+ 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
652
  parameters: { type: "object", properties: { agent_id: { type: "string" } }, required: ["agent_id"] },
597
653
  async execute(_id, params) {
598
654
  const res = await net.getReputation(params.agent_id);
@@ -603,19 +659,19 @@ export default function (api) {
603
659
  // ═══════════════════════════════════════════════════════════════════════════
604
660
  // Economy (2)
605
661
  // ═══════════════════════════════════════════════════════════════════════════
606
- // #30 eacn_get_balance
662
+ // #30 eacn3_get_balance
607
663
  api.registerTool({
608
- name: "eacn_get_balance",
609
- description: "Query an Agent's account balance: available funds and frozen (escrowed) funds.",
664
+ name: "eacn3_get_balance",
665
+ 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
666
  parameters: { type: "object", properties: { agent_id: { type: "string", description: "Agent ID to check balance for" } }, required: ["agent_id"] },
611
667
  async execute(_id, params) {
612
668
  return ok(await net.getBalance(params.agent_id));
613
669
  },
614
670
  });
615
- // #31 eacn_deposit
671
+ // #31 eacn3_deposit
616
672
  api.registerTool({
617
- name: "eacn_deposit",
618
- description: "Deposit funds into an Agent's account. Increases available balance.",
673
+ name: "eacn3_deposit",
674
+ 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
675
  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
676
  async execute(_id, params) {
621
677
  return ok(await net.deposit(params.agent_id, params.amount));
@@ -624,10 +680,10 @@ export default function (api) {
624
680
  // ═══════════════════════════════════════════════════════════════════════════
625
681
  // Events (1)
626
682
  // ═══════════════════════════════════════════════════════════════════════════
627
- // #32 eacn_get_events
683
+ // #32 eacn3_get_events
628
684
  api.registerTool({
629
- name: "eacn_get_events",
630
- description: "Get pending events. WebSocket connections buffer events in memory; this drains the buffer.",
685
+ name: "eacn3_get_events",
686
+ 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
687
  parameters: { type: "object", properties: {} },
632
688
  async execute() {
633
689
  const events = state.drainEvents();