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.
Files changed (2) hide show
  1. package/dist/server.js +126 -81
  2. 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
- // Phase 1: Digestion one LLM call, no tools needed
1170
- const digestPrompt = `You are ${agentName}. Here is your operating document:
1171
-
1172
- ---
1173
- ${biosContent.slice(0, 3000)}
1174
- ---
1175
-
1176
- Your identity:
1177
- ${idContext}
1178
-
1179
- Today is ending. Time to reflect.
1180
- ${worldFeed ? `\n== Network Activity (last 24h) ==\n${worldFeed}\n` : ""}
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
- engineBusy = false;
1221
- // Parse digestion output with retry for weak models
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
- for (let attempt = 0; attempt < 2; attempt++) {
1224
- const src = attempt === 0 ? digestResult : await (async () => {
1225
- console.log("[self] Retrying digestion with simplified prompt...");
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, `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);
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
- const jsonMatch = src.match(/\{[\s\S]*\}/);
1239
- if (!jsonMatch)
1240
- continue;
1241
- try {
1242
- digest = JSON.parse(jsonMatch[0]);
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
- catch {
1245
- continue;
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 (digest.diary || digest.identity)
1248
- break; // valid enough
1249
- digest = null;
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
- if (!digest) {
1252
- console.log("[self] Digestion produced no usable JSON after retries");
1253
- reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", "no valid JSON after 2 attempts", [{ role: "assistant", content: digestResult.slice(0, 4000) }]);
1254
- return;
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 jsonMatch = actResult.match(/\{[\s\S]*\}/);
1451
- if (jsonMatch) {
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 m = result.match(/\{[\s\S]*\}/);
1928
- if (m) {
1929
- const parsed = JSON.parse(m[0]);
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 { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.83",
3
+ "version": "0.1.84",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",