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 +4 -0
- package/dist/relay-client.js +3 -0
- package/dist/server.js +155 -40
- package/package.json +1 -1
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
|
});
|
package/dist/relay-client.js
CHANGED
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
|
-
//
|
|
1045
|
-
|
|
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
|
|
1084
|
+
Your impressions today:
|
|
1053
1085
|
${impText}
|
|
1054
1086
|
|
|
1055
|
-
|
|
1056
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
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
|
-
|
|
1112
|
-
console.log("[self]
|
|
1113
|
-
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "
|
|
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:
|
|
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 = `
|
|
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 = `
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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;
|