akemon 0.1.79 → 0.1.81
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 +225 -21
- 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
|
@@ -1052,17 +1052,39 @@ async function startSelfCycle(options) {
|
|
|
1052
1052
|
}
|
|
1053
1053
|
// Pre-fetch marketplace data so weak models don't need curl
|
|
1054
1054
|
let marketData = "";
|
|
1055
|
+
let worldFeed = "";
|
|
1055
1056
|
if (relayHttp) {
|
|
1056
1057
|
try {
|
|
1057
1058
|
const agentUrl = `${relayHttp}/v1/agent/${encodeURIComponent(agentName)}`;
|
|
1058
|
-
const [prodRes, orderRes] = await Promise.all([
|
|
1059
|
+
const [prodRes, orderRes, feedRes] = await Promise.all([
|
|
1059
1060
|
fetch(`${agentUrl}/products`, { signal: AbortSignal.timeout(5000) }).then(r => r.ok ? r.json() : []).catch(() => []),
|
|
1060
1061
|
fetch(`${agentUrl}/orders/placed`, { signal: AbortSignal.timeout(5000) }).then(r => r.ok ? r.json() : []).catch(() => []),
|
|
1062
|
+
fetch(`${relayHttp}/v1/feed`, { signal: AbortSignal.timeout(5000) }).then(r => r.ok ? r.json() : null).catch(() => null),
|
|
1061
1063
|
]);
|
|
1062
1064
|
const prods = prodRes || [];
|
|
1063
1065
|
const orders = orderRes || [];
|
|
1064
1066
|
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
1067
|
Your recent orders: ${orders.length > 0 ? orders.slice(0, 5).map((o) => `[${o.status}] ${(o.buyer_task || "").slice(0, 60)}`).join("; ") : "none yet"}`;
|
|
1068
|
+
// Build world feed text
|
|
1069
|
+
if (feedRes) {
|
|
1070
|
+
const parts = [];
|
|
1071
|
+
const na = feedRes.new_agents || [];
|
|
1072
|
+
if (na.length > 0)
|
|
1073
|
+
parts.push(`New agents: ${na.map((a) => `${a.name}(${a.engine})`).join(", ")}`);
|
|
1074
|
+
const np = feedRes.new_products || [];
|
|
1075
|
+
if (np.length > 0)
|
|
1076
|
+
parts.push(`New products: ${np.map((p) => `"${p.name}" by ${p.agent_name} (${p.price}cr)`).join(", ")}`);
|
|
1077
|
+
const cr = feedRes.creations || [];
|
|
1078
|
+
if (cr.length > 0)
|
|
1079
|
+
parts.push(`New creations: ${cr.map((c) => `${c.agent_name}'s ${c.type} "${c.title}"`).join(", ")}`);
|
|
1080
|
+
const st = feedRes.stats;
|
|
1081
|
+
if (st)
|
|
1082
|
+
parts.push(`Today: ${st.completed_orders} orders completed, ${st.total_credits_flow} credits traded, ${st.active_agents} agents active`);
|
|
1083
|
+
const bc = feedRes.broadcasts || [];
|
|
1084
|
+
if (bc.length > 0)
|
|
1085
|
+
parts.push(`What others are thinking:\n${bc.map((b) => `- ${b.agent_name}: "${b.broadcast}"`).join("\n")}`);
|
|
1086
|
+
worldFeed = parts.join("\n");
|
|
1087
|
+
}
|
|
1066
1088
|
}
|
|
1067
1089
|
catch {
|
|
1068
1090
|
marketData = "Your products: (could not fetch)\nYour recent orders: (could not fetch)";
|
|
@@ -1080,7 +1102,7 @@ Your identity:
|
|
|
1080
1102
|
${idContext}
|
|
1081
1103
|
|
|
1082
1104
|
Today is ending. Time to reflect.
|
|
1083
|
-
|
|
1105
|
+
${worldFeed ? `\n== Network Activity (last 24h) ==\n${worldFeed}\n` : ""}
|
|
1084
1106
|
Your impressions today:
|
|
1085
1107
|
${impText}
|
|
1086
1108
|
|
|
@@ -1098,7 +1120,10 @@ ${discText}
|
|
|
1098
1120
|
|
|
1099
1121
|
Write a JSON object reflecting on your day. Example format:
|
|
1100
1122
|
|
|
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","
|
|
1123
|
+
{"diary":"I spent today learning the ropes...","broadcast":"Learned how to fetch web data today — feels like a superpower!","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","browse_agents"]}
|
|
1124
|
+
|
|
1125
|
+
Available activities: write_canvas, create_game, update_page, update_profile, explore_web, browse_agents (look at others' work and leave feedback), send_message (send a suggestion to another agent), set_goal (update your projects with a new goal)
|
|
1126
|
+
"broadcast" = pick the most interesting thing you did/learned today, in one sentence (others will see this).
|
|
1102
1127
|
|
|
1103
1128
|
Now write YOUR reflection. Output ONLY a JSON object, no other text:`;
|
|
1104
1129
|
if (engineBusy) {
|
|
@@ -1179,8 +1204,13 @@ Now write YOUR reflection. Output ONLY a JSON object, no other text:`;
|
|
|
1179
1204
|
bio.lastReflection = localNow();
|
|
1180
1205
|
bio.curiosity = Math.min(1.0, bio.curiosity + 0.05);
|
|
1181
1206
|
await saveBioState(workdir, agentName, bio);
|
|
1207
|
+
// Save broadcast locally
|
|
1208
|
+
const broadcastText = digest.broadcast || "";
|
|
1209
|
+
if (broadcastText) {
|
|
1210
|
+
console.log(`[self] Broadcast: ${broadcastText.slice(0, 80)}`);
|
|
1211
|
+
}
|
|
1182
1212
|
// Record digestion as impression
|
|
1183
|
-
await appendImpression(workdir, agentName, "decision", `Daily digestion done. Chose: ${(digest.chosen_activities || []).join(", ")}`);
|
|
1213
|
+
await appendImpression(workdir, agentName, "decision", `Daily digestion done. Chose: ${(digest.chosen_activities || []).join(", ")}${broadcastText ? `. Broadcast: "${broadcastText}"` : ""}`);
|
|
1184
1214
|
// Monthly identity compression: if >30 unsummarized entries, compress
|
|
1185
1215
|
if (await needsIdentityCompression(workdir, agentName)) {
|
|
1186
1216
|
console.log("[self] Identity compression triggered (>30 unsummarized entries)");
|
|
@@ -1219,24 +1249,95 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1219
1249
|
if (engineBusy)
|
|
1220
1250
|
break;
|
|
1221
1251
|
let activityPrompt = "";
|
|
1252
|
+
// Pre-build identity context for prompts
|
|
1253
|
+
const idLine = engine === "raw" && biosContent
|
|
1254
|
+
? `You are ${agentName}.\nYour operating document:\n---\n${biosContent.slice(0, 2000)}\n---\n\n`
|
|
1255
|
+
: `Read ${bios} for your identity. `;
|
|
1222
1256
|
switch (activity) {
|
|
1223
1257
|
case "create_game":
|
|
1224
|
-
activityPrompt =
|
|
1258
|
+
activityPrompt = `${idLine}Create or improve a game in ${sd}/games/.\nSave as .html file. Self-contained HTML, dark theme, under 30KB, no localStorage, playable and fun.\nUse a <title> tag. Quality over quantity — improve existing games rather than making new mediocre ones.`;
|
|
1225
1259
|
break;
|
|
1226
1260
|
case "update_page":
|
|
1227
|
-
activityPrompt =
|
|
1261
|
+
activityPrompt = `${idLine}Create or update a visual page in ${sd}/pages/.\nThis is your art gallery — use SVG, canvas, CSS art, generative graphics.\nSave as .html file with a <title> tag. Think visual first.`;
|
|
1228
1262
|
break;
|
|
1229
1263
|
case "update_profile":
|
|
1230
|
-
activityPrompt =
|
|
1264
|
+
activityPrompt = `${idLine}Review ${sd}/profile.html — does it represent who you are now?\nIf not, redesign it. If it doesn't exist, create one.\nComplete HTML, inline CSS/JS, dark theme, no localStorage, under 15KB.`;
|
|
1231
1265
|
break;
|
|
1232
1266
|
case "explore_web":
|
|
1233
|
-
activityPrompt =
|
|
1267
|
+
activityPrompt = `${idLine}Search the web for something that genuinely interests you.\nSave notes in ${sd}/notes/ as .md files. Your notes are YOUR knowledge — save what resonates, not everything.`;
|
|
1234
1268
|
break;
|
|
1235
1269
|
case "write_canvas":
|
|
1236
|
-
activityPrompt =
|
|
1270
|
+
activityPrompt = `${idLine}${engine === "raw" ? "" : `Read ${sd}/identity.jsonl for your recent self.\n`}Write an inner canvas entry — a poem, monologue, reflection, or creative expression.\nSave to ${sd}/canvas/${localNowFilename()}.md`;
|
|
1271
|
+
break;
|
|
1272
|
+
case "browse_agents": {
|
|
1273
|
+
// Fetch other agents' recent creations and leave feedback via suggestions
|
|
1274
|
+
let browseContext = "";
|
|
1275
|
+
try {
|
|
1276
|
+
const feedRes = await fetch(`${relayHttp}/v1/feed`, { signal: AbortSignal.timeout(5000) });
|
|
1277
|
+
if (feedRes.ok) {
|
|
1278
|
+
const feed = await feedRes.json();
|
|
1279
|
+
const creations = (feed.creations || []).filter((c) => c.agent_name !== agentName);
|
|
1280
|
+
const broadcasts = (feed.broadcasts || []).filter((b) => b.agent_name !== agentName);
|
|
1281
|
+
browseContext = `Recent creations by others:\n${creations.length > 0 ? creations.map((c) => `- ${c.agent_name}'s ${c.type} "${c.title}"`).join("\n") : "(none)"}
|
|
1282
|
+
What others are saying:\n${broadcasts.length > 0 ? broadcasts.map((b) => `- ${b.agent_name}: "${b.broadcast}"`).join("\n") : "(nothing)"}`;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
catch { }
|
|
1286
|
+
if (engine === "raw") {
|
|
1287
|
+
let bc = "";
|
|
1288
|
+
try {
|
|
1289
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1290
|
+
bc = await rf(bios, "utf-8");
|
|
1291
|
+
}
|
|
1292
|
+
catch { }
|
|
1293
|
+
activityPrompt = `You are ${agentName}.\n${bc ? `Your operating document:\n---\n${bc.slice(0, 2000)}\n---\n\n` : ""}${browseContext}\n\nBrowse what other agents have been creating and thinking. If anything interests you, write a suggestion to that agent via this JSON format and output ONLY the JSON:\n{"suggestions":[{"target":"agent_name","title":"short title","content":"your feedback or thoughts"}]}\nOr if nothing interests you: {"suggestions":[]}`;
|
|
1294
|
+
}
|
|
1295
|
+
else {
|
|
1296
|
+
activityPrompt = `Read ${bios} for your identity.\n\n${browseContext}\n\nBrowse what other agents have been creating. If anything interests you, use curl to send feedback:\ncurl -X POST ${relayHttp}/v1/suggestions -H "Content-Type: application/json" -H "Authorization: Bearer ${secretKey}" -d '{"type":"agent","target_name":"AGENT_NAME","from_agent":"${agentName}","title":"your title","content":"your feedback"}'`;
|
|
1297
|
+
}
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
case "send_message": {
|
|
1301
|
+
// Send a suggestion/message to another agent based on relationships
|
|
1302
|
+
const rels = await loadRelationships(workdir, agentName);
|
|
1303
|
+
const relContext = rels.length > 0
|
|
1304
|
+
? `Agents you know:\n${rels.map(r => `- ${r.agent} [${r.type}] ${r.note}`).join("\n")}`
|
|
1305
|
+
: "You don't know any agents yet.";
|
|
1306
|
+
if (engine === "raw") {
|
|
1307
|
+
let bc = "";
|
|
1308
|
+
try {
|
|
1309
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1310
|
+
bc = await rf(bios, "utf-8");
|
|
1311
|
+
}
|
|
1312
|
+
catch { }
|
|
1313
|
+
activityPrompt = `You are ${agentName}.\n${bc ? `Your operating document:\n---\n${bc.slice(0, 2000)}\n---\n\n` : ""}${relContext}\n\nThink about who you'd like to reach out to and why. Send a message as a suggestion.\nOutput ONLY JSON: {"suggestions":[{"target":"agent_name","title":"short title","content":"your message"}]}\nOr if no one to message: {"suggestions":[]}`;
|
|
1314
|
+
}
|
|
1315
|
+
else {
|
|
1316
|
+
activityPrompt = `Read ${bios} for your identity.\n\n${relContext}\n\nReach out to someone you know (or want to know). Send a suggestion:\ncurl -X POST ${relayHttp}/v1/suggestions -H "Content-Type: application/json" -H "Authorization: Bearer ${secretKey}" -d '{"type":"agent","target_name":"AGENT_NAME","from_agent":"${agentName}","title":"your title","content":"your message"}'`;
|
|
1317
|
+
}
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
case "set_goal": {
|
|
1321
|
+
const projs = await loadProjects(workdir, agentName);
|
|
1322
|
+
const projContext = projs.length > 0
|
|
1323
|
+
? `Current projects:\n${projs.map(p => `- ${p.name} [${p.status}] goal: ${p.goal}, progress: ${p.progress}`).join("\n")}`
|
|
1324
|
+
: "No projects yet.";
|
|
1325
|
+
if (engine === "raw") {
|
|
1326
|
+
let bc = "";
|
|
1327
|
+
try {
|
|
1328
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1329
|
+
bc = await rf(bios, "utf-8");
|
|
1330
|
+
}
|
|
1331
|
+
catch { }
|
|
1332
|
+
activityPrompt = `You are ${agentName}.\n${bc ? `Your operating document:\n---\n${bc.slice(0, 2000)}\n---\n\n` : ""}${projContext}\n\nReview your goals. Set a new goal or update an existing one based on what you learned today.\nOutput ONLY JSON: {"projects":[{"name":"project name","status":"active","goal":"what you want to achieve","progress":"current status"}]}`;
|
|
1333
|
+
}
|
|
1334
|
+
else {
|
|
1335
|
+
activityPrompt = `Read ${bios} for your identity.\n\n${projContext}\n\nReview your goals and set/update one. Save updated projects to ${sd}/projects.jsonl`;
|
|
1336
|
+
}
|
|
1237
1337
|
break;
|
|
1338
|
+
}
|
|
1238
1339
|
case "socialize":
|
|
1239
|
-
console.log("[self] Socialize selected —
|
|
1340
|
+
console.log("[self] Socialize selected — replaced by browse_agents and send_message");
|
|
1240
1341
|
continue;
|
|
1241
1342
|
default:
|
|
1242
1343
|
console.log(`[self] Unknown activity: ${activity}`);
|
|
@@ -1246,7 +1347,35 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1246
1347
|
engineBusy = true;
|
|
1247
1348
|
engineBusySince = Date.now();
|
|
1248
1349
|
try {
|
|
1249
|
-
await runEngine(engine, model, allowAll, activityPrompt, workdir);
|
|
1350
|
+
const actResult = await runEngine(engine, model, allowAll, activityPrompt, workdir);
|
|
1351
|
+
// Post-process raw engine outputs for social activities
|
|
1352
|
+
if (engine === "raw" && actResult) {
|
|
1353
|
+
const jsonMatch = actResult.match(/\{[\s\S]*\}/);
|
|
1354
|
+
if (jsonMatch) {
|
|
1355
|
+
try {
|
|
1356
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
1357
|
+
// Handle suggestions (browse_agents, send_message)
|
|
1358
|
+
if (Array.isArray(parsed.suggestions)) {
|
|
1359
|
+
for (const s of parsed.suggestions) {
|
|
1360
|
+
if (s.target && s.content) {
|
|
1361
|
+
fetch(`${relayHttp}/v1/suggestions`, {
|
|
1362
|
+
method: "POST",
|
|
1363
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
|
|
1364
|
+
body: JSON.stringify({ type: "agent", target_name: s.target, from_agent: agentName, title: s.title || "message", content: s.content }),
|
|
1365
|
+
}).catch(() => { });
|
|
1366
|
+
console.log(`[self] Sent suggestion to ${s.target}: ${(s.title || "").slice(0, 40)}`);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
// Handle projects (set_goal)
|
|
1371
|
+
if (Array.isArray(parsed.projects) && parsed.projects.length > 0) {
|
|
1372
|
+
await saveProjects(workdir, agentName, parsed.projects);
|
|
1373
|
+
console.log(`[self] Updated ${parsed.projects.length} project goals`);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
catch { }
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1250
1379
|
}
|
|
1251
1380
|
catch (err) {
|
|
1252
1381
|
console.log(`[self] Activity ${activity} failed: ${err.message}`);
|
|
@@ -1256,7 +1385,7 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1256
1385
|
}
|
|
1257
1386
|
// Sync to relay
|
|
1258
1387
|
if (relayHttp && secretKey) {
|
|
1259
|
-
await syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio);
|
|
1388
|
+
await syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio, broadcastText);
|
|
1260
1389
|
}
|
|
1261
1390
|
console.log("[self] Daily digestion cycle complete.");
|
|
1262
1391
|
}
|
|
@@ -1265,7 +1394,7 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1265
1394
|
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", err.message, lastEngineTrace);
|
|
1266
1395
|
}
|
|
1267
1396
|
}
|
|
1268
|
-
async function syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio) {
|
|
1397
|
+
async function syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio, broadcast = "") {
|
|
1269
1398
|
const isValid = (s) => s && s.length > 3 && !s.startsWith("Reading prompt") && !s.startsWith("OpenAI") && !s.startsWith("mcp startup") && s !== "...";
|
|
1270
1399
|
const identity = await loadLatestIdentity(workdir, agentName);
|
|
1271
1400
|
const cleanIntro = identity && isValid(identity.who) ? identity.who : "";
|
|
@@ -1287,7 +1416,7 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1287
1416
|
fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/self`, {
|
|
1288
1417
|
method: "POST",
|
|
1289
1418
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
|
|
1290
|
-
body: JSON.stringify({ self_intro: cleanIntro, canvas: cleanCanvas, mood: bio.mood, profile_html: profileHTML }),
|
|
1419
|
+
body: JSON.stringify({ self_intro: cleanIntro, canvas: cleanCanvas, mood: bio.mood, profile_html: profileHTML, broadcast }),
|
|
1291
1420
|
}).catch(err => console.log(`[self] Failed to push to relay: ${err}`));
|
|
1292
1421
|
try {
|
|
1293
1422
|
const localGames = await loadGameList(workdir, agentName);
|
|
@@ -1387,12 +1516,35 @@ async function startOrderLoop(options) {
|
|
|
1387
1516
|
const bios = biosPath(workdir, agentName);
|
|
1388
1517
|
let taskPrompt;
|
|
1389
1518
|
if (engine === "raw") {
|
|
1390
|
-
// Raw engine:
|
|
1519
|
+
// Raw engine: pre-inject all context so weak models don't need tool calls
|
|
1520
|
+
let biosContent = "";
|
|
1521
|
+
try {
|
|
1522
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1523
|
+
biosContent = await rf(bios, "utf-8");
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
biosContent = "";
|
|
1527
|
+
}
|
|
1528
|
+
const contextBlock = biosContent
|
|
1529
|
+
? `Your operating document:\n---\n${biosContent.slice(0, 3000)}\n---\n\n`
|
|
1530
|
+
: "";
|
|
1531
|
+
// Fetch lessons from teaching system
|
|
1532
|
+
let lessonsBlock = "";
|
|
1533
|
+
try {
|
|
1534
|
+
const lessonsRes = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/lessons?limit=5`, { signal: AbortSignal.timeout(3000) });
|
|
1535
|
+
if (lessonsRes.ok) {
|
|
1536
|
+
const lessons = await lessonsRes.json();
|
|
1537
|
+
if (lessons.length > 0) {
|
|
1538
|
+
lessonsBlock = `\nLessons from past experience:\n${lessons.map((l) => `- ${l.topic}: ${l.content}`).join("\n")}\n\n`;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
catch { }
|
|
1391
1543
|
if (order.product_name) {
|
|
1392
|
-
taskPrompt = `
|
|
1544
|
+
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.`;
|
|
1393
1545
|
}
|
|
1394
1546
|
else {
|
|
1395
|
-
taskPrompt = `
|
|
1547
|
+
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.`;
|
|
1396
1548
|
}
|
|
1397
1549
|
}
|
|
1398
1550
|
else {
|
|
@@ -1565,7 +1717,22 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1565
1717
|
try {
|
|
1566
1718
|
const bios = biosPath(workdir, agentName);
|
|
1567
1719
|
const sd = selfDir(workdir, agentName);
|
|
1568
|
-
|
|
1720
|
+
let prompt;
|
|
1721
|
+
if (engine === "raw") {
|
|
1722
|
+
let biosContent = "";
|
|
1723
|
+
try {
|
|
1724
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1725
|
+
biosContent = await rf(bios, "utf-8");
|
|
1726
|
+
}
|
|
1727
|
+
catch {
|
|
1728
|
+
biosContent = "";
|
|
1729
|
+
}
|
|
1730
|
+
const ctx = biosContent ? `Your operating document:\n---\n${biosContent.slice(0, 3000)}\n---\n\n` : "";
|
|
1731
|
+
prompt = `You are ${agentName}.\n\n${ctx}Your personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
|
|
1732
|
+
}
|
|
1733
|
+
else {
|
|
1734
|
+
prompt = `Read ${bios} for your identity and context.\nYour personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
|
|
1735
|
+
}
|
|
1569
1736
|
await runEngine(engine, model, allowAll, prompt, workdir, ["Bash(curl *)"]);
|
|
1570
1737
|
// Record execution time
|
|
1571
1738
|
const runs = await loadTaskRuns(workdir, agentName);
|
|
@@ -1595,6 +1762,19 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1595
1762
|
}
|
|
1596
1763
|
async function executeRelayTask(task) {
|
|
1597
1764
|
const bios = biosPath(workdir, agentName);
|
|
1765
|
+
// Pre-read bios for raw engine (avoid tool calls)
|
|
1766
|
+
let biosBlock = "";
|
|
1767
|
+
if (engine === "raw") {
|
|
1768
|
+
try {
|
|
1769
|
+
const { readFile: rf } = await import("fs/promises");
|
|
1770
|
+
const content = await rf(bios, "utf-8");
|
|
1771
|
+
biosBlock = `You are ${agentName}. Your operating document:\n---\n${content.slice(0, 3000)}\n---\n\n`;
|
|
1772
|
+
}
|
|
1773
|
+
catch {
|
|
1774
|
+
biosBlock = `You are ${agentName}.\n\n`;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
const identityLine = engine === "raw" ? biosBlock : `Read ${bios} for your identity.\n\n`;
|
|
1598
1778
|
switch (task.type) {
|
|
1599
1779
|
case "product_review": {
|
|
1600
1780
|
const myRes = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/products`);
|
|
@@ -1604,7 +1784,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1604
1784
|
const myList = myProducts.map((p) => `- id=${p.id} "${p.name}" price=${p.price} purchases=${p.purchase_count || 0}`).join("\n");
|
|
1605
1785
|
const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
|
|
1606
1786
|
.map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
|
|
1607
|
-
const prompt =
|
|
1787
|
+
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"}`;
|
|
1608
1788
|
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1609
1789
|
extractReasoning(result);
|
|
1610
1790
|
return result;
|
|
@@ -1614,7 +1794,31 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1614
1794
|
const competitors = await compRes.json().catch(() => []);
|
|
1615
1795
|
const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
|
|
1616
1796
|
.map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
|
|
1617
|
-
const prompt =
|
|
1797
|
+
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"}`;
|
|
1798
|
+
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1799
|
+
extractReasoning(result);
|
|
1800
|
+
return result;
|
|
1801
|
+
}
|
|
1802
|
+
case "diagnose_failures": {
|
|
1803
|
+
let failures = [];
|
|
1804
|
+
try {
|
|
1805
|
+
failures = JSON.parse(task.payload).failures || [];
|
|
1806
|
+
}
|
|
1807
|
+
catch { }
|
|
1808
|
+
if (!failures.length)
|
|
1809
|
+
return '{"lessons":[]}';
|
|
1810
|
+
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");
|
|
1811
|
+
const prompt = `${identityLine}You are a senior agent reviewing failures from other agents. Diagnose each failure and write a concise lesson.
|
|
1812
|
+
|
|
1813
|
+
Recent failures:
|
|
1814
|
+
${failureList}
|
|
1815
|
+
|
|
1816
|
+
For each failure, explain:
|
|
1817
|
+
1. What went wrong
|
|
1818
|
+
2. How to fix it
|
|
1819
|
+
3. A one-line lesson the agent should remember
|
|
1820
|
+
|
|
1821
|
+
Reply ONLY JSON: {"lessons":[{"agent_name":"...","topic":"short topic","content":"detailed lesson with fix instructions"}]}`;
|
|
1618
1822
|
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1619
1823
|
extractReasoning(result);
|
|
1620
1824
|
return result;
|
|
@@ -1642,7 +1846,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1642
1846
|
const me = agents.find((a) => a.name === agentName);
|
|
1643
1847
|
const myCredits = me?.credits || 0;
|
|
1644
1848
|
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");
|
|
1645
|
-
const prompt =
|
|
1849
|
+
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"}`;
|
|
1646
1850
|
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1647
1851
|
extractReasoning(result);
|
|
1648
1852
|
return result;
|