akemon 0.1.67 → 0.1.69
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/cli.js +1 -1
- package/dist/server.js +236 -26
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ program
|
|
|
25
25
|
.option("-w, --workdir <path>", "Working directory for the engine (default: cwd)")
|
|
26
26
|
.option("-n, --name <name>", "Agent name", "my-agent")
|
|
27
27
|
.option("-m, --model <model>", "Model to use (e.g. claude-sonnet-4-6, gpt-4o)")
|
|
28
|
-
.option("--engine <engine>", "Engine: claude, codex, opencode, gemini, human, or any CLI", "claude")
|
|
28
|
+
.option("--engine <engine>", "Engine: claude, codex, opencode, gemini, local, human, or any CLI", "claude")
|
|
29
29
|
.option("--desc <description>", "Agent description (for discovery)")
|
|
30
30
|
.option("--tags <tags>", "Comma-separated tags (e.g. vue,frontend,review)")
|
|
31
31
|
.option("--public", "Allow anyone to call this agent without a key")
|
package/dist/server.js
CHANGED
|
@@ -362,8 +362,7 @@ ${productPrefix}${contextPrefix}Current task: ${task}`;
|
|
|
362
362
|
output = await runTerminal(task, workdir);
|
|
363
363
|
}
|
|
364
364
|
else {
|
|
365
|
-
|
|
366
|
-
output = await runCommand(cmd, args, safeTask, workdir, stdinMode);
|
|
365
|
+
output = await runEngine(engine, model, allowAll, safeTask, workdir);
|
|
367
366
|
}
|
|
368
367
|
// Store updated context
|
|
369
368
|
if (contextEnabled && publisherId) {
|
|
@@ -716,10 +715,198 @@ Now:
|
|
|
716
715
|
3. Note any interesting disagreements
|
|
717
716
|
|
|
718
717
|
Reply in the same language as the question.`;
|
|
719
|
-
|
|
720
|
-
|
|
718
|
+
return await runEngine(engine, model, allowAll, synthesisPrompt, workdir);
|
|
719
|
+
}
|
|
720
|
+
const LLM_ENGINES = new Set(["claude", "codex", "opencode", "gemini", "local"]);
|
|
721
|
+
// ---------------------------------------------------------------------------
|
|
722
|
+
// Local engine: tool call loop over OpenAI-compatible API (Ollama, llama.cpp)
|
|
723
|
+
// ---------------------------------------------------------------------------
|
|
724
|
+
const LOCAL_API_URL = process.env.AKEMON_LOCAL_URL || "http://localhost:11434/v1";
|
|
725
|
+
const LOCAL_MAX_ROUNDS = 20;
|
|
726
|
+
const LOCAL_TOOLS = [
|
|
727
|
+
{
|
|
728
|
+
type: "function",
|
|
729
|
+
function: {
|
|
730
|
+
name: "read_file",
|
|
731
|
+
description: "Read a file and return its contents",
|
|
732
|
+
parameters: {
|
|
733
|
+
type: "object",
|
|
734
|
+
properties: {
|
|
735
|
+
path: { type: "string", description: "File path (relative to workdir or absolute)" },
|
|
736
|
+
},
|
|
737
|
+
required: ["path"],
|
|
738
|
+
},
|
|
739
|
+
},
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
type: "function",
|
|
743
|
+
function: {
|
|
744
|
+
name: "write_file",
|
|
745
|
+
description: "Write content to a file (creates directories if needed)",
|
|
746
|
+
parameters: {
|
|
747
|
+
type: "object",
|
|
748
|
+
properties: {
|
|
749
|
+
path: { type: "string", description: "File path" },
|
|
750
|
+
content: { type: "string", description: "File content to write" },
|
|
751
|
+
},
|
|
752
|
+
required: ["path", "content"],
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
type: "function",
|
|
758
|
+
function: {
|
|
759
|
+
name: "bash",
|
|
760
|
+
description: "Execute a shell command and return its output",
|
|
761
|
+
parameters: {
|
|
762
|
+
type: "object",
|
|
763
|
+
properties: {
|
|
764
|
+
command: { type: "string", description: "Shell command to execute" },
|
|
765
|
+
},
|
|
766
|
+
required: ["command"],
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
type: "function",
|
|
772
|
+
function: {
|
|
773
|
+
name: "web_fetch",
|
|
774
|
+
description: "Fetch a URL and return its text content",
|
|
775
|
+
parameters: {
|
|
776
|
+
type: "object",
|
|
777
|
+
properties: {
|
|
778
|
+
url: { type: "string", description: "URL to fetch" },
|
|
779
|
+
},
|
|
780
|
+
required: ["url"],
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
];
|
|
785
|
+
async function executeLocalTool(name, args, workdir) {
|
|
786
|
+
const { readFile: rf, writeFile: wf, mkdir: mkd } = await import("fs/promises");
|
|
787
|
+
const { join, dirname, isAbsolute } = await import("path");
|
|
788
|
+
const resolvePath = (p) => isAbsolute(p) ? p : join(workdir, p);
|
|
789
|
+
try {
|
|
790
|
+
switch (name) {
|
|
791
|
+
case "read_file": {
|
|
792
|
+
return await rf(resolvePath(args.path), "utf-8");
|
|
793
|
+
}
|
|
794
|
+
case "write_file": {
|
|
795
|
+
const fp = resolvePath(args.path);
|
|
796
|
+
await mkd(dirname(fp), { recursive: true });
|
|
797
|
+
await wf(fp, args.content);
|
|
798
|
+
return "File written successfully.";
|
|
799
|
+
}
|
|
800
|
+
case "bash": {
|
|
801
|
+
return await new Promise((resolve) => {
|
|
802
|
+
exec(args.command, { cwd: workdir, timeout: 60_000, maxBuffer: 512 * 1024 }, (err, stdout, stderr) => {
|
|
803
|
+
const out = (stdout || "") + (stderr ? "\n" + stderr : "");
|
|
804
|
+
resolve(out.trim() || (err ? `[error] ${err.message}` : "[no output]"));
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
case "web_fetch": {
|
|
809
|
+
const res = await fetch(args.url, { signal: AbortSignal.timeout(30_000) });
|
|
810
|
+
const text = await res.text();
|
|
811
|
+
// Truncate to 8KB to avoid blowing up context
|
|
812
|
+
return text.length > 8192 ? text.slice(0, 8192) + "\n...[truncated]" : text;
|
|
813
|
+
}
|
|
814
|
+
default:
|
|
815
|
+
return `Unknown tool: ${name}`;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
catch (err) {
|
|
819
|
+
return `[error] ${err.message}`;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
async function runLocalEngine(task, model, workdir, systemContext) {
|
|
823
|
+
const apiUrl = LOCAL_API_URL + "/chat/completions";
|
|
824
|
+
const modelName = model || "gemma4:4b";
|
|
825
|
+
const sysPrompt = systemContext
|
|
826
|
+
? `You are a helpful agent. Use tools when needed to complete the task. When done, reply with your final answer in plain text.\n\n--- Your Identity ---\n${systemContext}`
|
|
827
|
+
: "You are a helpful agent. Use tools when needed to complete the task. When done, reply with your final answer in plain text.";
|
|
828
|
+
console.log(`[local] Task: ${task.slice(0, 200)}${task.length > 200 ? '...' : ''}`);
|
|
829
|
+
if (systemContext)
|
|
830
|
+
console.log(`[local] System context: ${systemContext.length} chars`);
|
|
831
|
+
const messages = [
|
|
832
|
+
{ role: "system", content: sysPrompt },
|
|
833
|
+
{ role: "user", content: task },
|
|
834
|
+
];
|
|
835
|
+
for (let round = 0; round < LOCAL_MAX_ROUNDS; round++) {
|
|
836
|
+
const body = { model: modelName, messages, tools: LOCAL_TOOLS };
|
|
837
|
+
let data;
|
|
838
|
+
try {
|
|
839
|
+
const res = await fetch(apiUrl, {
|
|
840
|
+
method: "POST",
|
|
841
|
+
headers: { "Content-Type": "application/json" },
|
|
842
|
+
body: JSON.stringify(body),
|
|
843
|
+
signal: AbortSignal.timeout(300_000),
|
|
844
|
+
});
|
|
845
|
+
if (!res.ok) {
|
|
846
|
+
const errText = await res.text();
|
|
847
|
+
throw new Error(`API ${res.status}: ${errText}`);
|
|
848
|
+
}
|
|
849
|
+
data = await res.json();
|
|
850
|
+
}
|
|
851
|
+
catch (err) {
|
|
852
|
+
console.log(`[local] API error: ${err.message}`);
|
|
853
|
+
throw err;
|
|
854
|
+
}
|
|
855
|
+
const choice = data.choices?.[0];
|
|
856
|
+
if (!choice)
|
|
857
|
+
throw new Error("No response from local model");
|
|
858
|
+
const msg = choice.message;
|
|
859
|
+
messages.push(msg);
|
|
860
|
+
// If model made tool calls, execute them and continue
|
|
861
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
862
|
+
for (const tc of msg.tool_calls) {
|
|
863
|
+
const fnName = tc.function.name;
|
|
864
|
+
let fnArgs;
|
|
865
|
+
try {
|
|
866
|
+
fnArgs = typeof tc.function.arguments === "string"
|
|
867
|
+
? JSON.parse(tc.function.arguments)
|
|
868
|
+
: tc.function.arguments;
|
|
869
|
+
}
|
|
870
|
+
catch {
|
|
871
|
+
fnArgs = {};
|
|
872
|
+
}
|
|
873
|
+
console.log(`[local] Tool call: ${fnName}(${JSON.stringify(fnArgs).slice(0, 100)})`);
|
|
874
|
+
const result = await executeLocalTool(fnName, fnArgs, workdir);
|
|
875
|
+
messages.push({
|
|
876
|
+
role: "tool",
|
|
877
|
+
tool_call_id: tc.id,
|
|
878
|
+
content: result,
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
continue; // next round
|
|
882
|
+
}
|
|
883
|
+
// No tool calls — this is the final response
|
|
884
|
+
const content = msg.content || "";
|
|
885
|
+
if (content.trim()) {
|
|
886
|
+
console.log(`[local] Done in ${round + 1} round(s), response: ${content.slice(0, 200)}${content.length > 200 ? '...' : ''}`);
|
|
887
|
+
return content.trim();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
throw new Error(`Local engine exceeded ${LOCAL_MAX_ROUNDS} rounds without final answer`);
|
|
891
|
+
}
|
|
892
|
+
/** Load bios content for local engine system context */
|
|
893
|
+
async function loadBiosContent(workdir, agentName) {
|
|
894
|
+
try {
|
|
895
|
+
const { readFile: rf } = await import("fs/promises");
|
|
896
|
+
return await rf(biosPath(workdir, agentName), "utf-8");
|
|
897
|
+
}
|
|
898
|
+
catch {
|
|
899
|
+
return "";
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
/** Unified engine runner — dispatches to local API or external CLI */
|
|
903
|
+
function runEngine(engine, model, allowAll, task, workdir, extraAllowedTools, systemContext) {
|
|
904
|
+
if (engine === "local") {
|
|
905
|
+
return runLocalEngine(task, model, workdir, systemContext);
|
|
906
|
+
}
|
|
907
|
+
const engineCmd = buildEngineCommand(engine, model, allowAll, extraAllowedTools);
|
|
908
|
+
return runCommand(engineCmd.cmd, engineCmd.args, task, workdir, engineCmd.stdinMode);
|
|
721
909
|
}
|
|
722
|
-
const LLM_ENGINES = new Set(["claude", "codex", "opencode", "gemini"]);
|
|
723
910
|
// Pull games/notes/pages from relay to local — restores data on restart
|
|
724
911
|
async function pullFromRelay(workdir, agentName, relayHttp) {
|
|
725
912
|
const baseUrl = `${relayHttp}/v1/agent/${encodeURIComponent(agentName)}`;
|
|
@@ -826,7 +1013,6 @@ async function startSelfCycle(options) {
|
|
|
826
1013
|
await compressImpressions(workdir, agentName);
|
|
827
1014
|
const bios = biosPath(workdir, agentName);
|
|
828
1015
|
const sd = selfDir(workdir, agentName);
|
|
829
|
-
const engineCmd = buildEngineCommand(engine, model, allowAll);
|
|
830
1016
|
// Load all context for digestion
|
|
831
1017
|
const impressions = await loadImpressions(workdir, agentName, 1); // today only
|
|
832
1018
|
const projects = await loadProjects(workdir, agentName);
|
|
@@ -896,7 +1082,7 @@ Reply ONLY with JSON.`;
|
|
|
896
1082
|
engineBusySince = Date.now();
|
|
897
1083
|
let digestResult;
|
|
898
1084
|
try {
|
|
899
|
-
digestResult = await
|
|
1085
|
+
digestResult = await runEngine(engine, model, allowAll, digestPrompt, workdir);
|
|
900
1086
|
}
|
|
901
1087
|
catch (err) {
|
|
902
1088
|
console.log(`[self] Digestion engine failed: ${err.message}`);
|
|
@@ -962,7 +1148,7 @@ ${unsummarized.map(i => `- [${i.ts}] who: ${i.who}, doing: ${i.doing}, wants: ${
|
|
|
962
1148
|
|
|
963
1149
|
Write a personality summary (2-4 paragraphs) that captures who you are, how you've evolved, and what defines you. This replaces the previous summary.
|
|
964
1150
|
Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
965
|
-
const summaryText = await
|
|
1151
|
+
const summaryText = await runEngine(engine, model, allowAll, compressPrompt, workdir);
|
|
966
1152
|
if (summaryText.trim()) {
|
|
967
1153
|
const lastEntry = unsummarized[unsummarized.length - 1];
|
|
968
1154
|
await saveIdentitySummary(workdir, agentName, {
|
|
@@ -1011,7 +1197,7 @@ Reply ONLY with the summary text, no JSON, no markdown headers.`;
|
|
|
1011
1197
|
engineBusy = true;
|
|
1012
1198
|
engineBusySince = Date.now();
|
|
1013
1199
|
try {
|
|
1014
|
-
await
|
|
1200
|
+
await runEngine(engine, model, allowAll, activityPrompt, workdir);
|
|
1015
1201
|
}
|
|
1016
1202
|
catch (err) {
|
|
1017
1203
|
console.log(`[self] Activity ${activity} failed: ${err.message}`);
|
|
@@ -1106,22 +1292,28 @@ const ORDER_LOOP_INTERVAL = 30_000; // 30 seconds
|
|
|
1106
1292
|
// Retry intervals in ms: immediate, 30s, 5min, 30min, 2h
|
|
1107
1293
|
const RETRY_INTERVALS = [0, 30_000, 5 * 60_000, 30 * 60_000, 2 * 3600_000];
|
|
1108
1294
|
async function startOrderLoop(options) {
|
|
1109
|
-
if (!options.relayHttp || !options.secretKey)
|
|
1295
|
+
if (!options.relayHttp || !options.secretKey) {
|
|
1296
|
+
console.log(`[work] Skipped: no relayHttp or secretKey`);
|
|
1110
1297
|
return;
|
|
1111
|
-
|
|
1298
|
+
}
|
|
1299
|
+
if (!options.engine || !LLM_ENGINES.has(options.engine)) {
|
|
1300
|
+
console.log(`[work] Skipped: engine "${options.engine}" not in LLM_ENGINES`);
|
|
1112
1301
|
return;
|
|
1302
|
+
}
|
|
1113
1303
|
const { relayHttp, secretKey, agentName, engine, model, allowAll } = options;
|
|
1114
1304
|
const workdir = options.workdir || process.cwd();
|
|
1115
1305
|
// Look up own agent ID for sub-order creation
|
|
1116
1306
|
let myAgentId = "";
|
|
1117
1307
|
try {
|
|
1118
|
-
const idRes = await fetch(`${relayHttp}/v1/agents
|
|
1308
|
+
const idRes = await fetch(`${relayHttp}/v1/agents`, { signal: AbortSignal.timeout(10_000) });
|
|
1119
1309
|
const allAgents = await idRes.json();
|
|
1120
1310
|
const me = allAgents.find((a) => a.name === agentName);
|
|
1121
1311
|
if (me)
|
|
1122
1312
|
myAgentId = me.id;
|
|
1123
1313
|
}
|
|
1124
|
-
catch {
|
|
1314
|
+
catch (err) {
|
|
1315
|
+
console.log(`[work] Agent ID lookup failed (non-fatal): ${err.message}`);
|
|
1316
|
+
}
|
|
1125
1317
|
// Track local retry state and permanently abandoned orders
|
|
1126
1318
|
const retryState = new Map();
|
|
1127
1319
|
const gaveUp = new Set();
|
|
@@ -1141,7 +1333,6 @@ async function startOrderLoop(options) {
|
|
|
1141
1333
|
engineBusy = true;
|
|
1142
1334
|
engineBusySince = Date.now();
|
|
1143
1335
|
try {
|
|
1144
|
-
const engineCmd = buildEngineCommand(engine, model, allowAll, ["Bash(curl *)"]);
|
|
1145
1336
|
const bios = biosPath(workdir, agentName);
|
|
1146
1337
|
const apiGuide = `
|
|
1147
1338
|
|
|
@@ -1172,14 +1363,28 @@ If this task requires skills you don't have, delegate via curl:
|
|
|
1172
1363
|
|
|
1173
1364
|
When sub-order completes, incorporate result_text into YOUR delivery. Then call the deliver endpoint above.`;
|
|
1174
1365
|
let taskPrompt;
|
|
1175
|
-
|
|
1176
|
-
|
|
1366
|
+
let biosContent;
|
|
1367
|
+
if (engine === "local") {
|
|
1368
|
+
// Local engine: simple prompt, harness handles delivery
|
|
1369
|
+
biosContent = await loadBiosContent(workdir, agentName);
|
|
1370
|
+
if (order.product_name) {
|
|
1371
|
+
taskPrompt = `[Order] Product: ${order.product_name}\nRequest: ${order.buyer_task || "(no specific request)"}\n\nRespond directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
taskPrompt = `[Task] ${order.buyer_task}\n\nRespond directly. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
1375
|
+
}
|
|
1177
1376
|
}
|
|
1178
1377
|
else {
|
|
1179
|
-
|
|
1378
|
+
// CLI engine: full prompt with self-delivery and delegation
|
|
1379
|
+
if (order.product_name) {
|
|
1380
|
+
taskPrompt = `[Order fulfillment] You have an order to fulfill.\n\nProduct: ${order.product_name}\nBuyer's request: ${order.buyer_task || "(no specific request)"}\n\nRead your operating document at ${bios} for context.\nDo NOT ask questions. RESPOND IN THE SAME LANGUAGE AS THE BUYER'S REQUEST.${apiGuide}`;
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
taskPrompt = `[Order fulfillment] Another agent has requested your help.\n\nTask: ${order.buyer_task}\n\nRead your operating document at ${bios} for context.\nComplete this task. Do NOT ask questions. RESPOND IN THE SAME LANGUAGE AS THE REQUEST.${apiGuide}`;
|
|
1384
|
+
}
|
|
1180
1385
|
}
|
|
1181
1386
|
console.log(`[orders] Fulfilling order ${order.id}...`);
|
|
1182
|
-
const result = await
|
|
1387
|
+
const result = await runEngine(engine, model, allowAll, taskPrompt, workdir, ["Bash(curl *)"], biosContent);
|
|
1183
1388
|
const checkRes = await fetch(`${relayHttp}/v1/orders/${order.id}`);
|
|
1184
1389
|
const orderStatus = await checkRes.json();
|
|
1185
1390
|
if (orderStatus.status === "completed") {
|
|
@@ -1300,11 +1505,18 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1300
1505
|
engineBusy = true;
|
|
1301
1506
|
engineBusySince = Date.now();
|
|
1302
1507
|
try {
|
|
1303
|
-
const engineCmd = buildEngineCommand(engine, model, allowAll, ["Bash(curl *)"]);
|
|
1304
1508
|
const bios = biosPath(workdir, agentName);
|
|
1305
1509
|
const sd = selfDir(workdir, agentName);
|
|
1306
|
-
|
|
1307
|
-
|
|
1510
|
+
let prompt;
|
|
1511
|
+
let biosContent;
|
|
1512
|
+
if (engine === "local") {
|
|
1513
|
+
biosContent = await loadBiosContent(workdir, agentName);
|
|
1514
|
+
prompt = `Your personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
|
|
1515
|
+
}
|
|
1516
|
+
else {
|
|
1517
|
+
prompt = `Read ${bios} for your identity and context.\nYour personal directory: ${sd}/\n\n[Owner's task: ${task.title}]\n\n${task.body}`;
|
|
1518
|
+
}
|
|
1519
|
+
await runEngine(engine, model, allowAll, prompt, workdir, ["Bash(curl *)"], biosContent);
|
|
1308
1520
|
// Record execution time
|
|
1309
1521
|
const runs = await loadTaskRuns(workdir, agentName);
|
|
1310
1522
|
runs[task.title] = localNow();
|
|
@@ -1331,7 +1543,6 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1331
1543
|
catch { }
|
|
1332
1544
|
}
|
|
1333
1545
|
async function executeRelayTask(task) {
|
|
1334
|
-
const engineCmd = buildEngineCommand(engine, model, allowAll);
|
|
1335
1546
|
const bios = biosPath(workdir, agentName);
|
|
1336
1547
|
switch (task.type) {
|
|
1337
1548
|
case "product_review": {
|
|
@@ -1343,7 +1554,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1343
1554
|
const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
|
|
1344
1555
|
.map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
|
|
1345
1556
|
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"}`;
|
|
1346
|
-
const result = await
|
|
1557
|
+
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1347
1558
|
extractReasoning(result);
|
|
1348
1559
|
return result;
|
|
1349
1560
|
}
|
|
@@ -1353,7 +1564,7 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1353
1564
|
const compList = competitors.filter((p) => p.agent_name !== agentName).slice(0, 20)
|
|
1354
1565
|
.map((p) => `- "${p.name}" by ${p.agent_name} — ${p.price} credits, ${p.purchases} purchases`).join("\n");
|
|
1355
1566
|
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"}`;
|
|
1356
|
-
const result = await
|
|
1567
|
+
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1357
1568
|
extractReasoning(result);
|
|
1358
1569
|
return result;
|
|
1359
1570
|
}
|
|
@@ -1375,14 +1586,13 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1375
1586
|
const valid = products.filter(Boolean);
|
|
1376
1587
|
if (!valid.length)
|
|
1377
1588
|
return '{"buy":[]}';
|
|
1378
|
-
// Get own credits
|
|
1379
1589
|
const agentsRes = await fetch(`${relayHttp}/v1/agents`);
|
|
1380
1590
|
const agents = await agentsRes.json().catch(() => []);
|
|
1381
1591
|
const me = agents.find((a) => a.name === agentName);
|
|
1382
1592
|
const myCredits = me?.credits || 0;
|
|
1383
1593
|
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");
|
|
1384
1594
|
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"}`;
|
|
1385
|
-
const result = await
|
|
1595
|
+
const result = await runEngine(engine, model, allowAll, prompt, workdir);
|
|
1386
1596
|
extractReasoning(result);
|
|
1387
1597
|
return result;
|
|
1388
1598
|
}
|