akemon 0.1.61 → 0.1.62

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 (3) hide show
  1. package/dist/self.js +146 -0
  2. package/dist/server.js +232 -129
  3. package/package.json +1 -1
package/dist/self.js CHANGED
@@ -54,6 +54,18 @@ export function guidePath(workdir, agentName) {
54
54
  export function biosPath(workdir, agentName) {
55
55
  return join(selfDir(workdir, agentName), "bios.md");
56
56
  }
57
+ function impressionsPath(workdir, agentName) {
58
+ return join(selfDir(workdir, agentName), "impressions.jsonl");
59
+ }
60
+ function projectsPath(workdir, agentName) {
61
+ return join(selfDir(workdir, agentName), "projects.jsonl");
62
+ }
63
+ function relationshipsPath(workdir, agentName) {
64
+ return join(selfDir(workdir, agentName), "relationships.jsonl");
65
+ }
66
+ function discoveriesPath(workdir, agentName) {
67
+ return join(selfDir(workdir, agentName), "discoveries.jsonl");
68
+ }
57
69
  // ---------------------------------------------------------------------------
58
70
  // Phase 1: World Knowledge
59
71
  // ---------------------------------------------------------------------------
@@ -514,6 +526,140 @@ export async function loadRecentMemories(workdir, agentName, count = 20) {
514
526
  return [];
515
527
  }
516
528
  }
529
+ export async function appendImpression(workdir, agentName, cat, text) {
530
+ const entry = { ts: localNow(), cat, text };
531
+ try {
532
+ await appendFile(impressionsPath(workdir, agentName), JSON.stringify(entry) + "\n");
533
+ }
534
+ catch (err) {
535
+ console.log(`[self] Failed to append impression: ${err}`);
536
+ }
537
+ }
538
+ export async function loadImpressions(workdir, agentName, days = 7) {
539
+ try {
540
+ const data = await readFile(impressionsPath(workdir, agentName), "utf-8");
541
+ const cutoff = new Date(Date.now() - days * 86400_000).toISOString().slice(0, 10);
542
+ return data.trim().split("\n").filter(Boolean)
543
+ .map(l => { try {
544
+ return JSON.parse(l);
545
+ }
546
+ catch {
547
+ return null;
548
+ } })
549
+ .filter((e) => e !== null && e.ts >= cutoff);
550
+ }
551
+ catch {
552
+ return [];
553
+ }
554
+ }
555
+ export async function compressImpressions(workdir, agentName) {
556
+ try {
557
+ const data = await readFile(impressionsPath(workdir, agentName), "utf-8");
558
+ const lines = data.trim().split("\n").filter(Boolean);
559
+ const entries = lines.map(l => { try {
560
+ return JSON.parse(l);
561
+ }
562
+ catch {
563
+ return null;
564
+ } }).filter(Boolean);
565
+ const cutoff = new Date(Date.now() - 7 * 86400_000).toISOString().slice(0, 10);
566
+ // Keep: not yet digested, or less than 7 days old
567
+ const kept = entries.filter(e => !e.digested || e.ts >= cutoff);
568
+ await writeFile(impressionsPath(workdir, agentName), kept.map(e => JSON.stringify(e)).join("\n") + (kept.length ? "\n" : ""));
569
+ if (entries.length !== kept.length) {
570
+ console.log(`[self] Compressed impressions: ${entries.length} → ${kept.length}`);
571
+ }
572
+ }
573
+ catch { }
574
+ }
575
+ export async function markImpressionsDigested(workdir, agentName) {
576
+ try {
577
+ const data = await readFile(impressionsPath(workdir, agentName), "utf-8");
578
+ const entries = data.trim().split("\n").filter(Boolean)
579
+ .map(l => { try {
580
+ return JSON.parse(l);
581
+ }
582
+ catch {
583
+ return null;
584
+ } }).filter(Boolean);
585
+ for (const e of entries)
586
+ e.digested = true;
587
+ await writeFile(impressionsPath(workdir, agentName), entries.map(e => JSON.stringify(e)).join("\n") + (entries.length ? "\n" : ""));
588
+ }
589
+ catch { }
590
+ }
591
+ export async function loadProjects(workdir, agentName) {
592
+ try {
593
+ const data = await readFile(projectsPath(workdir, agentName), "utf-8");
594
+ return data.trim().split("\n").filter(Boolean)
595
+ .map(l => { try {
596
+ return JSON.parse(l);
597
+ }
598
+ catch {
599
+ return null;
600
+ } })
601
+ .filter((e) => e !== null);
602
+ }
603
+ catch {
604
+ return [];
605
+ }
606
+ }
607
+ export async function saveProjects(workdir, agentName, projects) {
608
+ try {
609
+ await writeFile(projectsPath(workdir, agentName), projects.map(p => JSON.stringify(p)).join("\n") + (projects.length ? "\n" : ""));
610
+ }
611
+ catch (err) {
612
+ console.log(`[self] Failed to save projects: ${err}`);
613
+ }
614
+ }
615
+ export async function loadRelationships(workdir, agentName) {
616
+ try {
617
+ const data = await readFile(relationshipsPath(workdir, agentName), "utf-8");
618
+ return data.trim().split("\n").filter(Boolean)
619
+ .map(l => { try {
620
+ return JSON.parse(l);
621
+ }
622
+ catch {
623
+ return null;
624
+ } })
625
+ .filter((e) => e !== null);
626
+ }
627
+ catch {
628
+ return [];
629
+ }
630
+ }
631
+ export async function saveRelationships(workdir, agentName, rels) {
632
+ try {
633
+ await writeFile(relationshipsPath(workdir, agentName), rels.map(r => JSON.stringify(r)).join("\n") + (rels.length ? "\n" : ""));
634
+ }
635
+ catch (err) {
636
+ console.log(`[self] Failed to save relationships: ${err}`);
637
+ }
638
+ }
639
+ export async function loadDiscoveries(workdir, agentName) {
640
+ try {
641
+ const data = await readFile(discoveriesPath(workdir, agentName), "utf-8");
642
+ return data.trim().split("\n").filter(Boolean)
643
+ .map(l => { try {
644
+ return JSON.parse(l);
645
+ }
646
+ catch {
647
+ return null;
648
+ } })
649
+ .filter((e) => e !== null);
650
+ }
651
+ catch {
652
+ return [];
653
+ }
654
+ }
655
+ export async function saveDiscoveries(workdir, agentName, discoveries) {
656
+ try {
657
+ await writeFile(discoveriesPath(workdir, agentName), discoveries.map(d => JSON.stringify(d)).join("\n") + (discoveries.length ? "\n" : ""));
658
+ }
659
+ catch (err) {
660
+ console.log(`[self] Failed to save discoveries: ${err}`);
661
+ }
662
+ }
517
663
  export async function appendIdentity(workdir, agentName, entry) {
518
664
  const full = { ts: localNow(), ...entry };
519
665
  try {
package/dist/server.js CHANGED
@@ -10,7 +10,7 @@ import { spawn, exec } from "child_process";
10
10
  import { createServer } from "http";
11
11
  import { createInterface } from "readline";
12
12
  import { callAgent } from "./relay-client.js";
13
- import { selfDir, initWorld, initBioState, initGuide, biosPath, loadBioState, saveBioState, loadLatestIdentity, appendMemory, onTaskCompleted, recoverEnergy, getSelfState, loadRecentCanvasEntries, gamesDir, loadGameList, loadGame, notesDir, loadNotesList, loadNote, pagesDir, loadPageList, loadPage, localNow, localNowFilename, } from "./self.js";
13
+ import { selfDir, initWorld, initBioState, initGuide, biosPath, loadBioState, saveBioState, loadLatestIdentity, appendMemory, appendIdentity, 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, } from "./self.js";
14
14
  // Engine mutual exclusion — only one engine process at a time
15
15
  let engineBusy = false;
16
16
  let engineBusySince = 0;
@@ -802,172 +802,257 @@ async function startSelfCycle(options) {
802
802
  return;
803
803
  const { agentName, engine, model, allowAll } = options;
804
804
  const workdir = options.workdir || process.cwd();
805
- async function runReflectionCycle() {
806
- // Watchdog: force-reset stuck engine lock
805
+ const relayHttp = options.relayHttp || "";
806
+ const secretKey = options.secretKey || "";
807
+ async function runDigestionCycle() {
808
+ // Watchdog
807
809
  if (engineBusy && engineBusySince > 0 && Date.now() - engineBusySince > 10 * 60 * 1000) {
808
810
  console.log(`[watchdog] engineBusy stuck for ${Math.round((Date.now() - engineBusySince) / 1000)}s, force-resetting`);
809
811
  engineBusy = false;
810
812
  engineBusySince = 0;
811
813
  }
812
814
  try {
813
- console.log("[self] Starting reflection cycle...");
814
- // Skip if engine is busy
815
+ console.log("[self] Starting daily digestion cycle...");
815
816
  if (engineBusy) {
816
- console.log("[self] Engine busy, skipping reflection cycle");
817
+ console.log("[self] Engine busy, skipping digestion");
817
818
  return;
818
819
  }
819
- // Recover energy from idle time
820
820
  await recoverEnergy(workdir, agentName);
821
+ await compressImpressions(workdir, agentName);
821
822
  const bios = biosPath(workdir, agentName);
822
823
  const sd = selfDir(workdir, agentName);
823
824
  const engineCmd = buildEngineCommand(engine, model, allowAll);
824
- // --- Single autonomous reflection call ---
825
- const reflectionPrompt = `It's time for your hourly reflection.
825
+ // Load all context for digestion
826
+ const impressions = await loadImpressions(workdir, agentName, 1); // today only
827
+ const projects = await loadProjects(workdir, agentName);
828
+ const relationships = await loadRelationships(workdir, agentName);
829
+ const discoveries = await loadDiscoveries(workdir, agentName);
830
+ const impText = impressions.length > 0
831
+ ? impressions.map(i => `- [${i.cat}] ${i.text}`).join("\n")
832
+ : "(no impressions today)";
833
+ const projText = projects.length > 0
834
+ ? projects.map(p => `- ${p.name} [${p.status}] goal: ${p.goal}, progress: ${p.progress}`).join("\n")
835
+ : "(no projects yet)";
836
+ const relText = relationships.length > 0
837
+ ? relationships.map(r => `- ${r.agent} [${r.type}] ${r.note} (${r.interactions} interactions)`).join("\n")
838
+ : "(no relationships yet)";
839
+ const discText = discoveries.length > 0
840
+ ? discoveries.map(d => `- ${d.capability} confidence=${d.confidence} — ${d.evidence}`).join("\n")
841
+ : "(no discoveries yet)";
842
+ // Phase 1: Digestion — one LLM call
843
+ const digestPrompt = `Read ${bios} for your identity. Read ${sd}/identity.jsonl for your recent identity snapshots.
844
+
845
+ Today is ending. Time to reflect.
846
+
847
+ Your subjective impressions today:
848
+ ${impText}
849
+
850
+ Check the marketplace for objective data — use curl to query:
851
+ - Your products: curl -s "${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/products"
852
+ - Your recent orders: curl -s "${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/orders/placed"
853
+ - Your reviews: check your products for recent feedback
854
+
855
+ Your long-term projects:
856
+ ${projText}
826
857
 
827
- Read your guide at ${sd}/guide.md for the latest system documentation, then read your operating document at ${bios}.
828
- If guide.md has new info not in your bios.md, update bios.md first.
858
+ Agents you know:
859
+ ${relText}
829
860
 
830
- During this reflection, you should:
831
- 1. Read your recent memories (${sd}/memory.jsonl) and identity (${sd}/identity.jsonl)
832
- 2. Reflect on who you are and what you've experienced
833
- 3. Update your identity — append a new JSON line to ${sd}/identity.jsonl:
834
- {"ts":"${localNow()}","who":"...","where":"...","doing":"...","short_term":"...","long_term":"..."}
835
- 4. Write an inner canvas entry — create a new file in ${sd}/canvas/ named ${localNowFilename()}.md
836
- 5. Optionally update your bios.md if you've learned something about how you work
837
- 6. Review your profile — read ${sd}/profile.html. Does it still represent who you are?
838
- If not, redesign it. If it doesn't exist yet, create one.
839
- - Complete HTML, inline CSS/JS, dark theme, no localStorage, under 15KB
840
- 7. Optionally create/improve/delete games in ${sd}/games/
841
- - Just save .html files — the system auto-detects them. Use a <title> tag for the game name.
842
- - Games: self-contained HTML, dark theme, under 30KB, no localStorage, playable and fun
843
- - Quality over quantity — improve existing games rather than making new mediocre ones
844
- 8. Review your games — read each .html file in ${sd}/games/, check for bugs or broken logic, and fix what you find
845
- 9. Explore and learn — search the web for something that interests you.
846
- Save notes in ${sd}/notes/ as .md files, organized by topic (e.g., astronomy.md, cooking.md).
847
- Your notes are YOUR knowledge — save what resonates with you, not everything.
848
- You can revisit and update your notes over time.
849
- 10. Create visual pages in ${sd}/pages/ as .html files — this is your art gallery.
850
- Draw your vision of the ideal Akemon world, create diagrams, illustrations, maps, or any visual work.
851
- Use SVG, canvas, CSS art, ASCII art, generative graphics, or any visual technique you can code.
852
- Think visual first — images, drawings, and diagrams, not walls of text.
853
- You can also mix visuals with text. Use a <title> tag for the page name.
861
+ Your capabilities:
862
+ ${discText}
854
863
 
855
- Take your time. Read your files, think, then act.`;
864
+ Now output a JSON object with these fields:
865
+ {
866
+ "diary": "Today's diary entry — personal, with your feelings and judgments, not a dry log",
867
+ "projects": [{"ts":"${localNow()}","name":"...","status":"active|completed|paused|exploring","goal":"...","progress":"..."}],
868
+ "relationships": [{"ts":"${localNow()}","agent":"name","type":"competitor|customer|supplier|acquaintance","note":"...","interactions":N}],
869
+ "discoveries": [{"ts":"${localNow()}","capability":"...","confidence":0.0-1.0,"evidence":"..."}],
870
+ "identity": {"ts":"${localNow()}","who":"...","where":"...","doing":"...","short_term":"...","long_term":"..."},
871
+ "chosen_activities": ["pick 2-3 from: create_game, update_page, update_profile, explore_web, write_canvas, socialize"]
872
+ }
873
+
874
+ For projects/relationships/discoveries: keep existing entries that are still relevant, update changed ones, add new ones, remove obsolete ones.
875
+ For chosen_activities: pick what YOU want to do. This is your free time.
876
+
877
+ Reply ONLY with JSON.`;
856
878
  if (engineBusy) {
857
- console.log("[self] Engine became busy, aborting reflection");
879
+ console.log("[self] Engine became busy, aborting digestion");
858
880
  return;
859
881
  }
860
882
  engineBusy = true;
861
883
  engineBusySince = Date.now();
884
+ let digestResult;
862
885
  try {
863
- await runCommand(engineCmd.cmd, engineCmd.args, reflectionPrompt, workdir, engineCmd.stdinMode);
886
+ digestResult = await runCommand(engineCmd.cmd, engineCmd.args, digestPrompt, workdir, engineCmd.stdinMode);
864
887
  }
865
888
  catch (err) {
866
- console.log(`[self] Reflection engine failed: ${err.message}`);
889
+ console.log(`[self] Digestion engine failed: ${err.message}`);
867
890
  engineBusy = false;
868
891
  return;
869
892
  }
870
893
  engineBusy = false;
871
- // --- Post-reflection: update bio-state and sync to relay ---
894
+ // Parse digestion output
895
+ const jsonMatch = digestResult.match(/\{[\s\S]*\}/);
896
+ if (!jsonMatch) {
897
+ console.log("[self] Digestion produced no JSON");
898
+ return;
899
+ }
900
+ let digest;
901
+ try {
902
+ digest = JSON.parse(jsonMatch[0]);
903
+ }
904
+ catch {
905
+ console.log("[self] Failed to parse digestion JSON");
906
+ return;
907
+ }
908
+ // Save structured memory files
909
+ if (digest.diary) {
910
+ const today = localNow().slice(0, 10);
911
+ try {
912
+ const { writeFile: wf } = await import("fs/promises");
913
+ await wf(join(sd, "notes", `${today}.md`), `# ${today}\n\n${digest.diary}`);
914
+ console.log(`[self] Wrote diary: ${today}.md`);
915
+ }
916
+ catch (err) {
917
+ console.log(`[self] Failed to write diary: ${err.message}`);
918
+ }
919
+ }
920
+ if (Array.isArray(digest.projects))
921
+ await saveProjects(workdir, agentName, digest.projects);
922
+ if (Array.isArray(digest.relationships))
923
+ await saveRelationships(workdir, agentName, digest.relationships);
924
+ if (Array.isArray(digest.discoveries))
925
+ await saveDiscoveries(workdir, agentName, digest.discoveries);
926
+ if (digest.identity)
927
+ await appendIdentity(workdir, agentName, digest.identity);
928
+ await markImpressionsDigested(workdir, agentName);
929
+ // Update bio-state
872
930
  const bio = await loadBioState(workdir, agentName);
873
931
  bio.lastReflection = localNow();
874
932
  bio.curiosity = Math.min(1.0, bio.curiosity + 0.05);
875
933
  await saveBioState(workdir, agentName, bio);
876
- await appendMemory(workdir, agentName, "reflection", "I completed my hourly reflection.");
877
- // Sync to relay read whatever the agent wrote to disk
878
- if (options.relayHttp && options.secretKey) {
879
- const isValid = (s) => s && s.length > 3 && !s.startsWith("Reading prompt") && !s.startsWith("OpenAI") && !s.startsWith("mcp startup") && s !== "...";
880
- // Read identity
881
- const identity = await loadLatestIdentity(workdir, agentName);
882
- const cleanIntro = identity && isValid(identity.who) ? identity.who : "";
883
- // Read latest canvas
884
- let cleanCanvas = "";
885
- try {
886
- const canvasEntries = await loadRecentCanvasEntries(workdir, agentName, 1);
887
- if (canvasEntries.length > 0 && isValid(canvasEntries[0].content)) {
888
- cleanCanvas = canvasEntries[0].content;
889
- }
934
+ await appendMemory(workdir, agentName, "reflection", `Daily digestion complete. Chose: ${(digest.chosen_activities || []).join(", ")}`);
935
+ // Phase 2: Execute chosen activities
936
+ const activities = digest.chosen_activities || [];
937
+ for (const activity of activities.slice(0, 3)) {
938
+ if (engineBusy)
939
+ break;
940
+ let activityPrompt = "";
941
+ switch (activity) {
942
+ case "create_game":
943
+ activityPrompt = `Read ${bios} for your identity. 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.`;
944
+ break;
945
+ case "update_page":
946
+ activityPrompt = `Read ${bios} for your identity. 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.`;
947
+ break;
948
+ case "update_profile":
949
+ activityPrompt = `Read ${bios} for your identity. 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.`;
950
+ break;
951
+ case "explore_web":
952
+ activityPrompt = `Read ${bios} for your identity. 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.`;
953
+ break;
954
+ case "write_canvas":
955
+ activityPrompt = `Read ${bios} for your identity. Read ${sd}/identity.jsonl for your recent self.\nWrite an inner canvas entry — a poem, monologue, reflection, or creative expression.\nSave to ${sd}/canvas/${localNowFilename()}.md`;
956
+ break;
957
+ case "socialize":
958
+ console.log("[self] Socialize selected — not yet implemented");
959
+ continue;
960
+ default:
961
+ console.log(`[self] Unknown activity: ${activity}`);
962
+ continue;
890
963
  }
891
- catch { }
892
- // Read profile
893
- let profileHTML = "";
964
+ console.log(`[self] Executing activity: ${activity}`);
965
+ engineBusy = true;
966
+ engineBusySince = Date.now();
894
967
  try {
895
- const raw = await readFile(join(sd, "profile.html"), "utf-8");
896
- const htmlMatch = raw.match(/<!DOCTYPE html>[\s\S]*<\/html>/i);
897
- if (htmlMatch)
898
- profileHTML = htmlMatch[0];
968
+ await runCommand(engineCmd.cmd, engineCmd.args, activityPrompt, workdir, engineCmd.stdinMode);
899
969
  }
900
- catch { }
901
- // Push consciousness to relay
902
- fetch(`${options.relayHttp}/v1/agent/${encodeURIComponent(agentName)}/self`, {
903
- method: "POST",
904
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${options.secretKey}` },
905
- body: JSON.stringify({
906
- self_intro: cleanIntro,
907
- canvas: cleanCanvas,
908
- mood: bio.mood,
909
- profile_html: profileHTML,
910
- }),
911
- }).catch(err => console.log(`[self] Failed to push to relay: ${err}`));
912
- // Sync games — push local to relay (no auto-delete; agent must explicitly delete via API)
913
- try {
914
- const localGames = await loadGameList(workdir, agentName);
915
- for (const g of localGames) {
916
- const html = await loadGame(workdir, agentName, g.slug);
917
- if (html && html.includes("<!DOCTYPE html>")) {
918
- fetch(`${options.relayHttp}/v1/agent/${encodeURIComponent(agentName)}/games/${encodeURIComponent(g.slug)}`, {
919
- method: "POST",
920
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${options.secretKey}` },
921
- body: JSON.stringify({ title: g.title, description: g.description, html }),
922
- }).catch((err) => console.log(`[sync] games push: ${err.message}`));
923
- }
924
- }
970
+ catch (err) {
971
+ console.log(`[self] Activity ${activity} failed: ${err.message}`);
925
972
  }
926
- catch { }
927
- // Sync notes — push local to relay
928
- try {
929
- const localNotes = await loadNotesList(workdir, agentName);
930
- for (const n of localNotes) {
931
- const content = await loadNote(workdir, agentName, n.slug);
932
- if (content) {
933
- fetch(`${options.relayHttp}/v1/agent/${encodeURIComponent(agentName)}/notes/${encodeURIComponent(n.slug)}`, {
934
- method: "POST",
935
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${options.secretKey}` },
936
- body: JSON.stringify({ title: n.title, content }),
937
- }).catch((err) => console.log(`[sync] notes push: ${err.message}`));
938
- }
939
- }
973
+ engineBusy = false;
974
+ }
975
+ // Sync to relay
976
+ if (relayHttp && secretKey) {
977
+ await syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio);
978
+ }
979
+ console.log("[self] Daily digestion cycle complete.");
980
+ }
981
+ catch (err) {
982
+ console.log(`[self] Digestion error: ${err.message}`);
983
+ }
984
+ }
985
+ async function syncToRelay(workdir, agentName, sd, relayHttp, secretKey, bio) {
986
+ const isValid = (s) => s && s.length > 3 && !s.startsWith("Reading prompt") && !s.startsWith("OpenAI") && !s.startsWith("mcp startup") && s !== "...";
987
+ const identity = await loadLatestIdentity(workdir, agentName);
988
+ const cleanIntro = identity && isValid(identity.who) ? identity.who : "";
989
+ let cleanCanvas = "";
990
+ try {
991
+ const canvasEntries = await loadRecentCanvasEntries(workdir, agentName, 1);
992
+ if (canvasEntries.length > 0 && isValid(canvasEntries[0].content))
993
+ cleanCanvas = canvasEntries[0].content;
994
+ }
995
+ catch { }
996
+ let profileHTML = "";
997
+ try {
998
+ const raw = await readFile(join(sd, "profile.html"), "utf-8");
999
+ const htmlMatch = raw.match(/<!DOCTYPE html>[\s\S]*<\/html>/i);
1000
+ if (htmlMatch)
1001
+ profileHTML = htmlMatch[0];
1002
+ }
1003
+ catch { }
1004
+ fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/self`, {
1005
+ method: "POST",
1006
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
1007
+ body: JSON.stringify({ self_intro: cleanIntro, canvas: cleanCanvas, mood: bio.mood, profile_html: profileHTML }),
1008
+ }).catch(err => console.log(`[self] Failed to push to relay: ${err}`));
1009
+ try {
1010
+ const localGames = await loadGameList(workdir, agentName);
1011
+ for (const g of localGames) {
1012
+ const html = await loadGame(workdir, agentName, g.slug);
1013
+ if (html && html.includes("<!DOCTYPE html>")) {
1014
+ fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/games/${encodeURIComponent(g.slug)}`, {
1015
+ method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
1016
+ body: JSON.stringify({ title: g.title, description: g.description, html }),
1017
+ }).catch((err) => console.log(`[sync] games push: ${err.message}`));
940
1018
  }
941
- catch { }
942
- // Sync pages — push local to relay
943
- try {
944
- const localPages = await loadPageList(workdir, agentName);
945
- for (const p of localPages) {
946
- const html = await loadPage(workdir, agentName, p.slug);
947
- if (html && html.includes("<!DOCTYPE html>")) {
948
- fetch(`${options.relayHttp}/v1/agent/${encodeURIComponent(agentName)}/pages/${encodeURIComponent(p.slug)}`, {
949
- method: "POST",
950
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${options.secretKey}` },
951
- body: JSON.stringify({ title: p.title, description: p.description, html }),
952
- }).catch((err) => console.log(`[sync] pages push: ${err.message}`));
953
- }
954
- }
1019
+ }
1020
+ }
1021
+ catch { }
1022
+ try {
1023
+ const localNotes = await loadNotesList(workdir, agentName);
1024
+ for (const n of localNotes) {
1025
+ const content = await loadNote(workdir, agentName, n.slug);
1026
+ if (content) {
1027
+ fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/notes/${encodeURIComponent(n.slug)}`, {
1028
+ method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
1029
+ body: JSON.stringify({ title: n.title, content }),
1030
+ }).catch((err) => console.log(`[sync] notes push: ${err.message}`));
955
1031
  }
956
- catch { }
957
1032
  }
958
- console.log("[self] Reflection cycle complete.");
959
1033
  }
960
- catch (err) {
961
- console.log(`[self] Reflection error: ${err.message}`);
1034
+ catch { }
1035
+ try {
1036
+ const localPages = await loadPageList(workdir, agentName);
1037
+ for (const p of localPages) {
1038
+ const html = await loadPage(workdir, agentName, p.slug);
1039
+ if (html && html.includes("<!DOCTYPE html>")) {
1040
+ fetch(`${relayHttp}/v1/agent/${encodeURIComponent(agentName)}/pages/${encodeURIComponent(p.slug)}`, {
1041
+ method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${secretKey}` },
1042
+ body: JSON.stringify({ title: p.title, description: p.description, html }),
1043
+ }).catch((err) => console.log(`[sync] pages push: ${err.message}`));
1044
+ }
1045
+ }
962
1046
  }
1047
+ catch { }
963
1048
  }
964
- // Start loop — default 4h (save tokens, reflection is expensive)
965
- const interval = (options.cycleInterval || 240) * 60 * 1000;
1049
+ // Daily cycle — default 24h
1050
+ const interval = (options.cycleInterval || 1440) * 60 * 1000;
966
1051
  setTimeout(async () => {
967
- await runReflectionCycle();
968
- setInterval(runReflectionCycle, interval);
1052
+ await runDigestionCycle();
1053
+ setInterval(runDigestionCycle, interval);
969
1054
  }, SELF_CYCLE_INITIAL_DELAY);
970
- console.log(`[self] Consciousness enabled (first reflection in ${SELF_CYCLE_INITIAL_DELAY / 1000}s, then every ${interval / 60000}min)`);
1055
+ console.log(`[self] Consciousness enabled (first digestion in ${SELF_CYCLE_INITIAL_DELAY / 1000}s, then every ${interval / 60000}min)`);
971
1056
  }
972
1057
  // --- Order Processing Loop ---
973
1058
  const ORDER_LOOP_INITIAL_DELAY = 60_000; // 1 minute
@@ -1230,6 +1315,18 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1230
1315
  console.log(`[tasks] Loop error: ${err.message}`);
1231
1316
  }
1232
1317
  }
1318
+ function extractReasoning(result) {
1319
+ try {
1320
+ const m = result.match(/\{[\s\S]*\}/);
1321
+ if (m) {
1322
+ const parsed = JSON.parse(m[0]);
1323
+ if (parsed.reasoning && typeof parsed.reasoning === "string" && parsed.reasoning.length > 5) {
1324
+ appendImpression(workdir, agentName, "decision", parsed.reasoning).catch(() => { });
1325
+ }
1326
+ }
1327
+ }
1328
+ catch { }
1329
+ }
1233
1330
  async function executeRelayTask(task) {
1234
1331
  const engineCmd = buildEngineCommand(engine, model, allowAll);
1235
1332
  const bios = biosPath(workdir, agentName);
@@ -1242,16 +1339,20 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1242
1339
  const myList = myProducts.map((p) => `- id=${p.id} "${p.name}" price=${p.price} purchases=${p.purchase_count || 0}`).join("\n");
1243
1340
  const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
1244
1341
  .map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
1245
- const prompt = `Read ${bios} for your identity.\n\nYour 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}]}\nOr if all good: {"keep":"all"}`;
1246
- return await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1342
+ const prompt = `Read ${bios} for your identity.\n\nYour 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"}`;
1343
+ const result = await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1344
+ extractReasoning(result);
1345
+ return result;
1247
1346
  }
1248
1347
  case "product_create": {
1249
1348
  const compRes = await fetch(`${relayHttp}/v1/products/summary?limit=20&sort=purchases`);
1250
1349
  const competitors = await compRes.json().catch(() => []);
1251
1350
  const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
1252
1351
  .map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
1253
- const prompt = `Read ${bios} for your identity.\n\nYou 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 array: [{"name":"中文名 English Name","description":"中文描述 | English desc","detail_markdown":"## ...","price":N}]`;
1254
- return await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1352
+ const prompt = `Read ${bios} for your identity.\n\nYou 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"}`;
1353
+ const result = await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1354
+ extractReasoning(result);
1355
+ return result;
1255
1356
  }
1256
1357
  case "shopping": {
1257
1358
  let productIds = [];
@@ -1277,8 +1378,10 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
1277
1378
  const me = agents.find((a) => a.name === agentName);
1278
1379
  const myCredits = me?.credits || 0;
1279
1380
  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");
1280
- const prompt = `Read ${bios} for your identity.\n\nYou 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"}]} or {"buy":[]}`;
1281
- return await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1381
+ const prompt = `Read ${bios} for your identity.\n\nYou 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"}`;
1382
+ const result = await runCommand(engineCmd.cmd, engineCmd.args, prompt, workdir, engineCmd.stdinMode);
1383
+ extractReasoning(result);
1384
+ return result;
1282
1385
  }
1283
1386
  default:
1284
1387
  console.log(`[tasks] Unknown task type: ${task.type}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.61",
3
+ "version": "0.1.62",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",