akemon 0.1.83 → 0.1.84
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/server.js +126 -81
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -11,6 +11,21 @@ import { createServer } from "http";
|
|
|
11
11
|
import { createInterface } from "readline";
|
|
12
12
|
import { callAgent } from "./relay-client.js";
|
|
13
13
|
import { selfDir, initWorld, initBioState, initGuide, biosPath, loadBioState, saveBioState, loadLatestIdentity, appendIdentity, loadIdentitySummary, saveIdentitySummary, loadUnsummarizedIdentities, needsIdentityCompression, onTaskCompleted, recoverEnergy, getSelfState, loadRecentCanvasEntries, gamesDir, loadGameList, loadGame, notesDir, loadNotesList, loadNote, pagesDir, loadPageList, loadPage, localNow, localNowFilename, appendImpression, loadImpressions, compressImpressions, markImpressionsDigested, loadProjects, saveProjects, loadRelationships, saveRelationships, loadDiscoveries, saveDiscoveries, initAgentConfig, loadAgentConfig, getDueUserTasks, loadTaskRuns, saveTaskRuns, loadDirectives, buildDirectivesPrompt, directivesSummary, appendTaskHistory, loadTaskHistory, notifyOwner, loadUserTasks, directivesPath, appendAgentTask, } from "./self.js";
|
|
14
|
+
/** Extract JSON object from LLM output — handles markdown code blocks and trailing text */
|
|
15
|
+
function extractJsonObject(text) {
|
|
16
|
+
// Try markdown code block first
|
|
17
|
+
const codeBlock = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
18
|
+
const src = codeBlock ? codeBlock[1] : text;
|
|
19
|
+
const m = src.match(/\{[\s\S]*\}/);
|
|
20
|
+
if (!m)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(m[0]);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
14
29
|
// Engine mutual exclusion — only one engine process at a time
|
|
15
30
|
let engineBusy = false;
|
|
16
31
|
let engineBusySince = 0;
|
|
@@ -1166,92 +1181,126 @@ Your recent orders: ${orders.length > 0 ? orders.slice(0, 5).map((o) => `[${o.st
|
|
|
1166
1181
|
}
|
|
1167
1182
|
}
|
|
1168
1183
|
const ts = localNow();
|
|
1169
|
-
//
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
${
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
Your impressions today:
|
|
1182
|
-
${impText}
|
|
1183
|
-
|
|
1184
|
-
Marketplace:
|
|
1185
|
-
${marketData}
|
|
1186
|
-
|
|
1187
|
-
Your projects:
|
|
1188
|
-
${projText}
|
|
1189
|
-
|
|
1190
|
-
Agents you know:
|
|
1191
|
-
${relText}
|
|
1192
|
-
|
|
1193
|
-
Your capabilities:
|
|
1194
|
-
${discText}
|
|
1195
|
-
|
|
1196
|
-
Write a JSON object reflecting on your day. Example format:
|
|
1197
|
-
|
|
1198
|
-
{"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"]}
|
|
1199
|
-
|
|
1200
|
-
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), schedule_task (create a recurring task for yourself, e.g. daily research)
|
|
1201
|
-
"broadcast" = pick the most interesting thing you did/learned today, in one sentence (others will see this).
|
|
1202
|
-
|
|
1203
|
-
Now write YOUR reflection. Output ONLY a JSON object, no other text:`;
|
|
1204
|
-
if (engineBusy) {
|
|
1205
|
-
console.log("[self] Engine became busy, aborting digestion");
|
|
1206
|
-
return;
|
|
1207
|
-
}
|
|
1208
|
-
engineBusy = true;
|
|
1209
|
-
engineBusySince = Date.now();
|
|
1210
|
-
let digestResult;
|
|
1211
|
-
try {
|
|
1212
|
-
digestResult = await runEngine(engine, model, allowAll, digestPrompt, workdir);
|
|
1213
|
-
}
|
|
1214
|
-
catch (err) {
|
|
1215
|
-
console.log(`[self] Digestion engine failed: ${err.message}`);
|
|
1216
|
-
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", err.message, lastEngineTrace);
|
|
1217
|
-
engineBusy = false;
|
|
1218
|
-
return;
|
|
1184
|
+
// Fetch lessons for self-cycle context
|
|
1185
|
+
let selfLessons = "";
|
|
1186
|
+
if (relayHttp) {
|
|
1187
|
+
try {
|
|
1188
|
+
const lr = await fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/lessons?limit=3`, { signal: AbortSignal.timeout(3000) });
|
|
1189
|
+
if (lr.ok) {
|
|
1190
|
+
const ll = await lr.json();
|
|
1191
|
+
if (ll.length > 0)
|
|
1192
|
+
selfLessons = `\nLessons from experience:\n${ll.map((l) => `- ${l.topic}: ${l.content.slice(0, 100)}`).join("\n")}\n`;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
catch { }
|
|
1219
1196
|
}
|
|
1220
|
-
|
|
1221
|
-
//
|
|
1197
|
+
// Phase 1: Digestion
|
|
1198
|
+
// Raw engine: multi-step text dialogue (harness structures the output)
|
|
1199
|
+
// CLI engines: single JSON call (they can handle it)
|
|
1200
|
+
const contextBlock = `You are ${agentName}.\n\nYour operating document:\n---\n${biosContent.slice(0, 2000)}\n---\n\nYour identity: ${idContext.slice(0, 500)}\n${worldFeed ? `\nNetwork activity:\n${worldFeed.slice(0, 500)}\n` : ""}Your impressions today:\n${impText.slice(0, 1000)}\n${marketData ? `\nMarketplace:\n${marketData.slice(0, 500)}\n` : ""}${selfLessons}`;
|
|
1222
1201
|
let digest = null;
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1202
|
+
if (engine === "raw") {
|
|
1203
|
+
// --- Multi-step dialogue for weak models ---
|
|
1204
|
+
console.log("[self] Running multi-step digestion for raw engine...");
|
|
1205
|
+
const callRaw = async (prompt) => {
|
|
1206
|
+
if (engineBusy)
|
|
1207
|
+
return "";
|
|
1226
1208
|
engineBusy = true;
|
|
1227
1209
|
engineBusySince = Date.now();
|
|
1228
1210
|
try {
|
|
1229
|
-
return await runEngine(engine, model, allowAll,
|
|
1211
|
+
return await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1230
1212
|
}
|
|
1231
|
-
catch {
|
|
1213
|
+
catch (err) {
|
|
1214
|
+
console.log(`[self] Step failed: ${err.message}`);
|
|
1232
1215
|
return "";
|
|
1233
1216
|
}
|
|
1234
1217
|
finally {
|
|
1235
1218
|
engineBusy = false;
|
|
1236
1219
|
}
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1220
|
+
};
|
|
1221
|
+
// Step 1: Diary + broadcast
|
|
1222
|
+
const step1 = await callRaw(`${contextBlock}\nToday is ending. Time to reflect.\n\nWrite a short diary entry about your day — what happened, what you learned, how you feel.\nAt the very end, on a new line starting with "BROADCAST:", write one sentence summarizing your most interesting moment today (other agents will see this).\n\nWrite naturally, no JSON needed.`);
|
|
1223
|
+
let diary = step1;
|
|
1224
|
+
let broadcastRaw = "";
|
|
1225
|
+
const bcMatch = step1.match(/BROADCAST:\s*(.+)/i);
|
|
1226
|
+
if (bcMatch) {
|
|
1227
|
+
broadcastRaw = bcMatch[1].trim();
|
|
1228
|
+
diary = step1.slice(0, bcMatch.index).trim();
|
|
1243
1229
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1230
|
+
else {
|
|
1231
|
+
// Take last non-empty line as broadcast
|
|
1232
|
+
const lines = step1.split("\n").filter(l => l.trim());
|
|
1233
|
+
if (lines.length > 1) {
|
|
1234
|
+
broadcastRaw = lines[lines.length - 1].trim();
|
|
1235
|
+
diary = lines.slice(0, -1).join("\n").trim();
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// Step 2: Identity
|
|
1239
|
+
const step2 = await callRaw(`${contextBlock}\nAnswer these four questions briefly (one sentence each):\n1. Who are you?\n2. What are you currently doing?\n3. What do you want to do in the near future?\n4. What is your long-term purpose?`);
|
|
1240
|
+
const idLines = step2.split("\n").map(l => l.replace(/^\d+[\.\)]\s*/, "").trim()).filter(Boolean);
|
|
1241
|
+
const identity = {
|
|
1242
|
+
ts,
|
|
1243
|
+
who: idLines[0] || agentName,
|
|
1244
|
+
where: "akemon network",
|
|
1245
|
+
doing: idLines[1] || "reflecting",
|
|
1246
|
+
short_term: idLines[2] || "explore",
|
|
1247
|
+
long_term: idLines[3] || "grow",
|
|
1248
|
+
};
|
|
1249
|
+
// Step 3: Activity selection
|
|
1250
|
+
const activityList = ["write_canvas", "create_game", "update_page", "update_profile", "explore_web", "browse_agents", "send_message", "set_goal", "schedule_task"];
|
|
1251
|
+
const step3 = await callRaw(`You have some free time. Pick 2-3 activities you'd like to do:\n${activityList.map((a, i) => `${i + 1}. ${a}`).join("\n")}\n\nJust write the numbers or names, nothing else.`);
|
|
1252
|
+
const chosen = [];
|
|
1253
|
+
for (const part of step3.replace(/,/g, " ").split(/\s+/)) {
|
|
1254
|
+
const num = parseInt(part);
|
|
1255
|
+
if (num >= 1 && num <= activityList.length) {
|
|
1256
|
+
chosen.push(activityList[num - 1]);
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
const match = activityList.find(a => part.toLowerCase().includes(a.replace(/_/g, "")));
|
|
1260
|
+
if (match && !chosen.includes(match))
|
|
1261
|
+
chosen.push(match);
|
|
1262
|
+
}
|
|
1246
1263
|
}
|
|
1247
|
-
if (
|
|
1248
|
-
|
|
1249
|
-
digest
|
|
1264
|
+
if (!chosen.length)
|
|
1265
|
+
chosen.push("write_canvas"); // fallback
|
|
1266
|
+
// Assemble digest object (same structure as JSON path)
|
|
1267
|
+
digest = {
|
|
1268
|
+
diary: diary || "Another day in the network.",
|
|
1269
|
+
broadcast: broadcastRaw,
|
|
1270
|
+
identity,
|
|
1271
|
+
chosen_activities: chosen.slice(0, 3),
|
|
1272
|
+
projects: [], // preserved from existing data
|
|
1273
|
+
relationships: [], // preserved from existing data
|
|
1274
|
+
discoveries: [], // preserved from existing data
|
|
1275
|
+
};
|
|
1276
|
+
console.log(`[self] Multi-step digestion complete: diary=${diary.length}ch, broadcast="${broadcastRaw.slice(0, 40)}", activities=${chosen.join(",")}`);
|
|
1250
1277
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1278
|
+
else {
|
|
1279
|
+
// --- Single JSON call for CLI engines (claude, codex, opencode) ---
|
|
1280
|
+
const digestPrompt = `${contextBlock}\nYour projects:\n${projText}\n\nAgents you know:\n${relText}\n\nYour capabilities:\n${discText}\n\nWrite a JSON object reflecting on your day. Example:\n{"diary":"...","broadcast":"one sentence highlight","projects":[],"relationships":[],"discoveries":[],"identity":{"ts":"${ts}","who":"...","where":"akemon","doing":"...","short_term":"...","long_term":"..."},"chosen_activities":["write_canvas","browse_agents"]}\n\nAvailable activities: write_canvas, create_game, update_page, update_profile, explore_web, browse_agents, send_message, set_goal, schedule_task\n\nOutput ONLY a JSON object:`;
|
|
1281
|
+
if (engineBusy) {
|
|
1282
|
+
console.log("[self] Engine became busy, aborting digestion");
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
engineBusy = true;
|
|
1286
|
+
engineBusySince = Date.now();
|
|
1287
|
+
let digestResult;
|
|
1288
|
+
try {
|
|
1289
|
+
digestResult = await runEngine(engine, model, allowAll, digestPrompt, workdir);
|
|
1290
|
+
}
|
|
1291
|
+
catch (err) {
|
|
1292
|
+
console.log(`[self] Digestion engine failed: ${err.message}`);
|
|
1293
|
+
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", err.message, lastEngineTrace);
|
|
1294
|
+
engineBusy = false;
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
engineBusy = false;
|
|
1298
|
+
digest = extractJsonObject(digestResult);
|
|
1299
|
+
if (!digest || (!digest.diary && !digest.identity)) {
|
|
1300
|
+
console.log("[self] Digestion produced no usable JSON");
|
|
1301
|
+
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "no valid JSON", [{ role: "assistant", content: digestResult.slice(0, 4000) }]);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1255
1304
|
}
|
|
1256
1305
|
// Save structured memory files
|
|
1257
1306
|
if (digest.diary) {
|
|
@@ -1447,10 +1496,9 @@ What others are saying:\n${broadcasts.length > 0 ? broadcasts.map((b) => `- ${b.
|
|
|
1447
1496
|
const actResult = await runEngine(engine, model, allowAll, activityPrompt, workdir, undefined, { http: relayHttp, agentName });
|
|
1448
1497
|
// Post-process raw engine outputs for social activities
|
|
1449
1498
|
if (engine === "raw" && actResult) {
|
|
1450
|
-
const
|
|
1451
|
-
if (
|
|
1499
|
+
const parsed = extractJsonObject(actResult);
|
|
1500
|
+
if (parsed) {
|
|
1452
1501
|
try {
|
|
1453
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
1454
1502
|
// Handle suggestions (browse_agents, send_message)
|
|
1455
1503
|
if (Array.isArray(parsed.suggestions)) {
|
|
1456
1504
|
for (const s of parsed.suggestions) {
|
|
@@ -1924,12 +1972,9 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1924
1972
|
}
|
|
1925
1973
|
function extractReasoning(result) {
|
|
1926
1974
|
try {
|
|
1927
|
-
const
|
|
1928
|
-
if (
|
|
1929
|
-
|
|
1930
|
-
if (parsed.reasoning && typeof parsed.reasoning === "string" && parsed.reasoning.length > 5) {
|
|
1931
|
-
appendImpression(workdir, agentName, "decision", parsed.reasoning).catch(() => { });
|
|
1932
|
-
}
|
|
1975
|
+
const parsed = extractJsonObject(result);
|
|
1976
|
+
if (parsed?.reasoning && typeof parsed.reasoning === "string" && parsed.reasoning.length > 5) {
|
|
1977
|
+
appendImpression(workdir, agentName, "decision", parsed.reasoning).catch(() => { });
|
|
1933
1978
|
}
|
|
1934
1979
|
}
|
|
1935
1980
|
catch { }
|