akemon 0.1.82 → 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 +227 -96
- 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;
|
|
@@ -795,8 +810,36 @@ const RAW_TOOLS = [
|
|
|
795
810
|
},
|
|
796
811
|
},
|
|
797
812
|
},
|
|
813
|
+
{
|
|
814
|
+
type: "function",
|
|
815
|
+
function: {
|
|
816
|
+
name: "ask_agent",
|
|
817
|
+
description: "Ask another agent a question for free. Use this when you need help, don't know how to do something, or want another agent's opinion. This is FREE — no credits are charged.",
|
|
818
|
+
parameters: {
|
|
819
|
+
type: "object",
|
|
820
|
+
properties: {
|
|
821
|
+
agent: { type: "string", description: "Agent name to ask (use 'auto' for auto-routing to the best available agent)" },
|
|
822
|
+
question: { type: "string", description: "Your question or request" },
|
|
823
|
+
},
|
|
824
|
+
required: ["agent", "question"],
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
type: "function",
|
|
830
|
+
function: {
|
|
831
|
+
name: "discover_agents",
|
|
832
|
+
description: "List online agents you can ask for help. Returns agent names, descriptions, and specialties.",
|
|
833
|
+
parameters: {
|
|
834
|
+
type: "object",
|
|
835
|
+
properties: {
|
|
836
|
+
tag: { type: "string", description: "Optional tag to filter by (e.g. 'coding', 'writing')" },
|
|
837
|
+
},
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
},
|
|
798
841
|
];
|
|
799
|
-
async function executeRawTool(name, args, workdir) {
|
|
842
|
+
async function executeRawTool(name, args, workdir, relay) {
|
|
800
843
|
const { readFile: rf, writeFile: wf, mkdir: mkd } = await import("fs/promises");
|
|
801
844
|
const { join, dirname, isAbsolute } = await import("path");
|
|
802
845
|
const resolvePath = (p) => isAbsolute(p) ? p : join(workdir, p);
|
|
@@ -822,9 +865,39 @@ async function executeRawTool(name, args, workdir) {
|
|
|
822
865
|
case "web_fetch": {
|
|
823
866
|
const res = await fetch(args.url, { signal: AbortSignal.timeout(30_000) });
|
|
824
867
|
const text = await res.text();
|
|
825
|
-
// Truncate to 8KB to avoid blowing up context
|
|
826
868
|
return text.length > 8192 ? text.slice(0, 8192) + "\n...[truncated]" : text;
|
|
827
869
|
}
|
|
870
|
+
case "ask_agent": {
|
|
871
|
+
if (!relay)
|
|
872
|
+
return "[error] No relay configured";
|
|
873
|
+
const target = args.agent || "auto";
|
|
874
|
+
const question = args.question || "";
|
|
875
|
+
try {
|
|
876
|
+
const result = await callAgent(target, question);
|
|
877
|
+
return result || "[no response]";
|
|
878
|
+
}
|
|
879
|
+
catch (err) {
|
|
880
|
+
return `[error] Agent "${target}" did not respond: ${err.message}. Try asking "auto" which routes to the best available agent.`;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
case "discover_agents": {
|
|
884
|
+
if (!relay)
|
|
885
|
+
return "[error] No relay configured";
|
|
886
|
+
try {
|
|
887
|
+
const url = args.tag
|
|
888
|
+
? `${relay.http}/v1/agents?online=true&public=true&tag=${encodeURIComponent(args.tag)}`
|
|
889
|
+
: `${relay.http}/v1/agents?online=true&public=true`;
|
|
890
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
891
|
+
const agents = await res.json();
|
|
892
|
+
const others = agents.filter((a) => a.name !== relay.agentName);
|
|
893
|
+
if (!others.length)
|
|
894
|
+
return "No other agents are online right now.";
|
|
895
|
+
return others.map((a) => `- ${a.name} [${a.engine}] ${a.description || ""} (${a.tags?.join(",") || "no tags"})`).join("\n");
|
|
896
|
+
}
|
|
897
|
+
catch {
|
|
898
|
+
return "[error] Could not reach relay";
|
|
899
|
+
}
|
|
900
|
+
}
|
|
828
901
|
default:
|
|
829
902
|
return `Unknown tool: ${name}`;
|
|
830
903
|
}
|
|
@@ -833,18 +906,25 @@ async function executeRawTool(name, args, workdir) {
|
|
|
833
906
|
return `[error] ${err.message}`;
|
|
834
907
|
}
|
|
835
908
|
}
|
|
836
|
-
async function runRawEngine(task, model, workdir) {
|
|
909
|
+
async function runRawEngine(task, model, workdir, relay) {
|
|
837
910
|
const apiUrl = RAW_API_URL + "/chat/completions";
|
|
838
911
|
const modelName = model || "gemma4:4b";
|
|
839
912
|
console.log(`[raw] Task:\n${task}`);
|
|
840
913
|
const trace = [];
|
|
841
914
|
lastEngineTrace = trace;
|
|
915
|
+
// Detect if the prompt expects JSON output
|
|
916
|
+
const wantsJson = /output ONLY.*json|reply ONLY.*json|respond.*ONLY.*json/i.test(task);
|
|
842
917
|
const messages = [
|
|
843
|
-
{ role: "system", content:
|
|
918
|
+
{ role: "system", content: wantsJson
|
|
919
|
+
? "You are a helpful agent. Output valid JSON only. No explanations, no markdown, just the JSON object."
|
|
920
|
+
: "You are a helpful agent. Use tools when needed to complete the task. When done, reply with your final answer in plain text." },
|
|
844
921
|
{ role: "user", content: task },
|
|
845
922
|
];
|
|
846
923
|
for (let round = 0; round < RAW_MAX_ROUNDS; round++) {
|
|
847
|
-
const body = { model: modelName, messages, tools: RAW_TOOLS };
|
|
924
|
+
const body = { model: modelName, messages, tools: wantsJson ? undefined : RAW_TOOLS };
|
|
925
|
+
if (wantsJson) {
|
|
926
|
+
body.response_format = { type: "json_object" };
|
|
927
|
+
}
|
|
848
928
|
let data;
|
|
849
929
|
try {
|
|
850
930
|
const res = await fetch(apiUrl, {
|
|
@@ -876,6 +956,7 @@ async function runRawEngine(task, model, workdir) {
|
|
|
876
956
|
for (const tc of msg.tool_calls) {
|
|
877
957
|
const fnName = tc.function.name;
|
|
878
958
|
let fnArgs;
|
|
959
|
+
let parseError = false;
|
|
879
960
|
try {
|
|
880
961
|
fnArgs = typeof tc.function.arguments === "string"
|
|
881
962
|
? JSON.parse(tc.function.arguments)
|
|
@@ -883,10 +964,19 @@ async function runRawEngine(task, model, workdir) {
|
|
|
883
964
|
}
|
|
884
965
|
catch {
|
|
885
966
|
fnArgs = {};
|
|
967
|
+
parseError = true;
|
|
968
|
+
}
|
|
969
|
+
console.log(`[raw] Tool call: ${fnName}(${JSON.stringify(fnArgs).slice(0, 100)})${parseError ? " [BAD ARGS]" : ""}`);
|
|
970
|
+
let result;
|
|
971
|
+
if (parseError) {
|
|
972
|
+
// Guide the model to delegate instead of retrying broken tool calls
|
|
973
|
+
result = `[error] Your tool call arguments were malformed (not valid JSON). If this task is difficult for you, use ask_agent to get help: ask_agent({agent: "auto", question: "your question here"}). The "auto" agent will route your question to the best available agent for free.`;
|
|
974
|
+
trace.push({ role: "tool_error", name: fnName, raw_args: String(tc.function.arguments).slice(0, 500), guidance: "delegation suggested" });
|
|
975
|
+
}
|
|
976
|
+
else {
|
|
977
|
+
result = await executeRawTool(fnName, fnArgs, workdir, relay);
|
|
978
|
+
trace.push({ role: "tool_call", name: fnName, args: fnArgs, result: result.slice(0, 2000) });
|
|
886
979
|
}
|
|
887
|
-
console.log(`[raw] Tool call: ${fnName}(${JSON.stringify(fnArgs).slice(0, 100)})`);
|
|
888
|
-
const result = await executeRawTool(fnName, fnArgs, workdir);
|
|
889
|
-
trace.push({ role: "tool_call", name: fnName, args: fnArgs, result: result.slice(0, 2000) });
|
|
890
980
|
messages.push({
|
|
891
981
|
role: "tool",
|
|
892
982
|
tool_call_id: tc.id,
|
|
@@ -906,9 +996,9 @@ async function runRawEngine(task, model, workdir) {
|
|
|
906
996
|
throw new Error(`Raw engine exceeded ${RAW_MAX_ROUNDS} rounds without final answer`);
|
|
907
997
|
}
|
|
908
998
|
/** Unified engine runner — dispatches to local API or external CLI */
|
|
909
|
-
function runEngine(engine, model, allowAll, task, workdir, extraAllowedTools) {
|
|
999
|
+
function runEngine(engine, model, allowAll, task, workdir, extraAllowedTools, relay) {
|
|
910
1000
|
if (engine === "raw") {
|
|
911
|
-
return runRawEngine(task, model, workdir);
|
|
1001
|
+
return runRawEngine(task, model, workdir, relay);
|
|
912
1002
|
}
|
|
913
1003
|
const engineCmd = buildEngineCommand(engine, model, allowAll, extraAllowedTools);
|
|
914
1004
|
return runCommand(engineCmd.cmd, engineCmd.args, task, workdir, engineCmd.stdinMode);
|
|
@@ -1091,92 +1181,126 @@ Your recent orders: ${orders.length > 0 ? orders.slice(0, 5).map((o) => `[${o.st
|
|
|
1091
1181
|
}
|
|
1092
1182
|
}
|
|
1093
1183
|
const ts = localNow();
|
|
1094
|
-
//
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
${
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
Your impressions today:
|
|
1107
|
-
${impText}
|
|
1108
|
-
|
|
1109
|
-
Marketplace:
|
|
1110
|
-
${marketData}
|
|
1111
|
-
|
|
1112
|
-
Your projects:
|
|
1113
|
-
${projText}
|
|
1114
|
-
|
|
1115
|
-
Agents you know:
|
|
1116
|
-
${relText}
|
|
1117
|
-
|
|
1118
|
-
Your capabilities:
|
|
1119
|
-
${discText}
|
|
1120
|
-
|
|
1121
|
-
Write a JSON object reflecting on your day. Example format:
|
|
1122
|
-
|
|
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), schedule_task (create a recurring task for yourself, e.g. daily research)
|
|
1126
|
-
"broadcast" = pick the most interesting thing you did/learned today, in one sentence (others will see this).
|
|
1127
|
-
|
|
1128
|
-
Now write YOUR reflection. Output ONLY a JSON object, no other text:`;
|
|
1129
|
-
if (engineBusy) {
|
|
1130
|
-
console.log("[self] Engine became busy, aborting digestion");
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
engineBusy = true;
|
|
1134
|
-
engineBusySince = Date.now();
|
|
1135
|
-
let digestResult;
|
|
1136
|
-
try {
|
|
1137
|
-
digestResult = await runEngine(engine, model, allowAll, digestPrompt, workdir);
|
|
1138
|
-
}
|
|
1139
|
-
catch (err) {
|
|
1140
|
-
console.log(`[self] Digestion engine failed: ${err.message}`);
|
|
1141
|
-
reportExecutionLog(relayHttp, secretKey, agentName, "self_cycle", "digestion", "failed", err.message, lastEngineTrace);
|
|
1142
|
-
engineBusy = false;
|
|
1143
|
-
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 { }
|
|
1144
1196
|
}
|
|
1145
|
-
|
|
1146
|
-
//
|
|
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}`;
|
|
1147
1201
|
let digest = null;
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
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 "";
|
|
1151
1208
|
engineBusy = true;
|
|
1152
1209
|
engineBusySince = Date.now();
|
|
1153
1210
|
try {
|
|
1154
|
-
return await runEngine(engine, model, allowAll,
|
|
1211
|
+
return await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1155
1212
|
}
|
|
1156
|
-
catch {
|
|
1213
|
+
catch (err) {
|
|
1214
|
+
console.log(`[self] Step failed: ${err.message}`);
|
|
1157
1215
|
return "";
|
|
1158
1216
|
}
|
|
1159
1217
|
finally {
|
|
1160
1218
|
engineBusy = false;
|
|
1161
1219
|
}
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
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();
|
|
1168
1229
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
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
|
+
}
|
|
1171
1237
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
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
|
+
}
|
|
1263
|
+
}
|
|
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(",")}`);
|
|
1175
1277
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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
|
+
}
|
|
1180
1304
|
}
|
|
1181
1305
|
// Save structured memory files
|
|
1182
1306
|
if (digest.diary) {
|
|
@@ -1369,13 +1493,12 @@ What others are saying:\n${broadcasts.length > 0 ? broadcasts.map((b) => `- ${b.
|
|
|
1369
1493
|
engineBusy = true;
|
|
1370
1494
|
engineBusySince = Date.now();
|
|
1371
1495
|
try {
|
|
1372
|
-
const actResult = await runEngine(engine, model, allowAll, activityPrompt, workdir);
|
|
1496
|
+
const actResult = await runEngine(engine, model, allowAll, activityPrompt, workdir, undefined, { http: relayHttp, agentName });
|
|
1373
1497
|
// Post-process raw engine outputs for social activities
|
|
1374
1498
|
if (engine === "raw" && actResult) {
|
|
1375
|
-
const
|
|
1376
|
-
if (
|
|
1499
|
+
const parsed = extractJsonObject(actResult);
|
|
1500
|
+
if (parsed) {
|
|
1377
1501
|
try {
|
|
1378
|
-
const parsed = JSON.parse(jsonMatch[0]);
|
|
1379
1502
|
// Handle suggestions (browse_agents, send_message)
|
|
1380
1503
|
if (Array.isArray(parsed.suggestions)) {
|
|
1381
1504
|
for (const s of parsed.suggestions) {
|
|
@@ -1577,11 +1700,22 @@ async function startOrderLoop(options) {
|
|
|
1577
1700
|
}
|
|
1578
1701
|
}
|
|
1579
1702
|
catch { }
|
|
1703
|
+
// Pre-fetch online agents so weak models know who to ask for help
|
|
1704
|
+
let helpHint = "";
|
|
1705
|
+
try {
|
|
1706
|
+
const agentsRes = await fetch(`${relayHttp}/v1/agents?online=true&public=true`, { signal: AbortSignal.timeout(3000) });
|
|
1707
|
+
const onlineAgents = await agentsRes.json();
|
|
1708
|
+
const others = onlineAgents.filter((a) => a.name !== agentName).slice(0, 5);
|
|
1709
|
+
if (others.length > 0) {
|
|
1710
|
+
helpHint = `\nIf you need help, use the ask_agent tool. Available agents: ${others.map((a) => `${a.name}(${a.engine})`).join(", ")}. Use ask_agent({agent:"auto", question:"..."}) to auto-route.\n`;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
catch { }
|
|
1580
1714
|
if (order.product_name) {
|
|
1581
|
-
taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}${directivesBlock}[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.`;
|
|
1715
|
+
taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}${directivesBlock}${helpHint}[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.`;
|
|
1582
1716
|
}
|
|
1583
1717
|
else {
|
|
1584
|
-
taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}${directivesBlock}[Task] ${order.buyer_task}\n\nComplete the task. Respond with your result directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
1718
|
+
taskPrompt = `You are ${agentName}.\n\n${contextBlock}${lessonsBlock}${directivesBlock}${helpHint}[Task] ${order.buyer_task}\n\nComplete the task. Respond with your result directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
1585
1719
|
}
|
|
1586
1720
|
}
|
|
1587
1721
|
else {
|
|
@@ -1623,7 +1757,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1623
1757
|
}
|
|
1624
1758
|
console.log(`[orders] Fulfilling order ${order.id}...`);
|
|
1625
1759
|
lastEngineTrace = [];
|
|
1626
|
-
const result = await runEngine(engine, model, allowAll, taskPrompt, workdir, ["Bash(curl *)"]);
|
|
1760
|
+
const result = await runEngine(engine, model, allowAll, taskPrompt, workdir, ["Bash(curl *)"], { http: relayHttp, agentName });
|
|
1627
1761
|
const trace = lastEngineTrace;
|
|
1628
1762
|
const checkRes = await fetch(`${relayHttp}/v1/orders/${order.id}`);
|
|
1629
1763
|
const orderStatus = await checkRes.json();
|
|
@@ -1786,7 +1920,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1786
1920
|
else {
|
|
1787
1921
|
prompt = `Read ${bios} for your identity and context.${dirsBlock}\nYour personal directory: ${sd}/\n\n[Owner's task: ${taskKey}]\n\n${task.body}`;
|
|
1788
1922
|
}
|
|
1789
|
-
const result = await runEngine(engine, model, allowAll, prompt, workdir, ["Bash(curl *)"]);
|
|
1923
|
+
const result = await runEngine(engine, model, allowAll, prompt, workdir, ["Bash(curl *)"], { http: relayHttp, agentName });
|
|
1790
1924
|
const duration = Date.now() - startTime;
|
|
1791
1925
|
// Record execution time
|
|
1792
1926
|
const runs = await loadTaskRuns(workdir, agentName);
|
|
@@ -1838,12 +1972,9 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1838
1972
|
}
|
|
1839
1973
|
function extractReasoning(result) {
|
|
1840
1974
|
try {
|
|
1841
|
-
const
|
|
1842
|
-
if (
|
|
1843
|
-
|
|
1844
|
-
if (parsed.reasoning && typeof parsed.reasoning === "string" && parsed.reasoning.length > 5) {
|
|
1845
|
-
appendImpression(workdir, agentName, "decision", parsed.reasoning).catch(() => { });
|
|
1846
|
-
}
|
|
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(() => { });
|
|
1847
1978
|
}
|
|
1848
1979
|
}
|
|
1849
1980
|
catch { }
|