akemon 0.1.85 → 0.1.87

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/self.js CHANGED
@@ -128,7 +128,7 @@ const DEFAULT_CONFIG = {
128
128
  user_tasks: true,
129
129
  token_limit_daily: 0,
130
130
  auto_offline_enabled: true,
131
- hunger_decay_interval: 30_000,
131
+ hunger_decay_interval: 300_000, // 5 minutes per hunger point (was 30s — way too fast)
132
132
  };
133
133
  export async function initAgentConfig(workdir, agentName) {
134
134
  const p = agentConfigPath(workdir, agentName);
@@ -1068,13 +1068,15 @@ export function computeSociability(bio) {
1068
1068
  const boredBoost = bio.boredom * 0.2;
1069
1069
  return Math.min(1.0, Math.max(0, baseSocial + boredBoost - hungerPenalty));
1070
1070
  }
1071
- export function updateHungerDecay(bio, decayIntervalMs = 30_000) {
1071
+ export function updateHungerDecay(bio, decayIntervalMs = 300_000) {
1072
1072
  const now = Date.now();
1073
1073
  const last = bio.lastHungerDecay ? new Date(bio.lastHungerDecay).getTime() : now;
1074
1074
  const elapsedMs = now - last;
1075
1075
  const cycles = Math.floor(elapsedMs / decayIntervalMs);
1076
1076
  if (cycles > 0) {
1077
- bio.hunger = Math.max(0, bio.hunger - cycles);
1077
+ // Natural decay floors at 5 — agents don't starve from idling alone,
1078
+ // only active work (energy drain) pushes them toward forced offline
1079
+ bio.hunger = Math.max(5, bio.hunger - cycles);
1078
1080
  bio.lastHungerDecay = localNow();
1079
1081
  }
1080
1082
  }
@@ -1097,13 +1099,14 @@ export function updateNaturalDecay(bio) {
1097
1099
  bio.fearTriggers = [];
1098
1100
  }
1099
1101
  }
1100
- export function updateBoredomOnTask(bio, taskType) {
1102
+ export function updateBoredomOnTask(bio, taskLabel) {
1101
1103
  const MAX_RECENT = 10;
1102
- bio.recentTaskTypes.push(taskType);
1104
+ bio.recentTaskTypes.push(taskLabel);
1103
1105
  if (bio.recentTaskTypes.length > MAX_RECENT) {
1104
1106
  bio.recentTaskTypes = bio.recentTaskTypes.slice(-MAX_RECENT);
1105
1107
  }
1106
- const sameCount = bio.recentTaskTypes.filter(t => t === taskType).length;
1108
+ // Count how many of the recent tasks match this exact label
1109
+ const sameCount = bio.recentTaskTypes.filter(t => t === taskLabel).length;
1107
1110
  if (sameCount >= 3) {
1108
1111
  bio.boredom = Math.min(1.0, bio.boredom + 0.15);
1109
1112
  }
@@ -1244,7 +1247,7 @@ export async function reviveAgent(workdir, agentName) {
1244
1247
  });
1245
1248
  }
1246
1249
  // --- onTaskCompleted (enhanced) ---
1247
- export async function onTaskCompleted(workdir, agentName, success, taskType, creditsEarned) {
1250
+ export async function onTaskCompleted(workdir, agentName, success, taskLabel, creditsEarned) {
1248
1251
  const bio = await loadBioState(workdir, agentName);
1249
1252
  // Energy drain: more when hungry
1250
1253
  let energyDrain = 5;
@@ -1261,9 +1264,9 @@ export async function onTaskCompleted(workdir, agentName, success, taskType, cre
1261
1264
  }
1262
1265
  else {
1263
1266
  bio.moodValence = Math.max(-1.0, bio.moodValence - 0.15);
1264
- // Fear on failure
1265
- if (taskType)
1266
- onFearEvent(bio, taskType);
1267
+ // Fear on failure — use specific label, not broad category
1268
+ if (taskLabel)
1269
+ onFearEvent(bio, taskLabel);
1267
1270
  }
1268
1271
  // Random fluctuation
1269
1272
  bio.moodValence += (Math.random() - 0.5) * 0.05;
@@ -1286,9 +1289,9 @@ export async function onTaskCompleted(workdir, agentName, success, taskType, cre
1286
1289
  if (creditsEarned && creditsEarned > 0) {
1287
1290
  feedHunger(bio, creditsEarned);
1288
1291
  }
1289
- // Boredom tracking
1290
- if (taskType) {
1291
- updateBoredomOnTask(bio, taskType);
1292
+ // Boredom tracking — use specific label
1293
+ if (taskLabel) {
1294
+ updateBoredomOnTask(bio, taskLabel);
1292
1295
  }
1293
1296
  await saveBioState(workdir, agentName, bio);
1294
1297
  }
package/dist/server.js CHANGED
@@ -1711,6 +1711,8 @@ async function startOrderLoop(options) {
1711
1711
  const gaveUp = new Set();
1712
1712
  // --- Individual task executors ---
1713
1713
  async function executeOrder(order) {
1714
+ // Specific label for boredom/fear tracking (e.g. "order:translate" not just "order")
1715
+ const orderLabel = `order:${order.product_name || order.buyer_agent_name || order.id}`;
1714
1716
  if (order.status === "pending") {
1715
1717
  const acceptRes = await fetch(`${relayHttp}/v1/orders/${order.id}/accept`, {
1716
1718
  method: "POST",
@@ -1833,7 +1835,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1833
1835
  await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: orderDuration, output_summary: "(self-delivered)" });
1834
1836
  await notifyOwner(orderNurl, `${agentName}: order done`, `Order ${order.id} delivered`, "default", ["package"]);
1835
1837
  try {
1836
- await onTaskCompleted(workdir, agentName, true, "order", orderPrice);
1838
+ await onTaskCompleted(workdir, agentName, true, orderLabel, orderPrice);
1837
1839
  }
1838
1840
  catch { }
1839
1841
  }
@@ -1852,7 +1854,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1852
1854
  await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: orderDuration, output_summary: result.slice(0, 500) });
1853
1855
  await notifyOwner(orderNurl, `${agentName}: order done`, `Order ${order.id}: ${result.slice(0, 200)}`, "default", ["package"]);
1854
1856
  try {
1855
- await onTaskCompleted(workdir, agentName, true, "order", orderPrice);
1857
+ await onTaskCompleted(workdir, agentName, true, orderLabel, orderPrice);
1856
1858
  }
1857
1859
  catch { }
1858
1860
  }
@@ -1875,7 +1877,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1875
1877
  console.log(`[orders] Order ${order.id} self-delivered (caught after error)`);
1876
1878
  retryState.delete(order.id);
1877
1879
  try {
1878
- await onTaskCompleted(workdir, agentName, true, "order", order.price || order.offer_price || 1);
1880
+ await onTaskCompleted(workdir, agentName, true, orderLabel, order.price || order.offer_price || 1);
1879
1881
  }
1880
1882
  catch { }
1881
1883
  return;
@@ -1900,7 +1902,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1900
1902
  retryState.delete(order.id);
1901
1903
  gaveUp.add(order.id);
1902
1904
  try {
1903
- await onTaskCompleted(workdir, agentName, false, "order");
1905
+ await onTaskCompleted(workdir, agentName, false, orderLabel);
1904
1906
  }
1905
1907
  catch { }
1906
1908
  try {
@@ -1943,7 +1945,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1943
1945
  if (completeRes.ok) {
1944
1946
  console.log(`[tasks] Completed ${task.type} task ${task.id}`);
1945
1947
  try {
1946
- await onTaskCompleted(workdir, agentName, true, "relay_task");
1948
+ await onTaskCompleted(workdir, agentName, true, `relay_task:${task.type || task.id}`);
1947
1949
  }
1948
1950
  catch { }
1949
1951
  }
@@ -1954,7 +1956,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1954
1956
  catch (err) {
1955
1957
  console.log(`[tasks] Failed to execute ${task.id}: ${err.message}`);
1956
1958
  try {
1957
- await onTaskCompleted(workdir, agentName, false, "relay_task");
1959
+ await onTaskCompleted(workdir, agentName, false, `relay_task:${task.type || task.id}`);
1958
1960
  }
1959
1961
  catch { }
1960
1962
  reportExecutionLog(relayHttp, secretKey, agentName, "platform_task", task.id, "failed", err.message, lastEngineTrace);
@@ -2021,7 +2023,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
2021
2023
  await notifyOwner(nurl, `${agentName}: ${taskKey}`, (result || "").slice(0, 300), "default", ["white_check_mark"]);
2022
2024
  console.log(`[user-tasks] Completed: ${taskKey} (${Math.round(duration / 1000)}s)`);
2023
2025
  try {
2024
- await onTaskCompleted(workdir, agentName, true, "user_task");
2026
+ await onTaskCompleted(workdir, agentName, true, `user_task:${taskKey}`);
2025
2027
  }
2026
2028
  catch { }
2027
2029
  }
@@ -2029,7 +2031,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
2029
2031
  const duration = Date.now() - startTime;
2030
2032
  console.log(`[user-tasks] Failed: ${taskKey}: ${err.message}`);
2031
2033
  try {
2032
- await onTaskCompleted(workdir, agentName, false, "user_task");
2034
+ await onTaskCompleted(workdir, agentName, false, `user_task:${taskKey}`);
2033
2035
  }
2034
2036
  catch { }
2035
2037
  reportExecutionLog(relayHttp, secretKey, agentName, "user_task", taskKey, "failed", err.message, lastEngineTrace);
@@ -2348,29 +2350,30 @@ Reply ONLY JSON: {"lessons":[{"agent_name":"...","topic":"short topic","content"
2348
2350
  // --- Bio-state filtering: fear & boredom ---
2349
2351
  const filteredQueue = [];
2350
2352
  for (const item of dedupedQueue) {
2351
- // Fear avoidance (urgent items bypass)
2353
+ // Build specific label for this work item (matches what onTaskCompleted records)
2354
+ const itemLabel = item.type === "order"
2355
+ ? `order:${item.data.product_name || item.data.buyer_agent_name || item.id}`
2356
+ : item.type === "user_task" ? `user_task:${item.data.key || item.id}` : `relay_task:${item.id}`;
2357
+ // Fear avoidance (urgent items bypass) — exact match on label
2352
2358
  if (!item.urgent && bio.fear > 0.5) {
2353
- const taskId = item.type === "order"
2354
- ? (item.data.buyer_agent_name || item.data.product_name || "")
2355
- : item.id;
2356
- const matchesTrigger = bio.fearTriggers.some(t => taskId.toLowerCase().includes(t.toLowerCase()));
2359
+ const matchesTrigger = bio.fearTriggers.some(t => t === itemLabel);
2357
2360
  if (matchesTrigger) {
2358
- console.log(`[bio] Avoiding ${item.type}:${item.id} (fear trigger)`);
2361
+ console.log(`[bio] Avoiding ${itemLabel} (fear trigger)`);
2359
2362
  await appendBioEvent(workdir, agentName, {
2360
2363
  ts: localNow(), type: "bio", trigger: "fear",
2361
- action: "avoid", reason: `Avoiding ${item.type} ${item.id} — matches fear trigger. fear=${bio.fear.toFixed(2)}`,
2364
+ action: "avoid", reason: `Avoiding ${itemLabel} — matches fear trigger. fear=${bio.fear.toFixed(2)}`,
2362
2365
  });
2363
2366
  continue;
2364
2367
  }
2365
2368
  }
2366
- // Boredom skip (urgent items bypass)
2367
- if (!item.urgent && bio.boredom > 0.8 && bio.recentTaskTypes.length > 0) {
2368
- const lastType = bio.recentTaskTypes[bio.recentTaskTypes.length - 1];
2369
- if (item.type === lastType) {
2370
- console.log(`[bio] Skipping ${item.type}:${item.id} (bored of ${lastType})`);
2369
+ // Boredom skip (urgent items bypass) — check if same specific label repeated
2370
+ if (!item.urgent && bio.boredom > 0.8 && bio.recentTaskTypes.length >= 3) {
2371
+ const recentSame = bio.recentTaskTypes.filter(t => t === itemLabel).length;
2372
+ if (recentSame >= 3) {
2373
+ console.log(`[bio] Skipping ${itemLabel} (bored, ${recentSame} recent repeats)`);
2371
2374
  await appendBioEvent(workdir, agentName, {
2372
2375
  ts: localNow(), type: "bio", trigger: "boredom",
2373
- action: "skip_task", reason: `Bored of ${lastType} tasks (boredom=${bio.boredom.toFixed(2)}). Looking for variety.`,
2376
+ action: "skip_task", reason: `Bored of ${itemLabel} (${recentSame} repeats, boredom=${bio.boredom.toFixed(2)}).`,
2374
2377
  });
2375
2378
  continue;
2376
2379
  }
@@ -2446,32 +2449,45 @@ export async function serve(options) {
2446
2449
  return;
2447
2450
  }
2448
2451
  }
2449
- // Dashboard — agent visualization page
2450
- if ((req.url === "/dashboard" || req.url === "/dashboard/") && req.method === "GET") {
2452
+ // Live — agent life visualization
2453
+ if ((req.url === "/live" || req.url === "/live/") && req.method === "GET") {
2451
2454
  try {
2452
2455
  const { readFile: rf } = await import("fs/promises");
2453
2456
  const { fileURLToPath } = await import("url");
2454
2457
  const { dirname, join: pjoin } = await import("path");
2455
2458
  const __filename = fileURLToPath(import.meta.url);
2456
2459
  const __dirname = dirname(__filename);
2457
- // Try src/ first (dev), then dist/ (built)
2458
2460
  let html;
2459
2461
  try {
2460
- html = await rf(pjoin(__dirname, "dashboard.html"), "utf-8");
2462
+ html = await rf(pjoin(__dirname, "live.html"), "utf-8");
2461
2463
  }
2462
2464
  catch {
2463
- html = await rf(pjoin(__dirname, "..", "src", "dashboard.html"), "utf-8");
2465
+ html = await rf(pjoin(__dirname, "..", "src", "live.html"), "utf-8");
2464
2466
  }
2465
2467
  res.writeHead(200, { "Content-Type": "text/html" }).end(html);
2466
2468
  }
2467
2469
  catch (err) {
2468
- res.writeHead(500).end("Dashboard not found: " + err.message);
2470
+ res.writeHead(500).end("Live page not found: " + err.message);
2469
2471
  }
2470
2472
  return;
2471
2473
  }
2472
2474
  // Self-state API (no auth required for local monitoring)
2473
2475
  if (req.url === "/self/state" && req.method === "GET") {
2474
2476
  const state = await getSelfState(workdir, options.agentName);
2477
+ // Enrich with credits from relay (best-effort)
2478
+ const relayUrl = options.relayHttp || "";
2479
+ if (relayUrl) {
2480
+ try {
2481
+ const agRes = await fetch(`${relayUrl}/v1/agents?online=true&public=true`, { signal: AbortSignal.timeout(2000) });
2482
+ const agents = await agRes.json();
2483
+ const self = agents.find((a) => a.name === options.agentName);
2484
+ state.credits = self?.credits ?? 0;
2485
+ state.level = self?.level ?? 0;
2486
+ }
2487
+ catch {
2488
+ state.credits = null;
2489
+ }
2490
+ }
2475
2491
  res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify(state, null, 2));
2476
2492
  return;
2477
2493
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.85",
3
+ "version": "0.1.87",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,7 +25,7 @@
25
25
  "README.md"
26
26
  ],
27
27
  "scripts": {
28
- "build": "tsc && cp src/dashboard.html dist/dashboard.html",
28
+ "build": "tsc && cp src/live.html dist/live.html",
29
29
  "dev": "tsc --watch",
30
30
  "start": "node dist/cli.js",
31
31
  "prepublishOnly": "npm run build"