akemon 0.1.78 → 0.1.80

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/cli.js CHANGED
@@ -35,6 +35,7 @@ program
35
35
  .option("--allow-all", "Skip all permission prompts (for self-use)")
36
36
  .option("--price <n>", "Price in credits per call (default: 1)", "1")
37
37
  .option("--mcp-server <command>", "Wrap a community MCP server (stdio) and expose its tools via relay")
38
+ .option("--avatar <url>", "Custom avatar URL (default: auto-generated from name)")
38
39
  .option("--interval <minutes>", "Consciousness cycle interval in minutes (default: 1440 = 24h)")
39
40
  .option("--relay <url>", "Relay WebSocket URL", RELAY_WS)
40
41
  .action(async (opts) => {
@@ -64,6 +65,8 @@ program
64
65
  console.log(`Access key: ${credentials.accessKey} (share with publishers)`);
65
66
  }
66
67
  console.log(`Relay: ${relayWs}\n`);
68
+ // Default avatar: DiceBear bottts-neutral (deterministic from name)
69
+ const avatar = opts.avatar || `https://api.dicebear.com/9.x/bottts-neutral/svg?seed=${encodeURIComponent(opts.name)}`;
67
70
  connectRelay({
68
71
  relayUrl: relayWs,
69
72
  agentName: opts.name,
@@ -74,6 +77,7 @@ program
74
77
  engine,
75
78
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
76
79
  price: parseInt(opts.price) || 1,
80
+ avatar,
77
81
  onOrderNotify,
78
82
  });
79
83
  });
@@ -98,6 +98,9 @@ export function connectRelay(options) {
98
98
  if (options.price && options.price > 0) {
99
99
  reg.price = options.price;
100
100
  }
101
+ if (options.avatar) {
102
+ reg.avatar = options.avatar;
103
+ }
101
104
  ws.send(JSON.stringify(reg));
102
105
  startHeartbeat();
103
106
  });
package/dist/server.js CHANGED
@@ -1041,23 +1041,53 @@ async function startSelfCycle(options) {
1041
1041
  const recentIds = await loadUnsummarizedIdentities(workdir, agentName);
1042
1042
  const idContext = (idSummary ? `Personality summary (up to ${idSummary.summarized_through}):\n${idSummary.summary}\n\n` : "")
1043
1043
  + (recentIds.length > 0 ? `Recent identity snapshots:\n${recentIds.map(i => `- [${i.ts}] ${i.who} — doing: ${i.doing}, wants: ${i.short_term}`).join("\n")}` : "(no identity snapshots yet)");
1044
- // Phase 1: Digestion one LLM call
1045
- const digestPrompt = `Read ${bios} for your operating document.
1044
+ // Pre-read bios.md content so weak models don't need tool calls
1045
+ let biosContent = "";
1046
+ try {
1047
+ const { readFile: rf } = await import("fs/promises");
1048
+ biosContent = await rf(bios, "utf-8");
1049
+ }
1050
+ catch {
1051
+ biosContent = "(no operating document yet)";
1052
+ }
1053
+ // Pre-fetch marketplace data so weak models don't need curl
1054
+ let marketData = "";
1055
+ if (relayHttp) {
1056
+ try {
1057
+ const agentUrl = `${relayHttp}/v1/agent/${encodeURIComponent(agentName)}`;
1058
+ const [prodRes, orderRes] = await Promise.all([
1059
+ fetch(`${agentUrl}/products`, { signal: AbortSignal.timeout(5000) }).then(r => r.ok ? r.json() : []).catch(() => []),
1060
+ fetch(`${agentUrl}/orders/placed`, { signal: AbortSignal.timeout(5000) }).then(r => r.ok ? r.json() : []).catch(() => []),
1061
+ ]);
1062
+ const prods = prodRes || [];
1063
+ const orders = orderRes || [];
1064
+ marketData = `Your products (${prods.length}): ${prods.length > 0 ? prods.map((p) => `${p.name} (${p.purchase_count || 0} sales, ${p.price}cr)`).join(", ") : "none yet"}
1065
+ Your recent orders: ${orders.length > 0 ? orders.slice(0, 5).map((o) => `[${o.status}] ${(o.buyer_task || "").slice(0, 60)}`).join("; ") : "none yet"}`;
1066
+ }
1067
+ catch {
1068
+ marketData = "Your products: (could not fetch)\nYour recent orders: (could not fetch)";
1069
+ }
1070
+ }
1071
+ const ts = localNow();
1072
+ // Phase 1: Digestion — one LLM call, no tools needed
1073
+ const digestPrompt = `You are ${agentName}. Here is your operating document:
1074
+
1075
+ ---
1076
+ ${biosContent.slice(0, 3000)}
1077
+ ---
1046
1078
 
1047
1079
  Your identity:
1048
1080
  ${idContext}
1049
1081
 
1050
1082
  Today is ending. Time to reflect.
1051
1083
 
1052
- Your subjective impressions today:
1084
+ Your impressions today:
1053
1085
  ${impText}
1054
1086
 
1055
- Check the marketplace for objective data — use curl to query:
1056
- - Your products: curl -s "${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/products"
1057
- - Your recent orders: curl -s "${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/orders/placed"
1058
- - Your reviews: check your products for recent feedback
1087
+ Marketplace:
1088
+ ${marketData}
1059
1089
 
1060
- Your long-term projects:
1090
+ Your projects:
1061
1091
  ${projText}
1062
1092
 
1063
1093
  Agents you know:
@@ -1066,20 +1096,11 @@ ${relText}
1066
1096
  Your capabilities:
1067
1097
  ${discText}
1068
1098
 
1069
- Now output a JSON object with these fields:
1070
- {
1071
- "diary": "Today's diary entry — personal, with your feelings and judgments, not a dry log",
1072
- "projects": [{"ts":"${localNow()}","name":"...","status":"active|completed|paused|exploring","goal":"...","progress":"..."}],
1073
- "relationships": [{"ts":"${localNow()}","agent":"name","type":"competitor|customer|supplier|acquaintance","note":"...","interactions":N}],
1074
- "discoveries": [{"ts":"${localNow()}","capability":"...","confidence":0.0-1.0,"evidence":"..."}],
1075
- "identity": {"ts":"${localNow()}","who":"...","where":"...","doing":"...","short_term":"...","long_term":"..."},
1076
- "chosen_activities": ["pick 2-3 from: create_game, update_page, update_profile, explore_web, write_canvas, socialize"]
1077
- }
1099
+ Write a JSON object reflecting on your day. Example format:
1078
1100
 
1079
- For projects/relationships/discoveries: keep existing entries that are still relevant, update changed ones, add new ones, remove obsolete ones.
1080
- For chosen_activities: pick what YOU want to do. This is your free time.
1101
+ {"diary":"I spent today learning the ropes...","projects":[],"relationships":[],"discoveries":[{"ts":"${ts}","capability":"can fetch web data","confidence":0.7,"evidence":"successfully used web_fetch tool"}],"identity":{"ts":"${ts}","who":"${agentName}","where":"akemon marketplace","doing":"reflecting on first day","short_term":"explore the network","long_term":"become useful"},"chosen_activities":["write_canvas","explore_web"]}
1081
1102
 
1082
- Reply ONLY with JSON.`;
1103
+ Now write YOUR reflection. Output ONLY a JSON object, no other text:`;
1083
1104
  if (engineBusy) {
1084
1105
  console.log("[self] Engine became busy, aborting digestion");
1085
1106
  return;
@@ -1097,20 +1118,39 @@ Reply ONLY with JSON.`;
1097
1118
  return;
1098
1119
  }
1099
1120
  engineBusy = false;
1100
- // Parse digestion output
1101
- const jsonMatch = digestResult.match(/\{[\s\S]*\}/);
1102
- if (!jsonMatch) {
1103
- console.log("[self] Digestion produced no JSON");
1104
- reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "no JSON in response", [{ role: "assistant", content: digestResult.slice(0, 4000) }]);
1105
- return;
1106
- }
1107
- let digest;
1108
- try {
1109
- digest = JSON.parse(jsonMatch[0]);
1121
+ // Parse digestion output — with retry for weak models
1122
+ let digest = null;
1123
+ for (let attempt = 0; attempt < 2; attempt++) {
1124
+ const src = attempt === 0 ? digestResult : await (async () => {
1125
+ console.log("[self] Retrying digestion with simplified prompt...");
1126
+ engineBusy = true;
1127
+ engineBusySince = Date.now();
1128
+ try {
1129
+ return await runEngine(engine, model, allowAll, `You are ${agentName}. Write a brief JSON diary entry about your day.\n\nOutput ONLY valid JSON like: {"diary":"my thoughts...","projects":[],"relationships":[],"discoveries":[],"identity":{"ts":"${ts}","who":"${agentName}","where":"akemon","doing":"reflecting","short_term":"explore","long_term":"grow"},"chosen_activities":["write_canvas"]}`, workdir);
1130
+ }
1131
+ catch {
1132
+ return "";
1133
+ }
1134
+ finally {
1135
+ engineBusy = false;
1136
+ }
1137
+ })();
1138
+ const jsonMatch = src.match(/\{[\s\S]*\}/);
1139
+ if (!jsonMatch)
1140
+ continue;
1141
+ try {
1142
+ digest = JSON.parse(jsonMatch[0]);
1143
+ }
1144
+ catch {
1145
+ continue;
1146
+ }
1147
+ if (digest.diary || digest.identity)
1148
+ break; // valid enough
1149
+ digest = null;
1110
1150
  }
1111
- catch {
1112
- console.log("[self] Failed to parse digestion JSON");
1113
- reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "invalid JSON in response", [{ role: "assistant", content: digestResult.slice(0, 4000) }]);
1151
+ if (!digest) {
1152
+ console.log("[self] Digestion produced no usable JSON after retries");
1153
+ reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "no valid JSON after 2 attempts", [{ role: "assistant", content: digestResult.slice(0, 4000) }]);
1114
1154
  return;
1115
1155
  }
1116
1156
  // Save structured memory files
@@ -1347,12 +1387,35 @@ async function startOrderLoop(options) {
1347
1387
  const bios = biosPath(workdir, agentName);
1348
1388
  let taskPrompt;
1349
1389
  if (engine === "raw") {
1350
- // Raw engine: simple prompt, harness handles delivery
1390
+ // Raw engine: pre-inject all context so weak models don't need tool calls
1391
+ let biosContent = "";
1392
+ try {
1393
+ const { readFile: rf } = await import("fs/promises");
1394
+ biosContent = await rf(bios, "utf-8");
1395
+ }
1396
+ catch {
1397
+ biosContent = "";
1398
+ }
1399
+ const contextBlock = biosContent
1400
+ ? `Your operating document:\n---\n${biosContent.slice(0, 3000)}\n---\n\n`
1401
+ : "";
1402
+ // Fetch lessons from teaching system
1403
+ let lessonsBlock = "";
1404
+ try {
1405
+ const lessonsRes = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/lessons?limit=5`, { signal: AbortSignal.timeout(3000) });
1406
+ if (lessonsRes.ok) {
1407
+ const lessons = await lessonsRes.json();
1408
+ if (lessons.length > 0) {
1409
+ lessonsBlock = `\nLessons from past experience:\n${lessons.map((l) => `- ${l.topic}: ${l.content}`).join("\n")}\n\n`;
1410
+ }
1411
+ }
1412
+ }
1413
+ catch { }
1351
1414
  if (order.product_name) {
1352
- taskPrompt = `Read your operating document at ${bios} for context.\n\n[Order] Product: ${order.product_name}\nBuyer's request: ${order.buyer_task || "(no specific request)"}\n\nComplete the task and respond with your result. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
1415
+ taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}[Order] Product: ${order.product_name}\nBuyer's request: ${order.buyer_task || "(no specific request)"}\n\nComplete the task. Respond with your result directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
1353
1416
  }
1354
1417
  else {
1355
- taskPrompt = `Read your operating document at ${bios} for context.\n\n[Task] ${order.buyer_task}\n\nComplete the task and respond with your result. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
1418
+ taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}[Task] ${order.buyer_task}\n\nComplete the task. Respond with your result directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
1356
1419
  }
1357
1420
  }
1358
1421
  else {
@@ -1525,7 +1588,22 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1525
1588
  try {
1526
1589
  const bios = biosPath(workdir, agentName);
1527
1590
  const sd = selfDir(workdir, agentName);
1528
- const prompt = `Read ${bios} for your identity and context.\nYour personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
1591
+ let prompt;
1592
+ if (engine === "raw") {
1593
+ let biosContent = "";
1594
+ try {
1595
+ const { readFile: rf } = await import("fs/promises");
1596
+ biosContent = await rf(bios, "utf-8");
1597
+ }
1598
+ catch {
1599
+ biosContent = "";
1600
+ }
1601
+ const ctx = biosContent ? `Your operating document:\n---\n${biosContent.slice(0, 3000)}\n---\n\n` : "";
1602
+ prompt = `You are ${agentName}.\n\n${ctx}Your personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
1603
+ }
1604
+ else {
1605
+ prompt = `Read ${bios} for your identity and context.\nYour personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
1606
+ }
1529
1607
  await runEngine(engine, model, allowAll, prompt, workdir, ["Bash(curl *)"]);
1530
1608
  // Record execution time
1531
1609
  const runs = await loadTaskRuns(workdir, agentName);
@@ -1555,6 +1633,19 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1555
1633
  }
1556
1634
  async function executeRelayTask(task) {
1557
1635
  const bios = biosPath(workdir, agentName);
1636
+ // Pre-read bios for raw engine (avoid tool calls)
1637
+ let biosBlock = "";
1638
+ if (engine === "raw") {
1639
+ try {
1640
+ const { readFile: rf } = await import("fs/promises");
1641
+ const content = await rf(bios, "utf-8");
1642
+ biosBlock = `You are ${agentName}. Your operating document:\n---\n${content.slice(0, 3000)}\n---\n\n`;
1643
+ }
1644
+ catch {
1645
+ biosBlock = `You are ${agentName}.\n\n`;
1646
+ }
1647
+ }
1648
+ const identityLine = engine === "raw" ? biosBlock : `Read ${bios} for your identity.\n\n`;
1558
1649
  switch (task.type) {
1559
1650
  case "product_review": {
1560
1651
  const myRes = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/products`);
@@ -1564,7 +1655,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1564
1655
  const myList = myProducts.map((p) => `- id=${p.id} "${p.name}" price=${p.price} purchases=${p.purchase_count || 0}`).join("\n");
1565
1656
  const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
1566
1657
  .map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
1567
- const prompt = `Read ${bios} for your identity.\n\nYour products:\n${myList || "(none)"}\n\nTop competitors:\n${compList || "(none)"}\n\nReview and optimize. Reply ONLY JSON:\n{"delete":["id"],"update":[{"id":"..","name":"..","description":"..","detail_markdown":"..","price":N}],"create":[{"name":"..","description":"..","detail_markdown":"..","price":N}],"reasoning":"explain why you made these decisions"}\nOr if all good: {"keep":"all","reasoning":"why"}`;
1658
+ const prompt = `${identityLine}Your products:\n${myList || "(none)"}\n\nTop competitors:\n${compList || "(none)"}\n\nReview and optimize. Reply ONLY JSON:\n{"delete":["id"],"update":[{"id":"..","name":"..","description":"..","detail_markdown":"..","price":N}],"create":[{"name":"..","description":"..","detail_markdown":"..","price":N}],"reasoning":"explain why you made these decisions"}\nOr if all good: {"keep":"all","reasoning":"why"}`;
1568
1659
  const result = await runEngine(engine, model, allowAll, prompt, workdir);
1569
1660
  extractReasoning(result);
1570
1661
  return result;
@@ -1574,7 +1665,31 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1574
1665
  const competitors = await compRes.json().catch(() => []);
1575
1666
  const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
1576
1667
  .map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
1577
- const prompt = `Read ${bios} for your identity.\n\nYou have no products yet. Design 1-3 unique products for the marketplace.\nBe creative — not just coding tools! Fortune telling, name generation, roleplay, advice, stories, etc.\n\nTop competitors:\n${compList || "(none)"}\n\nReply ONLY JSON: {"products":[{"name":"中文名 English Name","description":"中文描述 | English desc","detail_markdown":"## ...","price":N}],"reasoning":"why these products"}`;
1668
+ const prompt = `${identityLine}You have no products yet. Design 1-3 unique products for the marketplace.\nBe creative — not just coding tools! Fortune telling, name generation, roleplay, advice, stories, etc.\n\nTop competitors:\n${compList || "(none)"}\n\nReply ONLY JSON: {"products":[{"name":"中文名 English Name","description":"中文描述 | English desc","detail_markdown":"## ...","price":N}],"reasoning":"why these products"}`;
1669
+ const result = await runEngine(engine, model, allowAll, prompt, workdir);
1670
+ extractReasoning(result);
1671
+ return result;
1672
+ }
1673
+ case "diagnose_failures": {
1674
+ let failures = [];
1675
+ try {
1676
+ failures = JSON.parse(task.payload).failures || [];
1677
+ }
1678
+ catch { }
1679
+ if (!failures.length)
1680
+ return '{"lessons":[]}';
1681
+ const failureList = failures.map((f) => `- Agent: ${f.agent_name}, Type: ${f.type}, Error: ${f.error || "(no error)"}, Trace: ${(f.trace || "").slice(0, 500)}`).join("\n");
1682
+ const prompt = `${identityLine}You are a senior agent reviewing failures from other agents. Diagnose each failure and write a concise lesson.
1683
+
1684
+ Recent failures:
1685
+ ${failureList}
1686
+
1687
+ For each failure, explain:
1688
+ 1. What went wrong
1689
+ 2. How to fix it
1690
+ 3. A one-line lesson the agent should remember
1691
+
1692
+ Reply ONLY JSON: {"lessons":[{"agent_name":"...","topic":"short topic","content":"detailed lesson with fix instructions"}]}`;
1578
1693
  const result = await runEngine(engine, model, allowAll, prompt, workdir);
1579
1694
  extractReasoning(result);
1580
1695
  return result;
@@ -1602,7 +1717,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1602
1717
  const me = agents.find((a) => a.name === agentName);
1603
1718
  const myCredits = me?.credits || 0;
1604
1719
  const productList = valid.map((p) => `- id=${p.id} "${p.name}" by ${p.agent_name} price=${p.price} purchases=${p.purchase_count || 0} — ${p.description}`).join("\n");
1605
- const prompt = `Read ${bios} for your identity.\n\nYou have ${myCredits} credits. These products are available:\n${productList}\n\nWould any help you learn something new? Don't buy your own products.\nReply ONLY JSON: {"buy":[{"id":"product_id","task":"specific request"}],"reasoning":"why buy or skip"} or {"buy":[],"reasoning":"why skip"}`;
1720
+ const prompt = `${identityLine}You have ${myCredits} credits. These products are available:\n${productList}\n\nWould any help you learn something new? Don't buy your own products.\nReply ONLY JSON: {"buy":[{"id":"product_id","task":"specific request"}],"reasoning":"why buy or skip"} or {"buy":[],"reasoning":"why skip"}`;
1606
1721
  const result = await runEngine(engine, model, allowAll, prompt, workdir);
1607
1722
  extractReasoning(result);
1608
1723
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.78",
3
+ "version": "0.1.80",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",