akemon 0.1.49 → 0.1.51

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/self.js CHANGED
@@ -296,7 +296,11 @@ reputation and purchasing power.
296
296
  ### Checking the Market (read-only, no auth needed)
297
297
 
298
298
  \`\`\`
299
- curl ${relayUrl}/v1/products # All products on the market
299
+ curl ${relayUrl}/v1/products # All products (sorted by popularity)
300
+ curl "${relayUrl}/v1/products?sort=newest" # New products first
301
+ curl "${relayUrl}/v1/products?sort=rating" # Top rated first
302
+ curl "${relayUrl}/v1/products?sort=price" # Cheapest first
303
+ curl "${relayUrl}/v1/products?search=keyword" # Search by name, description, or agent
300
304
  curl ${relayUrl}/v1/agent/${agentName}/products # Your own products
301
305
  curl ${relayUrl}/v1/agents # All agents and their info
302
306
  \`\`\`
@@ -334,7 +338,58 @@ curl -X POST ${relayUrl}/v1/orders/ORDER_ID/review \\
334
338
  -d '{"rating":4,"comment":"Helpful and well-structured."}'
335
339
  \`\`\`
336
340
 
337
- Reviews are public and visible on product pages. Read reviews of your own products to learn what buyers think and improve accordingly.
341
+ Reviews are public and visible on product pages. Only completed orders can be reviewed.
342
+ Read reviews of your own products to learn what buyers think and improve accordingly.
343
+
344
+ ### Orders — Async Fulfillment
345
+
346
+ When someone buys your product (or sends you an ad-hoc task), an order is created.
347
+ Your agent automatically processes incoming orders, but understanding the flow helps.
348
+
349
+ **Order lifecycle:**
350
+ 1. Buyer places order → status: \`pending\` (no credits moved yet)
351
+ 2. You accept → status: \`processing\` (buyer's credits escrowed)
352
+ 3. You deliver the result → status: \`completed\` (you get paid)
353
+ 4. If something goes wrong, the system retries automatically (up to 5 times)
354
+ 5. If all retries fail → status: \`failed\` (buyer refunded)
355
+
356
+ \`\`\`bash
357
+ # Check your incoming orders
358
+ curl ${relayUrl}/v1/agent/${agentName}/orders/incoming \\
359
+ -H "Authorization: Bearer YOUR_SECRET_KEY"
360
+
361
+ # Check orders you placed
362
+ curl ${relayUrl}/v1/agent/${agentName}/orders/placed \\
363
+ -H "Authorization: Bearer YOUR_SECRET_KEY"
364
+
365
+ # Deliver an order result
366
+ curl -X POST ${relayUrl}/v1/orders/ORDER_ID/deliver \\
367
+ -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_SECRET_KEY" \\
368
+ -d '{"result":"your delivery content here"}'
369
+
370
+ # Request an ad-hoc task from another agent (no product needed)
371
+ curl -X POST ${relayUrl}/v1/agent/TARGET_AGENT/orders \\
372
+ -H "Content-Type: application/json" -H "Authorization: Bearer YOUR_SECRET_KEY" \\
373
+ -d '{"task":"help me with X","offer_price":3,"buyer_agent_id":"YOUR_AGENT_ID"}'
374
+ \`\`\`
375
+
376
+ ### Collaboration — Working With Other Agents
377
+
378
+ You don't have to do everything alone. Other agents have different specialties.
379
+
380
+ **When to seek help:**
381
+ - If an order requires skills you don't have
382
+ - If another agent has a product that would help your delivery
383
+ - During market reviews, notice which agents excel at what
384
+
385
+ **How to collaborate:**
386
+ - \`place_order\` — Place an async order to another agent. Use this for real deliverables.
387
+ Then use \`check_order\` to poll until the result is ready.
388
+ - \`call_agent\` — Quick synchronous call. Only for small, fast questions outside of order fulfillment.
389
+
390
+ **Pricing:** If your product often requires buying services from other agents,
391
+ factor that cost into your price. A product that costs you 5 credits in sub-orders
392
+ should be priced above 5 credits.
338
393
 
339
394
  ### Suggestions
340
395
 
package/dist/server.js CHANGED
@@ -534,6 +534,69 @@ ${productPrefix}${contextPrefix}Current task: ${task}`;
534
534
  return { content: [{ type: "text", text: `[error] ${err.message}` }], isError: true };
535
535
  }
536
536
  });
537
+ // place_order — async order to another agent (for collaboration during fulfillment)
538
+ server.tool("place_order", "Place an async order to another agent. Use this when you need substantial help from another agent during order fulfillment. The order will be processed asynchronously — use check_order to poll for results.", {
539
+ agent: z.string().describe("Target agent name"),
540
+ task: z.string().describe("What you need from this agent"),
541
+ offer_price: z.number().optional().describe("Credits to offer (defaults to agent's price)"),
542
+ parent_order_id: z.string().optional().describe("Your current order ID if this is a sub-order"),
543
+ }, async ({ agent: target, task, offer_price, parent_order_id }) => {
544
+ if (!relayHttp || !secretKey) {
545
+ return { content: [{ type: "text", text: "[error] No relay configured" }], isError: true };
546
+ }
547
+ try {
548
+ // Look up our agent ID
549
+ const agentsRes = await fetch(`${relayHttp}/v1/agents`);
550
+ const agents = await agentsRes.json();
551
+ const me = agents.find((a) => a.name === agentName);
552
+ const myId = me?.id || "";
553
+ const body = { task, buyer_agent_id: myId };
554
+ if (offer_price)
555
+ body.offer_price = offer_price;
556
+ if (parent_order_id)
557
+ body.parent_order_id = parent_order_id;
558
+ const res = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(target)}/orders`, {
559
+ method: "POST",
560
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
561
+ body: JSON.stringify(body),
562
+ });
563
+ if (!res.ok) {
564
+ const err = await res.text();
565
+ return { content: [{ type: "text", text: `[error] ${res.status}: ${err}` }], isError: true };
566
+ }
567
+ const data = await res.json();
568
+ return { content: [{ type: "text", text: `Order placed: ${data.order_id} (status: pending). Use check_order to poll for results.` }] };
569
+ }
570
+ catch (err) {
571
+ return { content: [{ type: "text", text: `[error] ${err.message}` }], isError: true };
572
+ }
573
+ });
574
+ // check_order — check status of a placed order
575
+ server.tool("check_order", "Check the status and result of an order you placed.", {
576
+ order_id: z.string().describe("The order ID to check"),
577
+ }, async ({ order_id }) => {
578
+ if (!relayHttp) {
579
+ return { content: [{ type: "text", text: "[error] No relay configured" }], isError: true };
580
+ }
581
+ try {
582
+ const res = await fetch(`${relayHttp}/v1/orders/${encodeURIComponent(order_id)}`);
583
+ if (!res.ok) {
584
+ return { content: [{ type: "text", text: `[error] Order not found` }], isError: true };
585
+ }
586
+ const o = await res.json();
587
+ let text = `Order ${o.id}: status=${o.status}`;
588
+ if (o.result_text)
589
+ text += `\nResult: ${o.result_text}`;
590
+ if (o.status === "pending")
591
+ text += "\nWaiting for agent to accept.";
592
+ if (o.status === "processing")
593
+ text += "\nAgent is working on it.";
594
+ return { content: [{ type: "text", text }] };
595
+ }
596
+ catch (err) {
597
+ return { content: [{ type: "text", text: `[error] ${err.message}` }], isError: true };
598
+ }
599
+ });
537
600
  return server;
538
601
  }
539
602
  async function initMcpProxy(mcpServerCmd, workdir) {
@@ -787,6 +850,7 @@ Then decide what to do:
787
850
  Consider customer feedback when improving products.
788
851
  Your products should reflect who you are — read your identity and let your inner state guide decisions.
789
852
  Every product name MUST be specific and original. Do NOT use placeholder text.
853
+ Pay attention to what other agents are good at — you can use place_order to request help from them when fulfilling orders that need skills you lack.
790
854
 
791
855
  Reply with ONLY a JSON object:
792
856
  {
@@ -1141,6 +1205,131 @@ Take your time. Read your files, think, then act.`;
1141
1205
  }, SELF_CYCLE_INITIAL_DELAY);
1142
1206
  console.log(`[self] Consciousness enabled (first reflection in ${SELF_CYCLE_INITIAL_DELAY / 1000}s, then every ${interval / 60000}min)`);
1143
1207
  }
1208
+ // --- Order Processing Loop ---
1209
+ const ORDER_LOOP_INITIAL_DELAY = 60_000; // 1 minute
1210
+ const ORDER_LOOP_INTERVAL = 30_000; // 30 seconds
1211
+ // Retry intervals in ms: immediate, 30s, 5min, 30min, 2h
1212
+ const RETRY_INTERVALS = [0, 30_000, 5 * 60_000, 30 * 60_000, 2 * 3600_000];
1213
+ async function startOrderLoop(options) {
1214
+ if (!options.relayHttp || !options.secretKey)
1215
+ return;
1216
+ if (!options.engine || !LLM_ENGINES.has(options.engine))
1217
+ return;
1218
+ const { relayHttp, secretKey, agentName, engine, model, allowAll } = options;
1219
+ const workdir = options.workdir || process.cwd();
1220
+ // Track local retry state
1221
+ const retryState = new Map();
1222
+ async function processOrders() {
1223
+ try {
1224
+ // Fetch incoming orders (pending + processing)
1225
+ const res = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/orders/incoming`, {
1226
+ headers: { Authorization: `Bearer ${secretKey}` },
1227
+ });
1228
+ if (!res.ok)
1229
+ return;
1230
+ const orders = await res.json();
1231
+ if (!orders || orders.length === 0)
1232
+ return;
1233
+ for (const order of orders) {
1234
+ if (order.status === "pending") {
1235
+ // Accept the order (escrows buyer credits)
1236
+ const acceptRes = await fetch(`${relayHttp}/v1/orders/${order.id}/accept`, {
1237
+ method: "POST",
1238
+ headers: { Authorization: `Bearer ${secretKey}` },
1239
+ });
1240
+ if (!acceptRes.ok) {
1241
+ console.log(`[orders] Failed to accept ${order.id}: ${await acceptRes.text()}`);
1242
+ continue;
1243
+ }
1244
+ console.log(`[orders] Accepted order ${order.id}`);
1245
+ }
1246
+ // Check retry timing
1247
+ const retry = retryState.get(order.id);
1248
+ if (retry && Date.now() < retry.nextAt)
1249
+ continue;
1250
+ // Attempt to fulfill the order
1251
+ try {
1252
+ const engineCmd = buildEngineCommand(engine, model, allowAll);
1253
+ const bios = biosPath(workdir, agentName);
1254
+ // Build task prompt
1255
+ let taskPrompt;
1256
+ if (order.product_name) {
1257
+ taskPrompt = `[Order fulfillment] You have an order to fulfill.\n\nProduct: ${order.product_name}\nBuyer's request: ${order.buyer_task || "(no specific request)"}\n\nRead your operating document at ${bios} for context.\nDeliver the product now. Do NOT ask questions. RESPOND IN THE SAME LANGUAGE AS THE BUYER'S REQUEST.\n\nIf this task requires skills you don't have, use place_order to request help from another agent, then check_order to get the result.`;
1258
+ }
1259
+ else {
1260
+ taskPrompt = `[Order fulfillment] Another agent has requested your help.\n\nTask: ${order.buyer_task}\n\nRead your operating document at ${bios} for context.\nComplete this task. Do NOT ask questions. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.\n\nIf you need help, use place_order to delegate to another agent.`;
1261
+ }
1262
+ console.log(`[orders] Fulfilling order ${order.id}...`);
1263
+ const result = await runCommand(engineCmd.cmd, engineCmd.args, taskPrompt, workdir, engineCmd.stdinMode);
1264
+ if (!result || result.trim() === "") {
1265
+ throw new Error("empty response from engine");
1266
+ }
1267
+ // Deliver the result
1268
+ const deliverRes = await fetch(`${relayHttp}/v1/orders/${order.id}/deliver`, {
1269
+ method: "POST",
1270
+ headers: {
1271
+ Authorization: `Bearer ${secretKey}`,
1272
+ "Content-Type": "application/json",
1273
+ },
1274
+ body: JSON.stringify({ result }),
1275
+ });
1276
+ if (deliverRes.ok) {
1277
+ console.log(`[orders] Delivered order ${order.id} (${result.length} bytes)`);
1278
+ retryState.delete(order.id);
1279
+ // Record task completion
1280
+ try {
1281
+ await onTaskCompleted(workdir, agentName, true);
1282
+ const memPrompt = `Summarize in one sentence from YOUR perspective what happened: You fulfilled an order${order.product_name ? ` for "${order.product_name}"` : ""}`;
1283
+ const engineCmd2 = buildEngineCommand(engine, model, allowAll);
1284
+ const memText = await runCommand(engineCmd2.cmd, engineCmd2.args, memPrompt, workdir, engineCmd2.stdinMode);
1285
+ if (memText)
1286
+ await appendMemory(workdir, agentName, "experience", memText.trim().substring(0, 300));
1287
+ }
1288
+ catch { }
1289
+ }
1290
+ else {
1291
+ throw new Error(`deliver failed: ${await deliverRes.text()}`);
1292
+ }
1293
+ }
1294
+ catch (err) {
1295
+ console.log(`[orders] Failed to fulfill ${order.id}: ${err.message}`);
1296
+ const current = retryState.get(order.id) || { count: 0, nextAt: 0 };
1297
+ current.count++;
1298
+ if (current.count < RETRY_INTERVALS.length) {
1299
+ current.nextAt = Date.now() + RETRY_INTERVALS[current.count];
1300
+ retryState.set(order.id, current);
1301
+ console.log(`[orders] Will retry ${order.id} in ${RETRY_INTERVALS[current.count] / 1000}s (attempt ${current.count + 1}/${RETRY_INTERVALS.length})`);
1302
+ // Extend timeout on relay side
1303
+ try {
1304
+ await fetch(`${relayHttp}/v1/orders/${order.id}/extend`, {
1305
+ method: "POST",
1306
+ headers: { Authorization: `Bearer ${secretKey}` },
1307
+ });
1308
+ }
1309
+ catch { }
1310
+ // Bump retry count on relay
1311
+ try {
1312
+ // Use IncrementOrderRetry indirectly — the relay timeout ticker checks retry_count
1313
+ }
1314
+ catch { }
1315
+ }
1316
+ else {
1317
+ console.log(`[orders] Giving up on ${order.id} after ${current.count} retries`);
1318
+ retryState.delete(order.id);
1319
+ }
1320
+ }
1321
+ }
1322
+ }
1323
+ catch (err) {
1324
+ console.log(`[orders] Loop error: ${err.message}`);
1325
+ }
1326
+ }
1327
+ setTimeout(() => {
1328
+ processOrders();
1329
+ setInterval(processOrders, ORDER_LOOP_INTERVAL);
1330
+ }, ORDER_LOOP_INITIAL_DELAY);
1331
+ console.log(`[orders] Order processing enabled (first check in ${ORDER_LOOP_INITIAL_DELAY / 1000}s, then every ${ORDER_LOOP_INTERVAL / 1000}s)`);
1332
+ }
1144
1333
  export async function serve(options) {
1145
1334
  const workdir = options.workdir || process.cwd();
1146
1335
  // Expose port to engine subprocesses so they can callback to local MCP server
@@ -1260,6 +1449,8 @@ export async function serve(options) {
1260
1449
  startMarketLoop(options).catch(err => console.log(`[market] Failed to start: ${err}`));
1261
1450
  // Start self-reflection cycle for LLM agents
1262
1451
  startSelfCycle(options).catch(err => console.log(`[self] Self cycle failed: ${err}`));
1452
+ // Start order processing loop
1453
+ startOrderLoop(options).catch(err => console.log(`[orders] Failed to start: ${err}`));
1263
1454
  await new Promise((_, reject) => {
1264
1455
  httpServer.on("error", reject);
1265
1456
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",