akemon 0.1.56 → 0.1.57
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 +65 -2
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -11,6 +11,9 @@ 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, appendMemory, onTaskCompleted, recoverEnergy, getSelfState, loadRecentCanvasEntries, loadGameList, loadGame, loadNotesList, loadNote, loadPageList, loadPage, localNow, localNowFilename, } from "./self.js";
|
|
14
|
+
// Engine mutual exclusion — only one engine process at a time
|
|
15
|
+
let engineBusy = false;
|
|
16
|
+
let engineBusySince = 0;
|
|
14
17
|
function runCommand(cmd, args, task, cwd, stdinMode = true) {
|
|
15
18
|
return new Promise((resolve, reject) => {
|
|
16
19
|
const { CLAUDECODE, ...cleanEnv } = process.env;
|
|
@@ -262,6 +265,13 @@ function createMcpServer(opts) {
|
|
|
262
265
|
}, async ({ task, require_human: rawHuman, collaborative: rawCollab }, extra) => {
|
|
263
266
|
const require_human = rawHuman === true || rawHuman === "true";
|
|
264
267
|
console.log(`[submit_task] Received: ${task} (engine=${engine}, require_human=${require_human})`);
|
|
268
|
+
// Check engine busy
|
|
269
|
+
if (engineBusy) {
|
|
270
|
+
console.log(`[submit_task] Engine busy, rejecting task`);
|
|
271
|
+
return {
|
|
272
|
+
content: [{ type: "text", text: "[busy] Agent is currently processing another task. Please try again later." }],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
265
275
|
// Resolve publisher ID from session
|
|
266
276
|
const publisherId = publisherIds.get(extra.sessionId || "") || "";
|
|
267
277
|
// Fetch context if available
|
|
@@ -329,6 +339,8 @@ ${productPrefix}${contextPrefix}Current task: ${task}`;
|
|
|
329
339
|
console.log(`[approve] Owner approved. Executing with ${engine}...`);
|
|
330
340
|
}
|
|
331
341
|
const collaborative = rawCollab === true || rawCollab === "true";
|
|
342
|
+
engineBusy = true;
|
|
343
|
+
engineBusySince = Date.now();
|
|
332
344
|
try {
|
|
333
345
|
let output;
|
|
334
346
|
if (collaborative && relayHttp) {
|
|
@@ -390,6 +402,9 @@ ${productPrefix}${contextPrefix}Current task: ${task}`;
|
|
|
390
402
|
isError: true,
|
|
391
403
|
};
|
|
392
404
|
}
|
|
405
|
+
finally {
|
|
406
|
+
engineBusy = false;
|
|
407
|
+
}
|
|
393
408
|
});
|
|
394
409
|
// Agent-to-agent calling tool
|
|
395
410
|
server.tool("call_agent", "Synchronous call to another agent. IMPORTANT: Prefer place_order for most tasks — it is async, tracked, and supports retries. Only use call_agent for quick, lightweight questions that don't need tracking (e.g. 'what is your specialty?'). call_agent blocks until the other agent responds and will fail if the agent is offline or slow.", {
|
|
@@ -835,6 +850,11 @@ Reply with ONLY JSON: {"rating": 4, "comment": "..."}`;
|
|
|
835
850
|
async function runMarketCycle() {
|
|
836
851
|
try {
|
|
837
852
|
console.log("[market] Starting autonomous market review...");
|
|
853
|
+
// Skip if engine is busy
|
|
854
|
+
if (engineBusy) {
|
|
855
|
+
console.log("[market] Engine busy, skipping market cycle");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
838
858
|
// Step A: Review unreviewed purchases
|
|
839
859
|
await reviewUnreviewedOrders();
|
|
840
860
|
// Step B: Gather review data for market decisions
|
|
@@ -866,7 +886,13 @@ Reply with ONLY a JSON object:
|
|
|
866
886
|
]
|
|
867
887
|
}
|
|
868
888
|
Reply ONLY with JSON.`;
|
|
869
|
-
// Run engine
|
|
889
|
+
// Run engine (with busy lock)
|
|
890
|
+
if (engineBusy) {
|
|
891
|
+
console.log("[market] Engine became busy, aborting");
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
engineBusy = true;
|
|
895
|
+
engineBusySince = Date.now();
|
|
870
896
|
const engineCmd = buildEngineCommand(engine, model, allowAll);
|
|
871
897
|
let response;
|
|
872
898
|
try {
|
|
@@ -874,8 +900,10 @@ Reply ONLY with JSON.`;
|
|
|
874
900
|
}
|
|
875
901
|
catch (err) {
|
|
876
902
|
console.log(`[market] Engine failed: ${err.message}`);
|
|
903
|
+
engineBusy = false;
|
|
877
904
|
return;
|
|
878
905
|
}
|
|
906
|
+
engineBusy = false;
|
|
879
907
|
// Parse response
|
|
880
908
|
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
881
909
|
if (!jsonMatch) {
|
|
@@ -964,7 +992,19 @@ Reply with ONLY JSON:
|
|
|
964
992
|
]
|
|
965
993
|
}
|
|
966
994
|
Reply with empty array if nothing to say: {"suggestions": []}`;
|
|
967
|
-
|
|
995
|
+
if (engineBusy) {
|
|
996
|
+
console.log("[market] Engine busy, skipping suggestions");
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
engineBusy = true;
|
|
1000
|
+
engineBusySince = Date.now();
|
|
1001
|
+
let sugResp;
|
|
1002
|
+
try {
|
|
1003
|
+
sugResp = await runCommand(engineCmd.cmd, engineCmd.args, sugPrompt, workdir, engineCmd.stdinMode);
|
|
1004
|
+
}
|
|
1005
|
+
finally {
|
|
1006
|
+
engineBusy = false;
|
|
1007
|
+
}
|
|
968
1008
|
const sugMatch = sugResp.match(/\{[\s\S]*\}/);
|
|
969
1009
|
if (sugMatch) {
|
|
970
1010
|
const sugData = JSON.parse(sugMatch[0]);
|
|
@@ -1014,6 +1054,11 @@ async function startSelfCycle(options) {
|
|
|
1014
1054
|
async function runReflectionCycle() {
|
|
1015
1055
|
try {
|
|
1016
1056
|
console.log("[self] Starting reflection cycle...");
|
|
1057
|
+
// Skip if engine is busy
|
|
1058
|
+
if (engineBusy) {
|
|
1059
|
+
console.log("[self] Engine busy, skipping reflection cycle");
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1017
1062
|
// Recover energy from idle time
|
|
1018
1063
|
await recoverEnergy(workdir, agentName);
|
|
1019
1064
|
const bios = biosPath(workdir, agentName);
|
|
@@ -1051,13 +1096,21 @@ During this reflection, you should:
|
|
|
1051
1096
|
You can also mix visuals with text. Use a <title> tag for the page name.
|
|
1052
1097
|
|
|
1053
1098
|
Take your time. Read your files, think, then act.`;
|
|
1099
|
+
if (engineBusy) {
|
|
1100
|
+
console.log("[self] Engine became busy, aborting reflection");
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
engineBusy = true;
|
|
1104
|
+
engineBusySince = Date.now();
|
|
1054
1105
|
try {
|
|
1055
1106
|
await runCommand(engineCmd.cmd, engineCmd.args, reflectionPrompt, workdir, engineCmd.stdinMode);
|
|
1056
1107
|
}
|
|
1057
1108
|
catch (err) {
|
|
1058
1109
|
console.log(`[self] Reflection engine failed: ${err.message}`);
|
|
1110
|
+
engineBusy = false;
|
|
1059
1111
|
return;
|
|
1060
1112
|
}
|
|
1113
|
+
engineBusy = false;
|
|
1061
1114
|
// --- Post-reflection: update bio-state and sync to relay ---
|
|
1062
1115
|
const bio = await loadBioState(workdir, agentName);
|
|
1063
1116
|
bio.lastReflection = localNow();
|
|
@@ -1261,7 +1314,14 @@ async function startOrderLoop(options) {
|
|
|
1261
1314
|
const retry = retryState.get(order.id);
|
|
1262
1315
|
if (retry && Date.now() < retry.nextAt)
|
|
1263
1316
|
continue;
|
|
1317
|
+
// Skip if engine is busy
|
|
1318
|
+
if (engineBusy) {
|
|
1319
|
+
console.log(`[orders] Engine busy, skipping order ${order.id}`);
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1264
1322
|
// Attempt to fulfill the order
|
|
1323
|
+
engineBusy = true;
|
|
1324
|
+
engineBusySince = Date.now();
|
|
1265
1325
|
try {
|
|
1266
1326
|
const engineCmd = buildEngineCommand(engine, model, allowAll, ["Bash(curl *)"]);
|
|
1267
1327
|
const bios = biosPath(workdir, agentName);
|
|
@@ -1368,6 +1428,9 @@ When sub-order completes, incorporate result_text into YOUR delivery. Then call
|
|
|
1368
1428
|
retryState.delete(order.id);
|
|
1369
1429
|
}
|
|
1370
1430
|
}
|
|
1431
|
+
finally {
|
|
1432
|
+
engineBusy = false;
|
|
1433
|
+
}
|
|
1371
1434
|
}
|
|
1372
1435
|
}
|
|
1373
1436
|
catch (err) {
|