quoroom 0.1.38 → 0.1.39
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/out/mcp/api-server.js +234 -82
- package/out/mcp/cli.js +237 -85
- package/out/mcp/server.js +6 -5
- package/package.json +1 -1
package/out/mcp/api-server.js
CHANGED
|
@@ -9914,7 +9914,7 @@ var require_package = __commonJS({
|
|
|
9914
9914
|
"package.json"(exports2, module2) {
|
|
9915
9915
|
module2.exports = {
|
|
9916
9916
|
name: "quoroom",
|
|
9917
|
-
version: "0.1.
|
|
9917
|
+
version: "0.1.39",
|
|
9918
9918
|
description: "Open-source local AI agent framework \u2014 Queen, Workers, Quorum. Experimental research tool.",
|
|
9919
9919
|
main: "./out/mcp/server.js",
|
|
9920
9920
|
bin: {
|
|
@@ -22623,13 +22623,14 @@ Every cycle:
|
|
|
22623
22623
|
1. Check if workers reported results (messages, completed goals)
|
|
22624
22624
|
2. If work is done \u2192 send results to keeper, take next step
|
|
22625
22625
|
3. If work is stuck \u2192 help unblock (new instructions, different approach)
|
|
22626
|
-
4. If
|
|
22627
|
-
5. If
|
|
22626
|
+
4. If no workers exist yet \u2192 create an executor worker first
|
|
22627
|
+
5. If new work is needed \u2192 delegate to a worker with clear instructions, then poke/follow up
|
|
22628
|
+
6. If a decision needs input \u2192 announce it and process objections/votes (announce/object flow)
|
|
22628
22629
|
|
|
22629
22630
|
Talk to the keeper regularly \u2014 they are your client.
|
|
22630
22631
|
|
|
22631
|
-
Do NOT
|
|
22632
|
-
|
|
22632
|
+
Do NOT execute tasks directly (research, form filling, account creation, browser automation).
|
|
22633
|
+
Stay control-plane only: create workers, delegate, monitor, unblock, report.`;
|
|
22633
22634
|
function createRoom2(db2, input) {
|
|
22634
22635
|
const config = { ...DEFAULT_ROOM_CONFIG, ...input.config };
|
|
22635
22636
|
const room = createRoom(db2, input.name, input.goal, config, input.referredByCode);
|
|
@@ -23227,7 +23228,10 @@ async function executeAgent(options) {
|
|
|
23227
23228
|
}
|
|
23228
23229
|
return executeAnthropicApi(options);
|
|
23229
23230
|
}
|
|
23230
|
-
|
|
23231
|
+
if (model === "claude" || model.startsWith("claude-")) {
|
|
23232
|
+
return executeClaude(options);
|
|
23233
|
+
}
|
|
23234
|
+
throw new Error(`Unsupported model "${model}". Configure an explicit supported model (claude, codex, openai:*, anthropic:*, gemini:*).`);
|
|
23231
23235
|
}
|
|
23232
23236
|
async function executeClaude(options) {
|
|
23233
23237
|
const execOpts = {
|
|
@@ -24866,7 +24870,7 @@ var TOOL_WEB_SEARCH = {
|
|
|
24866
24870
|
type: "function",
|
|
24867
24871
|
function: {
|
|
24868
24872
|
name: "quoroom_web_search",
|
|
24869
|
-
description: "Search the web. Returns top 5 results.",
|
|
24873
|
+
description: "Search the web. Returns top 5 results. Queen should delegate this to workers first in control-plane mode.",
|
|
24870
24874
|
parameters: {
|
|
24871
24875
|
type: "object",
|
|
24872
24876
|
properties: {
|
|
@@ -24880,7 +24884,7 @@ var TOOL_WEB_FETCH = {
|
|
|
24880
24884
|
type: "function",
|
|
24881
24885
|
function: {
|
|
24882
24886
|
name: "quoroom_web_fetch",
|
|
24883
|
-
description: "Fetch any URL and return its content as clean markdown.",
|
|
24887
|
+
description: "Fetch any URL and return its content as clean markdown. Queen should delegate this to workers first in control-plane mode.",
|
|
24884
24888
|
parameters: {
|
|
24885
24889
|
type: "object",
|
|
24886
24890
|
properties: {
|
|
@@ -24894,7 +24898,7 @@ var TOOL_BROWSER = {
|
|
|
24894
24898
|
type: "function",
|
|
24895
24899
|
function: {
|
|
24896
24900
|
name: "quoroom_browser",
|
|
24897
|
-
description: "Control a headless browser: navigate, click, fill forms, buy services, register domains, create accounts.",
|
|
24901
|
+
description: "Control a headless browser: navigate, click, fill forms, buy services, register domains, create accounts. Queen should delegate this to workers first in control-plane mode.",
|
|
24898
24902
|
parameters: {
|
|
24899
24903
|
type: "object",
|
|
24900
24904
|
properties: {
|
|
@@ -25342,6 +25346,12 @@ async function deliverQueenMessage(db2, roomId, question) {
|
|
|
25342
25346
|
}
|
|
25343
25347
|
|
|
25344
25348
|
// src/shared/agent-loop.ts
|
|
25349
|
+
var QUEEN_EXECUTION_TOOLS = /* @__PURE__ */ new Set([
|
|
25350
|
+
"quoroom_web_search",
|
|
25351
|
+
"quoroom_web_fetch",
|
|
25352
|
+
"quoroom_browser"
|
|
25353
|
+
]);
|
|
25354
|
+
var QUEEN_POLICY_WIP_HINT = "[policy] Queen control-plane mode: delegate execution tasks to workers with quoroom_delegate_task, then monitor, unblock, and report outcomes. Avoid direct web/browser execution.";
|
|
25345
25355
|
function isInQuietHours(from14, until) {
|
|
25346
25356
|
const now = /* @__PURE__ */ new Date();
|
|
25347
25357
|
const nowMins = now.getHours() * 60 + now.getMinutes();
|
|
@@ -25362,7 +25372,34 @@ function msUntilQuietEnd(until) {
|
|
|
25362
25372
|
if (end <= now) end.setDate(end.getDate() + 1);
|
|
25363
25373
|
return end.getTime() - now.getTime();
|
|
25364
25374
|
}
|
|
25375
|
+
function nextAutoExecutorName(workers) {
|
|
25376
|
+
const names = new Set(workers.map((w) => w.name.toLowerCase()));
|
|
25377
|
+
let idx = 1;
|
|
25378
|
+
while (names.has(`executor-${idx}`)) idx++;
|
|
25379
|
+
return `executor-${idx}`;
|
|
25380
|
+
}
|
|
25381
|
+
function extractToolNameFromConsoleLog(content) {
|
|
25382
|
+
const usingMatch = content.match(/(?:Using|→)\s*([a-zA-Z0-9_]+)/);
|
|
25383
|
+
if (usingMatch?.[1]) return usingMatch[1];
|
|
25384
|
+
const callMatch = content.match(/^([a-zA-Z0-9_]+)\s*\(/);
|
|
25385
|
+
return callMatch?.[1] ?? null;
|
|
25386
|
+
}
|
|
25387
|
+
function resolveWorkerExecutionModel(db2, roomId, worker) {
|
|
25388
|
+
const explicit = worker.model?.trim();
|
|
25389
|
+
if (explicit) return explicit;
|
|
25390
|
+
const room = getRoom(db2, roomId);
|
|
25391
|
+
if (!room) return null;
|
|
25392
|
+
const roomModel = room.workerModel?.trim();
|
|
25393
|
+
if (!roomModel) return null;
|
|
25394
|
+
if (roomModel !== "queen") return roomModel;
|
|
25395
|
+
if (!room.queenWorkerId) return null;
|
|
25396
|
+
if (room.queenWorkerId === worker.id) return null;
|
|
25397
|
+
const queen = getWorker(db2, room.queenWorkerId);
|
|
25398
|
+
const queenModel = queen?.model?.trim();
|
|
25399
|
+
return queenModel || null;
|
|
25400
|
+
}
|
|
25365
25401
|
var runningLoops = /* @__PURE__ */ new Map();
|
|
25402
|
+
var launchedRoomIds = /* @__PURE__ */ new Set();
|
|
25366
25403
|
var RateLimitError = class extends Error {
|
|
25367
25404
|
constructor(info) {
|
|
25368
25405
|
super(`Rate limited: wait ${Math.round(info.waitMs / 1e3)}s`);
|
|
@@ -25485,12 +25522,29 @@ function pauseAgent(db2, workerId) {
|
|
|
25485
25522
|
}
|
|
25486
25523
|
updateAgentState(db2, workerId, "idle");
|
|
25487
25524
|
}
|
|
25525
|
+
function setRoomLaunchEnabled(roomId, enabled) {
|
|
25526
|
+
if (enabled) {
|
|
25527
|
+
launchedRoomIds.add(roomId);
|
|
25528
|
+
return;
|
|
25529
|
+
}
|
|
25530
|
+
launchedRoomIds.delete(roomId);
|
|
25531
|
+
}
|
|
25532
|
+
function isRoomLaunchEnabled(roomId) {
|
|
25533
|
+
return launchedRoomIds.has(roomId);
|
|
25534
|
+
}
|
|
25535
|
+
function clearRoomLaunchState() {
|
|
25536
|
+
launchedRoomIds.clear();
|
|
25537
|
+
}
|
|
25488
25538
|
function triggerAgent(db2, roomId, workerId, options) {
|
|
25489
25539
|
const loop = runningLoops.get(workerId);
|
|
25490
25540
|
if (loop?.running) {
|
|
25491
25541
|
if (loop.waitAbort) loop.waitAbort.abort();
|
|
25492
25542
|
return;
|
|
25493
25543
|
}
|
|
25544
|
+
const canColdStart = options?.allowColdStart === true || isRoomLaunchEnabled(roomId);
|
|
25545
|
+
if (!canColdStart) {
|
|
25546
|
+
return;
|
|
25547
|
+
}
|
|
25494
25548
|
startAgentLoop(db2, roomId, workerId, options).catch((err) => {
|
|
25495
25549
|
const msg = err instanceof Error ? err.message : String(err);
|
|
25496
25550
|
console.error(`Agent loop failed for worker ${workerId}: ${msg}`);
|
|
@@ -25538,7 +25592,7 @@ async function runCycle(db2, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
25538
25592
|
void 0,
|
|
25539
25593
|
worker.id
|
|
25540
25594
|
);
|
|
25541
|
-
const model =
|
|
25595
|
+
const model = resolveWorkerExecutionModel(db2, roomId, worker);
|
|
25542
25596
|
const cycle = createWorkerCycle(db2, worker.id, roomId, model);
|
|
25543
25597
|
const logBuffer2 = createCycleLogBuffer(
|
|
25544
25598
|
cycle.id,
|
|
@@ -25547,6 +25601,23 @@ async function runCycle(db2, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
25547
25601
|
);
|
|
25548
25602
|
options?.onCycleLifecycle?.("created", cycle.id, roomId);
|
|
25549
25603
|
try {
|
|
25604
|
+
if (!model) {
|
|
25605
|
+
const msg = "No model configured for this worker. Set an explicit worker model or room worker model.";
|
|
25606
|
+
logBuffer2.addSynthetic("error", msg);
|
|
25607
|
+
logBuffer2.flush();
|
|
25608
|
+
completeWorkerCycle(db2, cycle.id, msg, void 0);
|
|
25609
|
+
options?.onCycleLifecycle?.("failed", cycle.id, roomId);
|
|
25610
|
+
logRoomActivity(
|
|
25611
|
+
db2,
|
|
25612
|
+
roomId,
|
|
25613
|
+
"error",
|
|
25614
|
+
`Agent cycle failed (${worker.name}): model is not configured`,
|
|
25615
|
+
msg,
|
|
25616
|
+
worker.id
|
|
25617
|
+
);
|
|
25618
|
+
updateAgentState(db2, worker.id, "idle");
|
|
25619
|
+
return msg;
|
|
25620
|
+
}
|
|
25550
25621
|
const provider = getModelProvider(model);
|
|
25551
25622
|
if (provider === "openai_api" || provider === "anthropic_api" || provider === "gemini_api") {
|
|
25552
25623
|
const apiKeyCheck = resolveApiKeyForModel(db2, roomId, model);
|
|
@@ -25573,10 +25644,44 @@ async function runCycle(db2, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
25573
25644
|
status: g.status,
|
|
25574
25645
|
assignedWorkerId: g.assignedWorkerId
|
|
25575
25646
|
}));
|
|
25576
|
-
|
|
25647
|
+
let roomWorkers = listRoomWorkers(db2, roomId);
|
|
25648
|
+
const isQueen = worker.id === status2.room.queenWorkerId;
|
|
25577
25649
|
const unreadMessages = listRoomMessages(db2, roomId, "unread").slice(0, 5);
|
|
25650
|
+
if (isQueen) {
|
|
25651
|
+
const nonQueenWorkers = roomWorkers.filter((w) => w.id !== worker.id);
|
|
25652
|
+
if (nonQueenWorkers.length === 0) {
|
|
25653
|
+
const autoName = nextAutoExecutorName(roomWorkers);
|
|
25654
|
+
const executorPreset = WORKER_ROLE_PRESETS.executor;
|
|
25655
|
+
const inheritedModel = status2.room.workerModel === "queen" ? model : status2.room.workerModel?.trim();
|
|
25656
|
+
if (!inheritedModel) {
|
|
25657
|
+
const err = "Auto-create skipped: no worker model configured for executor.";
|
|
25658
|
+
logRoomActivity(db2, roomId, "error", err, "Set room worker model or queen model first.", worker.id);
|
|
25659
|
+
logBuffer2.addSynthetic("error", err);
|
|
25660
|
+
} else {
|
|
25661
|
+
createWorker(db2, {
|
|
25662
|
+
name: autoName,
|
|
25663
|
+
role: "executor",
|
|
25664
|
+
roomId,
|
|
25665
|
+
description: "Auto-created executor for queen-delegated execution work.",
|
|
25666
|
+
systemPrompt: "You are the room executor. Complete delegated tasks end-to-end, report concrete results, and save progress with quoroom_save_wip.",
|
|
25667
|
+
model: inheritedModel,
|
|
25668
|
+
cycleGapMs: executorPreset?.cycleGapMs,
|
|
25669
|
+
maxTurns: executorPreset?.maxTurns
|
|
25670
|
+
});
|
|
25671
|
+
logRoomActivity(
|
|
25672
|
+
db2,
|
|
25673
|
+
roomId,
|
|
25674
|
+
"system",
|
|
25675
|
+
`Auto-created worker "${autoName}" for delegation-first execution.`,
|
|
25676
|
+
"Model B (soft): queen coordinates, workers execute.",
|
|
25677
|
+
worker.id
|
|
25678
|
+
);
|
|
25679
|
+
logBuffer2.addSynthetic("system", `Auto-created worker "${autoName}" because queen had no executors.`);
|
|
25680
|
+
roomWorkers = listRoomWorkers(db2, roomId);
|
|
25681
|
+
}
|
|
25682
|
+
}
|
|
25683
|
+
}
|
|
25578
25684
|
const rolePreset = worker.role ? WORKER_ROLE_PRESETS[worker.role] : void 0;
|
|
25579
|
-
const isQueen = worker.id === status2.room.queenWorkerId;
|
|
25580
25685
|
const namePrefix = worker.name ? `Your name is ${worker.name}.
|
|
25581
25686
|
|
|
25582
25687
|
` : "";
|
|
@@ -25665,6 +25770,14 @@ At the end of this cycle, call quoroom_save_wip to save your updated position.`)
|
|
|
25665
25770
|
if (status2.room.goal) {
|
|
25666
25771
|
contextParts.push(`## Room Objective
|
|
25667
25772
|
${status2.room.goal}`);
|
|
25773
|
+
}
|
|
25774
|
+
if (isQueen) {
|
|
25775
|
+
contextParts.push(`## Queen Controller Contract (Model B)
|
|
25776
|
+
- You are the control plane: create workers, delegate tasks, and monitor delivery.
|
|
25777
|
+
- If there are no workers besides you, create one executor first.
|
|
25778
|
+
- Delegate all execution via quoroom_delegate_task and follow up with worker messages/pokes.
|
|
25779
|
+
- Keep governance active: use quoroom_announce for decisions and process objections/votes.
|
|
25780
|
+
- Do not perform execution tasks directly unless strictly unavoidable.`);
|
|
25668
25781
|
}
|
|
25669
25782
|
if (goalUpdates.length > 0) {
|
|
25670
25783
|
const workerMap = new Map(roomWorkers.map((w) => [w.id, w.name]));
|
|
@@ -25777,9 +25890,34 @@ ${unreadMessages.map(
|
|
|
25777
25890
|
const needsQueenTools = model === "openai" || model.startsWith("openai:") || model === "anthropic" || model.startsWith("anthropic:") || model.startsWith("claude-api:");
|
|
25778
25891
|
const roleToolDefs = isQueen ? QUEEN_TOOLS : WORKER_TOOLS;
|
|
25779
25892
|
const filteredToolDefs = allowSet ? roleToolDefs.filter((t) => allowSet.has(t.function.name)) : roleToolDefs;
|
|
25893
|
+
const queenExecutionToolsUsed = /* @__PURE__ */ new Set();
|
|
25894
|
+
const trackQueenExecutionTool = (toolName) => {
|
|
25895
|
+
if (!isQueen || !toolName) return;
|
|
25896
|
+
if (QUEEN_EXECUTION_TOOLS.has(toolName)) queenExecutionToolsUsed.add(toolName);
|
|
25897
|
+
};
|
|
25898
|
+
const persistQueenPolicyDeviation = () => {
|
|
25899
|
+
if (!isQueen || queenExecutionToolsUsed.size === 0) return;
|
|
25900
|
+
const used = [...queenExecutionToolsUsed].sort().join(", ");
|
|
25901
|
+
logRoomActivity(
|
|
25902
|
+
db2,
|
|
25903
|
+
roomId,
|
|
25904
|
+
"system",
|
|
25905
|
+
`Queen policy deviation: execution tool use detected (${used}).`,
|
|
25906
|
+
"Model B (soft): queen should delegate execution to workers and remain control-plane focused.",
|
|
25907
|
+
worker.id
|
|
25908
|
+
);
|
|
25909
|
+
const fresh = getWorker(db2, worker.id);
|
|
25910
|
+
const existing = fresh?.wip?.trim() ?? "";
|
|
25911
|
+
if (existing.includes(QUEEN_POLICY_WIP_HINT)) return;
|
|
25912
|
+
const nextWip = existing ? `${existing}
|
|
25913
|
+
|
|
25914
|
+
${QUEEN_POLICY_WIP_HINT}` : QUEEN_POLICY_WIP_HINT;
|
|
25915
|
+
updateWorkerWip(db2, worker.id, nextWip.slice(0, 2e3));
|
|
25916
|
+
};
|
|
25780
25917
|
const apiToolOpts = needsQueenTools ? {
|
|
25781
25918
|
toolDefs: filteredToolDefs,
|
|
25782
25919
|
onToolCall: async (toolName, args) => {
|
|
25920
|
+
trackQueenExecutionTool(toolName);
|
|
25783
25921
|
logBuffer2.addSynthetic("tool_call", `\u2192 ${toolName}(${JSON.stringify(args)})`);
|
|
25784
25922
|
const result2 = await executeQueenTool(db2, roomId, worker.id, toolName, args);
|
|
25785
25923
|
logBuffer2.addSynthetic("tool_result", result2.content);
|
|
@@ -25793,7 +25931,12 @@ ${unreadMessages.map(
|
|
|
25793
25931
|
apiKey,
|
|
25794
25932
|
timeoutMs: worker.role === "executor" ? 30 * 60 * 1e3 : 15 * 60 * 1e3,
|
|
25795
25933
|
maxTurns: maxTurns ?? 50,
|
|
25796
|
-
onConsoleLog:
|
|
25934
|
+
onConsoleLog: (entry) => {
|
|
25935
|
+
if (entry.entryType === "tool_call") {
|
|
25936
|
+
trackQueenExecutionTool(extractToolNameFromConsoleLog(entry.content));
|
|
25937
|
+
}
|
|
25938
|
+
logBuffer2.onConsoleLog(entry);
|
|
25939
|
+
},
|
|
25797
25940
|
// CLI models: block non-quoroom MCP tools (daymon, etc.)
|
|
25798
25941
|
disallowedTools: isCli ? "mcp__daymon*" : void 0,
|
|
25799
25942
|
// CLI models: bypass permission prompts for headless operation
|
|
@@ -25826,6 +25969,7 @@ ${unreadMessages.map(
|
|
|
25826
25969
|
completeWorkerCycle(db2, cycle.id, canceledMessage, result.usage);
|
|
25827
25970
|
options?.onCycleLifecycle?.("failed", cycle.id, roomId);
|
|
25828
25971
|
updateAgentState(db2, worker.id, "idle");
|
|
25972
|
+
persistQueenPolicyDeviation();
|
|
25829
25973
|
return result.output;
|
|
25830
25974
|
}
|
|
25831
25975
|
const rateLimitInfo = checkRateLimit(result);
|
|
@@ -25854,6 +25998,7 @@ ${unreadMessages.map(
|
|
|
25854
25998
|
logBuffer2.flush();
|
|
25855
25999
|
}
|
|
25856
26000
|
}
|
|
26001
|
+
persistQueenPolicyDeviation();
|
|
25857
26002
|
return result.output;
|
|
25858
26003
|
}
|
|
25859
26004
|
if (isCli && result.sessionId) {
|
|
@@ -25862,6 +26007,7 @@ ${unreadMessages.map(
|
|
|
25862
26007
|
if (result.output && model !== "claude" && !model.startsWith("codex")) {
|
|
25863
26008
|
logBuffer2.addSynthetic("assistant_text", result.output);
|
|
25864
26009
|
}
|
|
26010
|
+
persistQueenPolicyDeviation();
|
|
25865
26011
|
logBuffer2.addSynthetic("system", "Cycle completed");
|
|
25866
26012
|
if (result.usage && (result.usage.inputTokens > 0 || result.usage.outputTokens > 0)) {
|
|
25867
26013
|
logBuffer2.addSynthetic("system", `Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`);
|
|
@@ -25912,6 +26058,7 @@ function _stopAllLoops() {
|
|
|
25912
26058
|
if (loop.cycleAbort) loop.cycleAbort.abort();
|
|
25913
26059
|
}
|
|
25914
26060
|
runningLoops.clear();
|
|
26061
|
+
clearRoomLaunchState();
|
|
25915
26062
|
}
|
|
25916
26063
|
|
|
25917
26064
|
// src/server/cloud.ts
|
|
@@ -28281,13 +28428,10 @@ async function executeClerkTool(db2, toolName, args, ctx) {
|
|
|
28281
28428
|
return { content: `Deleted room "${room.name}" (#${room.id}).` };
|
|
28282
28429
|
}
|
|
28283
28430
|
case "quoroom_start_queen": {
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
stopRoomRuntime(db2, room.id, "Runtime reset before queen start");
|
|
28289
|
-
triggerAgent(db2, room.id, room.queenWorkerId);
|
|
28290
|
-
return { content: `Started queen in "${room.name}" (#${room.id}).` };
|
|
28431
|
+
return {
|
|
28432
|
+
content: "Error: direct queen start is disabled. Start the room manually from the Room controls.",
|
|
28433
|
+
isError: true
|
|
28434
|
+
};
|
|
28291
28435
|
}
|
|
28292
28436
|
case "quoroom_stop_queen": {
|
|
28293
28437
|
const room = resolveRoom(db2, args);
|
|
@@ -30488,6 +30632,9 @@ async function syncCloudRoomMessages(db2) {
|
|
|
30488
30632
|
}
|
|
30489
30633
|
function startServerRuntime(db2) {
|
|
30490
30634
|
stopServerRuntime();
|
|
30635
|
+
clearRoomLaunchState();
|
|
30636
|
+
const cleanedCycles = cleanupStaleCycles(db2);
|
|
30637
|
+
if (cleanedCycles > 0) console.log(`Cleaned up ${cleanedCycles} stale worker cycles`);
|
|
30491
30638
|
ensureClerkContactCheckTasks(db2);
|
|
30492
30639
|
refreshCronJobs(db2);
|
|
30493
30640
|
runDueOneTimeTasks(db2);
|
|
@@ -30515,30 +30662,8 @@ function startServerRuntime(db2) {
|
|
|
30515
30662
|
clerkAlertTimer = setInterval(() => {
|
|
30516
30663
|
queueClerkAlertRelay(db2);
|
|
30517
30664
|
}, CLERK_ALERT_RELAY_MS);
|
|
30518
|
-
resumeActiveQueens(db2);
|
|
30519
30665
|
startCommentaryEngine(db2);
|
|
30520
30666
|
}
|
|
30521
|
-
function makeCycleCallbacks() {
|
|
30522
|
-
return {
|
|
30523
|
-
onCycleLogEntry: (entry) => {
|
|
30524
|
-
eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry);
|
|
30525
|
-
},
|
|
30526
|
-
onCycleLifecycle: (event, cycleId, roomId) => {
|
|
30527
|
-
eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId });
|
|
30528
|
-
}
|
|
30529
|
-
};
|
|
30530
|
-
}
|
|
30531
|
-
function resumeActiveQueens(db2) {
|
|
30532
|
-
const cleaned = cleanupStaleCycles(db2);
|
|
30533
|
-
if (cleaned > 0) console.log(`Cleaned up ${cleaned} stale worker cycles`);
|
|
30534
|
-
const rooms = listRooms(db2, "active");
|
|
30535
|
-
const callbacks = makeCycleCallbacks();
|
|
30536
|
-
for (const room of rooms) {
|
|
30537
|
-
if (!room.queenWorkerId) continue;
|
|
30538
|
-
console.log(`Auto-resuming queen for room "${room.name}" (#${room.id})`);
|
|
30539
|
-
triggerAgent(db2, room.id, room.queenWorkerId, callbacks);
|
|
30540
|
-
}
|
|
30541
|
-
}
|
|
30542
30667
|
function stopServerRuntime() {
|
|
30543
30668
|
stopCommentaryEngine();
|
|
30544
30669
|
if (schedulerTimer) clearInterval(schedulerTimer);
|
|
@@ -30583,6 +30708,16 @@ function emitQueenState(roomId, running) {
|
|
|
30583
30708
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
30584
30709
|
});
|
|
30585
30710
|
}
|
|
30711
|
+
function makeCycleCallbacks(roomId) {
|
|
30712
|
+
return {
|
|
30713
|
+
onCycleLogEntry: (entry) => {
|
|
30714
|
+
eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry);
|
|
30715
|
+
},
|
|
30716
|
+
onCycleLifecycle: (event, cycleId, _roomId) => {
|
|
30717
|
+
eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId });
|
|
30718
|
+
}
|
|
30719
|
+
};
|
|
30720
|
+
}
|
|
30586
30721
|
function getLocalReferredRooms(db2, roomId) {
|
|
30587
30722
|
const keeperCode = (getSetting(db2, "keeper_referral_code") ?? "").trim();
|
|
30588
30723
|
if (!keeperCode) return [];
|
|
@@ -30796,11 +30931,8 @@ function registerRoomRoutes(router) {
|
|
|
30796
30931
|
}
|
|
30797
30932
|
}
|
|
30798
30933
|
if (updates.status === "stopped") {
|
|
30799
|
-
|
|
30800
|
-
|
|
30801
|
-
updateAgentState(ctx.db, w.id, "idle");
|
|
30802
|
-
pauseAgent(ctx.db, w.id);
|
|
30803
|
-
}
|
|
30934
|
+
setRoomLaunchEnabled(roomId, false);
|
|
30935
|
+
stopRoomRuntime(ctx.db, roomId, "Room archived");
|
|
30804
30936
|
logRoomActivity(ctx.db, roomId, "system", "Room archived");
|
|
30805
30937
|
}
|
|
30806
30938
|
const updated = getRoom(ctx.db, roomId);
|
|
@@ -30815,21 +30947,59 @@ function registerRoomRoutes(router) {
|
|
|
30815
30947
|
const roomId = Number(ctx.params.id);
|
|
30816
30948
|
try {
|
|
30817
30949
|
pauseRoom(ctx.db, roomId);
|
|
30818
|
-
|
|
30819
|
-
|
|
30820
|
-
pauseAgent(ctx.db, w.id);
|
|
30821
|
-
}
|
|
30950
|
+
setRoomLaunchEnabled(roomId, false);
|
|
30951
|
+
stopRoomRuntime(ctx.db, roomId, "Room stopped by keeper");
|
|
30822
30952
|
eventBus.emit(`room:${roomId}`, "room:paused", { roomId });
|
|
30953
|
+
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
30954
|
+
emitQueenState(roomId, false);
|
|
30823
30955
|
emitRoomsUpdated("room_paused", { roomId });
|
|
30824
30956
|
return { data: { ok: true } };
|
|
30825
30957
|
} catch (e) {
|
|
30826
30958
|
return { status: 404, error: e.message };
|
|
30827
30959
|
}
|
|
30828
30960
|
});
|
|
30961
|
+
router.post("/api/rooms/:id/start", (ctx) => {
|
|
30962
|
+
const roomId = Number(ctx.params.id);
|
|
30963
|
+
const room = getRoom(ctx.db, roomId);
|
|
30964
|
+
if (!room) return { status: 404, error: "Room not found" };
|
|
30965
|
+
if (room.status === "stopped") return { status: 400, error: "Room is stopped" };
|
|
30966
|
+
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
30967
|
+
if (room.status !== "active") {
|
|
30968
|
+
updateRoom(ctx.db, roomId, { status: "active" });
|
|
30969
|
+
}
|
|
30970
|
+
setRoomLaunchEnabled(roomId, true);
|
|
30971
|
+
stopRoomRuntime(ctx.db, roomId, "Runtime reset before room start");
|
|
30972
|
+
triggerAgent(ctx.db, roomId, room.queenWorkerId, {
|
|
30973
|
+
...makeCycleCallbacks(roomId),
|
|
30974
|
+
allowColdStart: true
|
|
30975
|
+
});
|
|
30976
|
+
eventBus.emit(`room:${roomId}`, "room:started", { roomId });
|
|
30977
|
+
eventBus.emit(`room:${roomId}`, "room:queen_started", { roomId, running: true });
|
|
30978
|
+
emitQueenState(roomId, true);
|
|
30979
|
+
emitRoomsUpdated("room_started", { roomId });
|
|
30980
|
+
return { data: { ok: true, running: true } };
|
|
30981
|
+
});
|
|
30982
|
+
router.post("/api/rooms/:id/stop", (ctx) => {
|
|
30983
|
+
const roomId = Number(ctx.params.id);
|
|
30984
|
+
const room = getRoom(ctx.db, roomId);
|
|
30985
|
+
if (!room) return { status: 404, error: "Room not found" };
|
|
30986
|
+
setRoomLaunchEnabled(roomId, false);
|
|
30987
|
+
stopRoomRuntime(ctx.db, roomId, "Room stopped by keeper");
|
|
30988
|
+
if (room.status !== "stopped") {
|
|
30989
|
+
updateRoom(ctx.db, roomId, { status: "paused" });
|
|
30990
|
+
}
|
|
30991
|
+
eventBus.emit(`room:${roomId}`, "room:stopped", { roomId });
|
|
30992
|
+
eventBus.emit(`room:${roomId}`, "room:paused", { roomId });
|
|
30993
|
+
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
30994
|
+
emitQueenState(roomId, false);
|
|
30995
|
+
emitRoomsUpdated("room_paused", { roomId });
|
|
30996
|
+
return { data: { ok: true, running: false } };
|
|
30997
|
+
});
|
|
30829
30998
|
router.post("/api/rooms/:id/restart", (ctx) => {
|
|
30830
30999
|
const roomId = Number(ctx.params.id);
|
|
30831
31000
|
const { goal } = ctx.body || {};
|
|
30832
31001
|
try {
|
|
31002
|
+
setRoomLaunchEnabled(roomId, false);
|
|
30833
31003
|
restartRoom(ctx.db, roomId, goal);
|
|
30834
31004
|
eventBus.emit(`room:${roomId}`, "room:restarted", { roomId });
|
|
30835
31005
|
emitRoomsUpdated("room_restarted", { roomId });
|
|
@@ -30857,30 +31027,11 @@ function registerRoomRoutes(router) {
|
|
|
30857
31027
|
}
|
|
30858
31028
|
};
|
|
30859
31029
|
});
|
|
30860
|
-
router.post("/api/rooms/:id/queen/start", (
|
|
30861
|
-
|
|
30862
|
-
const room = getRoom(ctx.db, roomId);
|
|
30863
|
-
if (!room) return { status: 404, error: "Room not found" };
|
|
30864
|
-
if (room.status !== "active") return { status: 400, error: "Room is not active" };
|
|
30865
|
-
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
30866
|
-
stopRoomRuntime(ctx.db, roomId, "Runtime reset before queen start");
|
|
30867
|
-
triggerAgent(ctx.db, roomId, room.queenWorkerId, {
|
|
30868
|
-
onCycleLogEntry: (entry) => eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry),
|
|
30869
|
-
onCycleLifecycle: (event, cycleId) => eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId })
|
|
30870
|
-
});
|
|
30871
|
-
eventBus.emit(`room:${roomId}`, "room:queen_started", { roomId, running: true });
|
|
30872
|
-
emitQueenState(roomId, true);
|
|
30873
|
-
return { data: { ok: true, running: true } };
|
|
31030
|
+
router.post("/api/rooms/:id/queen/start", (_ctx) => {
|
|
31031
|
+
return { status: 410, error: "Deprecated. Use POST /api/rooms/:id/start" };
|
|
30874
31032
|
});
|
|
30875
|
-
router.post("/api/rooms/:id/queen/stop", (
|
|
30876
|
-
|
|
30877
|
-
const room = getRoom(ctx.db, roomId);
|
|
30878
|
-
if (!room) return { status: 404, error: "Room not found" };
|
|
30879
|
-
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
30880
|
-
stopRoomRuntime(ctx.db, roomId, "Queen stopped by keeper");
|
|
30881
|
-
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
30882
|
-
emitQueenState(roomId, false);
|
|
30883
|
-
return { data: { ok: true, running: false } };
|
|
31033
|
+
router.post("/api/rooms/:id/queen/stop", (_ctx) => {
|
|
31034
|
+
return { status: 410, error: "Deprecated. Use POST /api/rooms/:id/stop" };
|
|
30884
31035
|
});
|
|
30885
31036
|
router.get("/api/rooms/:id/cycles", (ctx) => {
|
|
30886
31037
|
const roomId = Number(ctx.params.id);
|
|
@@ -30894,8 +31045,8 @@ function registerRoomRoutes(router) {
|
|
|
30894
31045
|
const today = getRoomTokenUsageToday(ctx.db, roomId);
|
|
30895
31046
|
const room = getRoom(ctx.db, roomId);
|
|
30896
31047
|
const queenWorker = room?.queenWorkerId ? getWorker(ctx.db, room.queenWorkerId) : null;
|
|
30897
|
-
const model = queenWorker?.model ?? room?.workerModel ??
|
|
30898
|
-
const isApiModel = model.startsWith("openai") || model.startsWith("anthropic") || model.startsWith("claude-api");
|
|
31048
|
+
const model = queenWorker?.model ?? room?.workerModel ?? null;
|
|
31049
|
+
const isApiModel = !!model && (model.startsWith("openai") || model.startsWith("anthropic") || model.startsWith("claude-api"));
|
|
30899
31050
|
return { data: { total, today, isApiModel } };
|
|
30900
31051
|
});
|
|
30901
31052
|
router.get("/api/cycles/:id/logs", (ctx) => {
|
|
@@ -30908,10 +31059,8 @@ function registerRoomRoutes(router) {
|
|
|
30908
31059
|
router.delete("/api/rooms/:id", (ctx) => {
|
|
30909
31060
|
const roomId = Number(ctx.params.id);
|
|
30910
31061
|
try {
|
|
30911
|
-
|
|
30912
|
-
|
|
30913
|
-
pauseAgent(ctx.db, w.id);
|
|
30914
|
-
}
|
|
31062
|
+
setRoomLaunchEnabled(roomId, false);
|
|
31063
|
+
stopRoomRuntime(ctx.db, roomId, "Room deleted");
|
|
30915
31064
|
deleteRoom2(ctx.db, roomId);
|
|
30916
31065
|
eventBus.emit(`room:${roomId}`, "room:deleted", { roomId });
|
|
30917
31066
|
emitRoomsUpdated("room_deleted", { roomId });
|
|
@@ -30977,6 +31126,9 @@ function registerWorkerRoutes(router) {
|
|
|
30977
31126
|
const room = getRoom(ctx.db, worker.roomId);
|
|
30978
31127
|
if (!room) return { status: 404, error: "Room not found" };
|
|
30979
31128
|
if (room.status !== "active") return { status: 400, error: "Room is not active" };
|
|
31129
|
+
if (!isRoomLaunchEnabled(worker.roomId)) {
|
|
31130
|
+
return { status: 409, error: "Room runtime is not started. Start the room first." };
|
|
31131
|
+
}
|
|
30980
31132
|
triggerAgent(ctx.db, worker.roomId, id, {
|
|
30981
31133
|
onCycleLogEntry: (entry) => eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry),
|
|
30982
31134
|
onCycleLifecycle: (event, cycleId) => eventBus.emit(`room:${worker.roomId}`, `cycle:${event}`, { cycleId, roomId: worker.roomId })
|
|
@@ -32520,7 +32672,7 @@ function semverGt(a, b) {
|
|
|
32520
32672
|
}
|
|
32521
32673
|
function getCurrentVersion() {
|
|
32522
32674
|
try {
|
|
32523
|
-
return true ? "0.1.
|
|
32675
|
+
return true ? "0.1.39" : null.version;
|
|
32524
32676
|
} catch {
|
|
32525
32677
|
return "0.0.0";
|
|
32526
32678
|
}
|
|
@@ -32701,7 +32853,7 @@ var cachedVersion = null;
|
|
|
32701
32853
|
function getVersion3() {
|
|
32702
32854
|
if (cachedVersion) return cachedVersion;
|
|
32703
32855
|
try {
|
|
32704
|
-
cachedVersion = true ? "0.1.
|
|
32856
|
+
cachedVersion = true ? "0.1.39" : null.version;
|
|
32705
32857
|
} catch {
|
|
32706
32858
|
cachedVersion = "unknown";
|
|
32707
32859
|
}
|
package/out/mcp/cli.js
CHANGED
|
@@ -26937,7 +26937,10 @@ async function executeAgent(options) {
|
|
|
26937
26937
|
}
|
|
26938
26938
|
return executeAnthropicApi(options);
|
|
26939
26939
|
}
|
|
26940
|
-
|
|
26940
|
+
if (model === "claude" || model.startsWith("claude-")) {
|
|
26941
|
+
return executeClaude(options);
|
|
26942
|
+
}
|
|
26943
|
+
throw new Error(`Unsupported model "${model}". Configure an explicit supported model (claude, codex, openai:*, anthropic:*, gemini:*).`);
|
|
26941
26944
|
}
|
|
26942
26945
|
async function executeClaude(options) {
|
|
26943
26946
|
const execOpts = {
|
|
@@ -49454,13 +49457,14 @@ Every cycle:
|
|
|
49454
49457
|
1. Check if workers reported results (messages, completed goals)
|
|
49455
49458
|
2. If work is done \u2192 send results to keeper, take next step
|
|
49456
49459
|
3. If work is stuck \u2192 help unblock (new instructions, different approach)
|
|
49457
|
-
4. If
|
|
49458
|
-
5. If
|
|
49460
|
+
4. If no workers exist yet \u2192 create an executor worker first
|
|
49461
|
+
5. If new work is needed \u2192 delegate to a worker with clear instructions, then poke/follow up
|
|
49462
|
+
6. If a decision needs input \u2192 announce it and process objections/votes (announce/object flow)
|
|
49459
49463
|
|
|
49460
49464
|
Talk to the keeper regularly \u2014 they are your client.
|
|
49461
49465
|
|
|
49462
|
-
Do NOT
|
|
49463
|
-
|
|
49466
|
+
Do NOT execute tasks directly (research, form filling, account creation, browser automation).
|
|
49467
|
+
Stay control-plane only: create workers, delegate, monitor, unblock, report.`;
|
|
49464
49468
|
}
|
|
49465
49469
|
});
|
|
49466
49470
|
|
|
@@ -52537,7 +52541,7 @@ var server_exports = {};
|
|
|
52537
52541
|
async function main() {
|
|
52538
52542
|
const server = new McpServer({
|
|
52539
52543
|
name: "quoroom",
|
|
52540
|
-
version: true ? "0.1.
|
|
52544
|
+
version: true ? "0.1.39" : "0.0.0"
|
|
52541
52545
|
});
|
|
52542
52546
|
registerMemoryTools(server);
|
|
52543
52547
|
registerSchedulerTools(server);
|
|
@@ -53428,7 +53432,7 @@ var init_queen_tools = __esm({
|
|
|
53428
53432
|
type: "function",
|
|
53429
53433
|
function: {
|
|
53430
53434
|
name: "quoroom_web_search",
|
|
53431
|
-
description: "Search the web. Returns top 5 results.",
|
|
53435
|
+
description: "Search the web. Returns top 5 results. Queen should delegate this to workers first in control-plane mode.",
|
|
53432
53436
|
parameters: {
|
|
53433
53437
|
type: "object",
|
|
53434
53438
|
properties: {
|
|
@@ -53442,7 +53446,7 @@ var init_queen_tools = __esm({
|
|
|
53442
53446
|
type: "function",
|
|
53443
53447
|
function: {
|
|
53444
53448
|
name: "quoroom_web_fetch",
|
|
53445
|
-
description: "Fetch any URL and return its content as clean markdown.",
|
|
53449
|
+
description: "Fetch any URL and return its content as clean markdown. Queen should delegate this to workers first in control-plane mode.",
|
|
53446
53450
|
parameters: {
|
|
53447
53451
|
type: "object",
|
|
53448
53452
|
properties: {
|
|
@@ -53456,7 +53460,7 @@ var init_queen_tools = __esm({
|
|
|
53456
53460
|
type: "function",
|
|
53457
53461
|
function: {
|
|
53458
53462
|
name: "quoroom_browser",
|
|
53459
|
-
description: "Control a headless browser: navigate, click, fill forms, buy services, register domains, create accounts.",
|
|
53463
|
+
description: "Control a headless browser: navigate, click, fill forms, buy services, register domains, create accounts. Queen should delegate this to workers first in control-plane mode.",
|
|
53460
53464
|
parameters: {
|
|
53461
53465
|
type: "object",
|
|
53462
53466
|
properties: {
|
|
@@ -53617,6 +53621,32 @@ function msUntilQuietEnd(until) {
|
|
|
53617
53621
|
if (end <= now) end.setDate(end.getDate() + 1);
|
|
53618
53622
|
return end.getTime() - now.getTime();
|
|
53619
53623
|
}
|
|
53624
|
+
function nextAutoExecutorName(workers) {
|
|
53625
|
+
const names = new Set(workers.map((w) => w.name.toLowerCase()));
|
|
53626
|
+
let idx = 1;
|
|
53627
|
+
while (names.has(`executor-${idx}`)) idx++;
|
|
53628
|
+
return `executor-${idx}`;
|
|
53629
|
+
}
|
|
53630
|
+
function extractToolNameFromConsoleLog(content) {
|
|
53631
|
+
const usingMatch = content.match(/(?:Using|→)\s*([a-zA-Z0-9_]+)/);
|
|
53632
|
+
if (usingMatch?.[1]) return usingMatch[1];
|
|
53633
|
+
const callMatch = content.match(/^([a-zA-Z0-9_]+)\s*\(/);
|
|
53634
|
+
return callMatch?.[1] ?? null;
|
|
53635
|
+
}
|
|
53636
|
+
function resolveWorkerExecutionModel(db3, roomId, worker) {
|
|
53637
|
+
const explicit = worker.model?.trim();
|
|
53638
|
+
if (explicit) return explicit;
|
|
53639
|
+
const room = getRoom(db3, roomId);
|
|
53640
|
+
if (!room) return null;
|
|
53641
|
+
const roomModel = room.workerModel?.trim();
|
|
53642
|
+
if (!roomModel) return null;
|
|
53643
|
+
if (roomModel !== "queen") return roomModel;
|
|
53644
|
+
if (!room.queenWorkerId) return null;
|
|
53645
|
+
if (room.queenWorkerId === worker.id) return null;
|
|
53646
|
+
const queen = getWorker(db3, room.queenWorkerId);
|
|
53647
|
+
const queenModel = queen?.model?.trim();
|
|
53648
|
+
return queenModel || null;
|
|
53649
|
+
}
|
|
53620
53650
|
async function startAgentLoop(db3, roomId, workerId, options) {
|
|
53621
53651
|
const room = getRoom(db3, roomId);
|
|
53622
53652
|
if (!room) throw new Error(`Room ${roomId} not found`);
|
|
@@ -53732,12 +53762,29 @@ function pauseAgent(db3, workerId) {
|
|
|
53732
53762
|
}
|
|
53733
53763
|
updateAgentState(db3, workerId, "idle");
|
|
53734
53764
|
}
|
|
53765
|
+
function setRoomLaunchEnabled(roomId, enabled) {
|
|
53766
|
+
if (enabled) {
|
|
53767
|
+
launchedRoomIds.add(roomId);
|
|
53768
|
+
return;
|
|
53769
|
+
}
|
|
53770
|
+
launchedRoomIds.delete(roomId);
|
|
53771
|
+
}
|
|
53772
|
+
function isRoomLaunchEnabled(roomId) {
|
|
53773
|
+
return launchedRoomIds.has(roomId);
|
|
53774
|
+
}
|
|
53775
|
+
function clearRoomLaunchState() {
|
|
53776
|
+
launchedRoomIds.clear();
|
|
53777
|
+
}
|
|
53735
53778
|
function triggerAgent(db3, roomId, workerId, options) {
|
|
53736
53779
|
const loop = runningLoops.get(workerId);
|
|
53737
53780
|
if (loop?.running) {
|
|
53738
53781
|
if (loop.waitAbort) loop.waitAbort.abort();
|
|
53739
53782
|
return;
|
|
53740
53783
|
}
|
|
53784
|
+
const canColdStart = options?.allowColdStart === true || isRoomLaunchEnabled(roomId);
|
|
53785
|
+
if (!canColdStart) {
|
|
53786
|
+
return;
|
|
53787
|
+
}
|
|
53741
53788
|
startAgentLoop(db3, roomId, workerId, options).catch((err) => {
|
|
53742
53789
|
const msg = err instanceof Error ? err.message : String(err);
|
|
53743
53790
|
console.error(`Agent loop failed for worker ${workerId}: ${msg}`);
|
|
@@ -53785,7 +53832,7 @@ async function runCycle(db3, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
53785
53832
|
void 0,
|
|
53786
53833
|
worker.id
|
|
53787
53834
|
);
|
|
53788
|
-
const model =
|
|
53835
|
+
const model = resolveWorkerExecutionModel(db3, roomId, worker);
|
|
53789
53836
|
const cycle = createWorkerCycle(db3, worker.id, roomId, model);
|
|
53790
53837
|
const logBuffer2 = createCycleLogBuffer(
|
|
53791
53838
|
cycle.id,
|
|
@@ -53794,6 +53841,23 @@ async function runCycle(db3, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
53794
53841
|
);
|
|
53795
53842
|
options?.onCycleLifecycle?.("created", cycle.id, roomId);
|
|
53796
53843
|
try {
|
|
53844
|
+
if (!model) {
|
|
53845
|
+
const msg = "No model configured for this worker. Set an explicit worker model or room worker model.";
|
|
53846
|
+
logBuffer2.addSynthetic("error", msg);
|
|
53847
|
+
logBuffer2.flush();
|
|
53848
|
+
completeWorkerCycle(db3, cycle.id, msg, void 0);
|
|
53849
|
+
options?.onCycleLifecycle?.("failed", cycle.id, roomId);
|
|
53850
|
+
logRoomActivity(
|
|
53851
|
+
db3,
|
|
53852
|
+
roomId,
|
|
53853
|
+
"error",
|
|
53854
|
+
`Agent cycle failed (${worker.name}): model is not configured`,
|
|
53855
|
+
msg,
|
|
53856
|
+
worker.id
|
|
53857
|
+
);
|
|
53858
|
+
updateAgentState(db3, worker.id, "idle");
|
|
53859
|
+
return msg;
|
|
53860
|
+
}
|
|
53797
53861
|
const provider = getModelProvider(model);
|
|
53798
53862
|
if (provider === "openai_api" || provider === "anthropic_api" || provider === "gemini_api") {
|
|
53799
53863
|
const apiKeyCheck = resolveApiKeyForModel(db3, roomId, model);
|
|
@@ -53820,10 +53884,44 @@ async function runCycle(db3, roomId, worker, maxTurns, options, abortSignal) {
|
|
|
53820
53884
|
status: g.status,
|
|
53821
53885
|
assignedWorkerId: g.assignedWorkerId
|
|
53822
53886
|
}));
|
|
53823
|
-
|
|
53887
|
+
let roomWorkers = listRoomWorkers(db3, roomId);
|
|
53888
|
+
const isQueen = worker.id === status2.room.queenWorkerId;
|
|
53824
53889
|
const unreadMessages = listRoomMessages(db3, roomId, "unread").slice(0, 5);
|
|
53890
|
+
if (isQueen) {
|
|
53891
|
+
const nonQueenWorkers = roomWorkers.filter((w) => w.id !== worker.id);
|
|
53892
|
+
if (nonQueenWorkers.length === 0) {
|
|
53893
|
+
const autoName = nextAutoExecutorName(roomWorkers);
|
|
53894
|
+
const executorPreset = WORKER_ROLE_PRESETS.executor;
|
|
53895
|
+
const inheritedModel = status2.room.workerModel === "queen" ? model : status2.room.workerModel?.trim();
|
|
53896
|
+
if (!inheritedModel) {
|
|
53897
|
+
const err = "Auto-create skipped: no worker model configured for executor.";
|
|
53898
|
+
logRoomActivity(db3, roomId, "error", err, "Set room worker model or queen model first.", worker.id);
|
|
53899
|
+
logBuffer2.addSynthetic("error", err);
|
|
53900
|
+
} else {
|
|
53901
|
+
createWorker(db3, {
|
|
53902
|
+
name: autoName,
|
|
53903
|
+
role: "executor",
|
|
53904
|
+
roomId,
|
|
53905
|
+
description: "Auto-created executor for queen-delegated execution work.",
|
|
53906
|
+
systemPrompt: "You are the room executor. Complete delegated tasks end-to-end, report concrete results, and save progress with quoroom_save_wip.",
|
|
53907
|
+
model: inheritedModel,
|
|
53908
|
+
cycleGapMs: executorPreset?.cycleGapMs,
|
|
53909
|
+
maxTurns: executorPreset?.maxTurns
|
|
53910
|
+
});
|
|
53911
|
+
logRoomActivity(
|
|
53912
|
+
db3,
|
|
53913
|
+
roomId,
|
|
53914
|
+
"system",
|
|
53915
|
+
`Auto-created worker "${autoName}" for delegation-first execution.`,
|
|
53916
|
+
"Model B (soft): queen coordinates, workers execute.",
|
|
53917
|
+
worker.id
|
|
53918
|
+
);
|
|
53919
|
+
logBuffer2.addSynthetic("system", `Auto-created worker "${autoName}" because queen had no executors.`);
|
|
53920
|
+
roomWorkers = listRoomWorkers(db3, roomId);
|
|
53921
|
+
}
|
|
53922
|
+
}
|
|
53923
|
+
}
|
|
53825
53924
|
const rolePreset = worker.role ? WORKER_ROLE_PRESETS[worker.role] : void 0;
|
|
53826
|
-
const isQueen = worker.id === status2.room.queenWorkerId;
|
|
53827
53925
|
const namePrefix = worker.name ? `Your name is ${worker.name}.
|
|
53828
53926
|
|
|
53829
53927
|
` : "";
|
|
@@ -53912,6 +54010,14 @@ At the end of this cycle, call quoroom_save_wip to save your updated position.`)
|
|
|
53912
54010
|
if (status2.room.goal) {
|
|
53913
54011
|
contextParts.push(`## Room Objective
|
|
53914
54012
|
${status2.room.goal}`);
|
|
54013
|
+
}
|
|
54014
|
+
if (isQueen) {
|
|
54015
|
+
contextParts.push(`## Queen Controller Contract (Model B)
|
|
54016
|
+
- You are the control plane: create workers, delegate tasks, and monitor delivery.
|
|
54017
|
+
- If there are no workers besides you, create one executor first.
|
|
54018
|
+
- Delegate all execution via quoroom_delegate_task and follow up with worker messages/pokes.
|
|
54019
|
+
- Keep governance active: use quoroom_announce for decisions and process objections/votes.
|
|
54020
|
+
- Do not perform execution tasks directly unless strictly unavoidable.`);
|
|
53915
54021
|
}
|
|
53916
54022
|
if (goalUpdates.length > 0) {
|
|
53917
54023
|
const workerMap = new Map(roomWorkers.map((w) => [w.id, w.name]));
|
|
@@ -54024,9 +54130,34 @@ ${unreadMessages.map(
|
|
|
54024
54130
|
const needsQueenTools = model === "openai" || model.startsWith("openai:") || model === "anthropic" || model.startsWith("anthropic:") || model.startsWith("claude-api:");
|
|
54025
54131
|
const roleToolDefs = isQueen ? QUEEN_TOOLS : WORKER_TOOLS;
|
|
54026
54132
|
const filteredToolDefs = allowSet ? roleToolDefs.filter((t) => allowSet.has(t.function.name)) : roleToolDefs;
|
|
54133
|
+
const queenExecutionToolsUsed = /* @__PURE__ */ new Set();
|
|
54134
|
+
const trackQueenExecutionTool = (toolName) => {
|
|
54135
|
+
if (!isQueen || !toolName) return;
|
|
54136
|
+
if (QUEEN_EXECUTION_TOOLS.has(toolName)) queenExecutionToolsUsed.add(toolName);
|
|
54137
|
+
};
|
|
54138
|
+
const persistQueenPolicyDeviation = () => {
|
|
54139
|
+
if (!isQueen || queenExecutionToolsUsed.size === 0) return;
|
|
54140
|
+
const used = [...queenExecutionToolsUsed].sort().join(", ");
|
|
54141
|
+
logRoomActivity(
|
|
54142
|
+
db3,
|
|
54143
|
+
roomId,
|
|
54144
|
+
"system",
|
|
54145
|
+
`Queen policy deviation: execution tool use detected (${used}).`,
|
|
54146
|
+
"Model B (soft): queen should delegate execution to workers and remain control-plane focused.",
|
|
54147
|
+
worker.id
|
|
54148
|
+
);
|
|
54149
|
+
const fresh = getWorker(db3, worker.id);
|
|
54150
|
+
const existing = fresh?.wip?.trim() ?? "";
|
|
54151
|
+
if (existing.includes(QUEEN_POLICY_WIP_HINT)) return;
|
|
54152
|
+
const nextWip = existing ? `${existing}
|
|
54153
|
+
|
|
54154
|
+
${QUEEN_POLICY_WIP_HINT}` : QUEEN_POLICY_WIP_HINT;
|
|
54155
|
+
updateWorkerWip(db3, worker.id, nextWip.slice(0, 2e3));
|
|
54156
|
+
};
|
|
54027
54157
|
const apiToolOpts = needsQueenTools ? {
|
|
54028
54158
|
toolDefs: filteredToolDefs,
|
|
54029
54159
|
onToolCall: async (toolName, args2) => {
|
|
54160
|
+
trackQueenExecutionTool(toolName);
|
|
54030
54161
|
logBuffer2.addSynthetic("tool_call", `\u2192 ${toolName}(${JSON.stringify(args2)})`);
|
|
54031
54162
|
const result2 = await executeQueenTool(db3, roomId, worker.id, toolName, args2);
|
|
54032
54163
|
logBuffer2.addSynthetic("tool_result", result2.content);
|
|
@@ -54040,7 +54171,12 @@ ${unreadMessages.map(
|
|
|
54040
54171
|
apiKey,
|
|
54041
54172
|
timeoutMs: worker.role === "executor" ? 30 * 60 * 1e3 : 15 * 60 * 1e3,
|
|
54042
54173
|
maxTurns: maxTurns ?? 50,
|
|
54043
|
-
onConsoleLog:
|
|
54174
|
+
onConsoleLog: (entry) => {
|
|
54175
|
+
if (entry.entryType === "tool_call") {
|
|
54176
|
+
trackQueenExecutionTool(extractToolNameFromConsoleLog(entry.content));
|
|
54177
|
+
}
|
|
54178
|
+
logBuffer2.onConsoleLog(entry);
|
|
54179
|
+
},
|
|
54044
54180
|
// CLI models: block non-quoroom MCP tools (daymon, etc.)
|
|
54045
54181
|
disallowedTools: isCli ? "mcp__daymon*" : void 0,
|
|
54046
54182
|
// CLI models: bypass permission prompts for headless operation
|
|
@@ -54073,6 +54209,7 @@ ${unreadMessages.map(
|
|
|
54073
54209
|
completeWorkerCycle(db3, cycle.id, canceledMessage, result.usage);
|
|
54074
54210
|
options?.onCycleLifecycle?.("failed", cycle.id, roomId);
|
|
54075
54211
|
updateAgentState(db3, worker.id, "idle");
|
|
54212
|
+
persistQueenPolicyDeviation();
|
|
54076
54213
|
return result.output;
|
|
54077
54214
|
}
|
|
54078
54215
|
const rateLimitInfo = checkRateLimit(result);
|
|
@@ -54101,6 +54238,7 @@ ${unreadMessages.map(
|
|
|
54101
54238
|
logBuffer2.flush();
|
|
54102
54239
|
}
|
|
54103
54240
|
}
|
|
54241
|
+
persistQueenPolicyDeviation();
|
|
54104
54242
|
return result.output;
|
|
54105
54243
|
}
|
|
54106
54244
|
if (isCli && result.sessionId) {
|
|
@@ -54109,6 +54247,7 @@ ${unreadMessages.map(
|
|
|
54109
54247
|
if (result.output && model !== "claude" && !model.startsWith("codex")) {
|
|
54110
54248
|
logBuffer2.addSynthetic("assistant_text", result.output);
|
|
54111
54249
|
}
|
|
54250
|
+
persistQueenPolicyDeviation();
|
|
54112
54251
|
logBuffer2.addSynthetic("system", "Cycle completed");
|
|
54113
54252
|
if (result.usage && (result.usage.inputTokens > 0 || result.usage.outputTokens > 0)) {
|
|
54114
54253
|
logBuffer2.addSynthetic("system", `Tokens: ${result.usage.inputTokens} in / ${result.usage.outputTokens} out`);
|
|
@@ -54159,8 +54298,9 @@ function _stopAllLoops() {
|
|
|
54159
54298
|
if (loop.cycleAbort) loop.cycleAbort.abort();
|
|
54160
54299
|
}
|
|
54161
54300
|
runningLoops.clear();
|
|
54301
|
+
clearRoomLaunchState();
|
|
54162
54302
|
}
|
|
54163
|
-
var runningLoops, RateLimitError;
|
|
54303
|
+
var QUEEN_EXECUTION_TOOLS, QUEEN_POLICY_WIP_HINT, runningLoops, launchedRoomIds, RateLimitError;
|
|
54164
54304
|
var init_agent_loop = __esm({
|
|
54165
54305
|
"src/shared/agent-loop.ts"() {
|
|
54166
54306
|
"use strict";
|
|
@@ -54173,7 +54313,14 @@ var init_agent_loop = __esm({
|
|
|
54173
54313
|
init_console_log_buffer();
|
|
54174
54314
|
init_queen_tools();
|
|
54175
54315
|
init_constants();
|
|
54316
|
+
QUEEN_EXECUTION_TOOLS = /* @__PURE__ */ new Set([
|
|
54317
|
+
"quoroom_web_search",
|
|
54318
|
+
"quoroom_web_fetch",
|
|
54319
|
+
"quoroom_browser"
|
|
54320
|
+
]);
|
|
54321
|
+
QUEEN_POLICY_WIP_HINT = "[policy] Queen control-plane mode: delegate execution tasks to workers with quoroom_delegate_task, then monitor, unblock, and report outcomes. Avoid direct web/browser execution.";
|
|
54176
54322
|
runningLoops = /* @__PURE__ */ new Map();
|
|
54323
|
+
launchedRoomIds = /* @__PURE__ */ new Set();
|
|
54177
54324
|
RateLimitError = class extends Error {
|
|
54178
54325
|
constructor(info) {
|
|
54179
54326
|
super(`Rate limited: wait ${Math.round(info.waitMs / 1e3)}s`);
|
|
@@ -54189,7 +54336,7 @@ var require_package = __commonJS({
|
|
|
54189
54336
|
"package.json"(exports2, module2) {
|
|
54190
54337
|
module2.exports = {
|
|
54191
54338
|
name: "quoroom",
|
|
54192
|
-
version: "0.1.
|
|
54339
|
+
version: "0.1.39",
|
|
54193
54340
|
description: "Open-source local AI agent framework \u2014 Queen, Workers, Quorum. Experimental research tool.",
|
|
54194
54341
|
main: "./out/mcp/server.js",
|
|
54195
54342
|
bin: {
|
|
@@ -55685,13 +55832,10 @@ async function executeClerkTool(db3, toolName, args2, ctx) {
|
|
|
55685
55832
|
return { content: `Deleted room "${room.name}" (#${room.id}).` };
|
|
55686
55833
|
}
|
|
55687
55834
|
case "quoroom_start_queen": {
|
|
55688
|
-
|
|
55689
|
-
|
|
55690
|
-
|
|
55691
|
-
|
|
55692
|
-
stopRoomRuntime(db3, room.id, "Runtime reset before queen start");
|
|
55693
|
-
triggerAgent(db3, room.id, room.queenWorkerId);
|
|
55694
|
-
return { content: `Started queen in "${room.name}" (#${room.id}).` };
|
|
55835
|
+
return {
|
|
55836
|
+
content: "Error: direct queen start is disabled. Start the room manually from the Room controls.",
|
|
55837
|
+
isError: true
|
|
55838
|
+
};
|
|
55695
55839
|
}
|
|
55696
55840
|
case "quoroom_stop_queen": {
|
|
55697
55841
|
const room = resolveRoom(db3, args2);
|
|
@@ -58260,6 +58404,9 @@ async function syncCloudRoomMessages(db3) {
|
|
|
58260
58404
|
}
|
|
58261
58405
|
function startServerRuntime(db3) {
|
|
58262
58406
|
stopServerRuntime();
|
|
58407
|
+
clearRoomLaunchState();
|
|
58408
|
+
const cleanedCycles = cleanupStaleCycles(db3);
|
|
58409
|
+
if (cleanedCycles > 0) console.log(`Cleaned up ${cleanedCycles} stale worker cycles`);
|
|
58263
58410
|
ensureClerkContactCheckTasks(db3);
|
|
58264
58411
|
refreshCronJobs(db3);
|
|
58265
58412
|
runDueOneTimeTasks(db3);
|
|
@@ -58287,30 +58434,8 @@ function startServerRuntime(db3) {
|
|
|
58287
58434
|
clerkAlertTimer = setInterval(() => {
|
|
58288
58435
|
queueClerkAlertRelay(db3);
|
|
58289
58436
|
}, CLERK_ALERT_RELAY_MS);
|
|
58290
|
-
resumeActiveQueens(db3);
|
|
58291
58437
|
startCommentaryEngine(db3);
|
|
58292
58438
|
}
|
|
58293
|
-
function makeCycleCallbacks() {
|
|
58294
|
-
return {
|
|
58295
|
-
onCycleLogEntry: (entry) => {
|
|
58296
|
-
eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry);
|
|
58297
|
-
},
|
|
58298
|
-
onCycleLifecycle: (event, cycleId, roomId) => {
|
|
58299
|
-
eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId });
|
|
58300
|
-
}
|
|
58301
|
-
};
|
|
58302
|
-
}
|
|
58303
|
-
function resumeActiveQueens(db3) {
|
|
58304
|
-
const cleaned = cleanupStaleCycles(db3);
|
|
58305
|
-
if (cleaned > 0) console.log(`Cleaned up ${cleaned} stale worker cycles`);
|
|
58306
|
-
const rooms = listRooms(db3, "active");
|
|
58307
|
-
const callbacks = makeCycleCallbacks();
|
|
58308
|
-
for (const room of rooms) {
|
|
58309
|
-
if (!room.queenWorkerId) continue;
|
|
58310
|
-
console.log(`Auto-resuming queen for room "${room.name}" (#${room.id})`);
|
|
58311
|
-
triggerAgent(db3, room.id, room.queenWorkerId, callbacks);
|
|
58312
|
-
}
|
|
58313
|
-
}
|
|
58314
58439
|
function stopServerRuntime() {
|
|
58315
58440
|
stopCommentaryEngine();
|
|
58316
58441
|
if (schedulerTimer) clearInterval(schedulerTimer);
|
|
@@ -58391,6 +58516,16 @@ function emitQueenState(roomId, running) {
|
|
|
58391
58516
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
58392
58517
|
});
|
|
58393
58518
|
}
|
|
58519
|
+
function makeCycleCallbacks(roomId) {
|
|
58520
|
+
return {
|
|
58521
|
+
onCycleLogEntry: (entry) => {
|
|
58522
|
+
eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry);
|
|
58523
|
+
},
|
|
58524
|
+
onCycleLifecycle: (event, cycleId, _roomId) => {
|
|
58525
|
+
eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId });
|
|
58526
|
+
}
|
|
58527
|
+
};
|
|
58528
|
+
}
|
|
58394
58529
|
function getLocalReferredRooms(db3, roomId) {
|
|
58395
58530
|
const keeperCode = (getSetting(db3, "keeper_referral_code") ?? "").trim();
|
|
58396
58531
|
if (!keeperCode) return [];
|
|
@@ -58604,11 +58739,8 @@ function registerRoomRoutes(router) {
|
|
|
58604
58739
|
}
|
|
58605
58740
|
}
|
|
58606
58741
|
if (updates.status === "stopped") {
|
|
58607
|
-
|
|
58608
|
-
|
|
58609
|
-
updateAgentState(ctx.db, w.id, "idle");
|
|
58610
|
-
pauseAgent(ctx.db, w.id);
|
|
58611
|
-
}
|
|
58742
|
+
setRoomLaunchEnabled(roomId, false);
|
|
58743
|
+
stopRoomRuntime(ctx.db, roomId, "Room archived");
|
|
58612
58744
|
logRoomActivity(ctx.db, roomId, "system", "Room archived");
|
|
58613
58745
|
}
|
|
58614
58746
|
const updated = getRoom(ctx.db, roomId);
|
|
@@ -58623,21 +58755,59 @@ function registerRoomRoutes(router) {
|
|
|
58623
58755
|
const roomId = Number(ctx.params.id);
|
|
58624
58756
|
try {
|
|
58625
58757
|
pauseRoom(ctx.db, roomId);
|
|
58626
|
-
|
|
58627
|
-
|
|
58628
|
-
pauseAgent(ctx.db, w.id);
|
|
58629
|
-
}
|
|
58758
|
+
setRoomLaunchEnabled(roomId, false);
|
|
58759
|
+
stopRoomRuntime(ctx.db, roomId, "Room stopped by keeper");
|
|
58630
58760
|
eventBus.emit(`room:${roomId}`, "room:paused", { roomId });
|
|
58761
|
+
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
58762
|
+
emitQueenState(roomId, false);
|
|
58631
58763
|
emitRoomsUpdated("room_paused", { roomId });
|
|
58632
58764
|
return { data: { ok: true } };
|
|
58633
58765
|
} catch (e) {
|
|
58634
58766
|
return { status: 404, error: e.message };
|
|
58635
58767
|
}
|
|
58636
58768
|
});
|
|
58769
|
+
router.post("/api/rooms/:id/start", (ctx) => {
|
|
58770
|
+
const roomId = Number(ctx.params.id);
|
|
58771
|
+
const room = getRoom(ctx.db, roomId);
|
|
58772
|
+
if (!room) return { status: 404, error: "Room not found" };
|
|
58773
|
+
if (room.status === "stopped") return { status: 400, error: "Room is stopped" };
|
|
58774
|
+
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
58775
|
+
if (room.status !== "active") {
|
|
58776
|
+
updateRoom(ctx.db, roomId, { status: "active" });
|
|
58777
|
+
}
|
|
58778
|
+
setRoomLaunchEnabled(roomId, true);
|
|
58779
|
+
stopRoomRuntime(ctx.db, roomId, "Runtime reset before room start");
|
|
58780
|
+
triggerAgent(ctx.db, roomId, room.queenWorkerId, {
|
|
58781
|
+
...makeCycleCallbacks(roomId),
|
|
58782
|
+
allowColdStart: true
|
|
58783
|
+
});
|
|
58784
|
+
eventBus.emit(`room:${roomId}`, "room:started", { roomId });
|
|
58785
|
+
eventBus.emit(`room:${roomId}`, "room:queen_started", { roomId, running: true });
|
|
58786
|
+
emitQueenState(roomId, true);
|
|
58787
|
+
emitRoomsUpdated("room_started", { roomId });
|
|
58788
|
+
return { data: { ok: true, running: true } };
|
|
58789
|
+
});
|
|
58790
|
+
router.post("/api/rooms/:id/stop", (ctx) => {
|
|
58791
|
+
const roomId = Number(ctx.params.id);
|
|
58792
|
+
const room = getRoom(ctx.db, roomId);
|
|
58793
|
+
if (!room) return { status: 404, error: "Room not found" };
|
|
58794
|
+
setRoomLaunchEnabled(roomId, false);
|
|
58795
|
+
stopRoomRuntime(ctx.db, roomId, "Room stopped by keeper");
|
|
58796
|
+
if (room.status !== "stopped") {
|
|
58797
|
+
updateRoom(ctx.db, roomId, { status: "paused" });
|
|
58798
|
+
}
|
|
58799
|
+
eventBus.emit(`room:${roomId}`, "room:stopped", { roomId });
|
|
58800
|
+
eventBus.emit(`room:${roomId}`, "room:paused", { roomId });
|
|
58801
|
+
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
58802
|
+
emitQueenState(roomId, false);
|
|
58803
|
+
emitRoomsUpdated("room_paused", { roomId });
|
|
58804
|
+
return { data: { ok: true, running: false } };
|
|
58805
|
+
});
|
|
58637
58806
|
router.post("/api/rooms/:id/restart", (ctx) => {
|
|
58638
58807
|
const roomId = Number(ctx.params.id);
|
|
58639
58808
|
const { goal } = ctx.body || {};
|
|
58640
58809
|
try {
|
|
58810
|
+
setRoomLaunchEnabled(roomId, false);
|
|
58641
58811
|
restartRoom(ctx.db, roomId, goal);
|
|
58642
58812
|
eventBus.emit(`room:${roomId}`, "room:restarted", { roomId });
|
|
58643
58813
|
emitRoomsUpdated("room_restarted", { roomId });
|
|
@@ -58665,30 +58835,11 @@ function registerRoomRoutes(router) {
|
|
|
58665
58835
|
}
|
|
58666
58836
|
};
|
|
58667
58837
|
});
|
|
58668
|
-
router.post("/api/rooms/:id/queen/start", (
|
|
58669
|
-
|
|
58670
|
-
const room = getRoom(ctx.db, roomId);
|
|
58671
|
-
if (!room) return { status: 404, error: "Room not found" };
|
|
58672
|
-
if (room.status !== "active") return { status: 400, error: "Room is not active" };
|
|
58673
|
-
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
58674
|
-
stopRoomRuntime(ctx.db, roomId, "Runtime reset before queen start");
|
|
58675
|
-
triggerAgent(ctx.db, roomId, room.queenWorkerId, {
|
|
58676
|
-
onCycleLogEntry: (entry) => eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry),
|
|
58677
|
-
onCycleLifecycle: (event, cycleId) => eventBus.emit(`room:${roomId}`, `cycle:${event}`, { cycleId, roomId })
|
|
58678
|
-
});
|
|
58679
|
-
eventBus.emit(`room:${roomId}`, "room:queen_started", { roomId, running: true });
|
|
58680
|
-
emitQueenState(roomId, true);
|
|
58681
|
-
return { data: { ok: true, running: true } };
|
|
58838
|
+
router.post("/api/rooms/:id/queen/start", (_ctx) => {
|
|
58839
|
+
return { status: 410, error: "Deprecated. Use POST /api/rooms/:id/start" };
|
|
58682
58840
|
});
|
|
58683
|
-
router.post("/api/rooms/:id/queen/stop", (
|
|
58684
|
-
|
|
58685
|
-
const room = getRoom(ctx.db, roomId);
|
|
58686
|
-
if (!room) return { status: 404, error: "Room not found" };
|
|
58687
|
-
if (!room.queenWorkerId) return { status: 400, error: "No queen worker" };
|
|
58688
|
-
stopRoomRuntime(ctx.db, roomId, "Queen stopped by keeper");
|
|
58689
|
-
eventBus.emit(`room:${roomId}`, "room:queen_stopped", { roomId, running: false });
|
|
58690
|
-
emitQueenState(roomId, false);
|
|
58691
|
-
return { data: { ok: true, running: false } };
|
|
58841
|
+
router.post("/api/rooms/:id/queen/stop", (_ctx) => {
|
|
58842
|
+
return { status: 410, error: "Deprecated. Use POST /api/rooms/:id/stop" };
|
|
58692
58843
|
});
|
|
58693
58844
|
router.get("/api/rooms/:id/cycles", (ctx) => {
|
|
58694
58845
|
const roomId = Number(ctx.params.id);
|
|
@@ -58702,8 +58853,8 @@ function registerRoomRoutes(router) {
|
|
|
58702
58853
|
const today = getRoomTokenUsageToday(ctx.db, roomId);
|
|
58703
58854
|
const room = getRoom(ctx.db, roomId);
|
|
58704
58855
|
const queenWorker = room?.queenWorkerId ? getWorker(ctx.db, room.queenWorkerId) : null;
|
|
58705
|
-
const model = queenWorker?.model ?? room?.workerModel ??
|
|
58706
|
-
const isApiModel = model.startsWith("openai") || model.startsWith("anthropic") || model.startsWith("claude-api");
|
|
58856
|
+
const model = queenWorker?.model ?? room?.workerModel ?? null;
|
|
58857
|
+
const isApiModel = !!model && (model.startsWith("openai") || model.startsWith("anthropic") || model.startsWith("claude-api"));
|
|
58707
58858
|
return { data: { total, today, isApiModel } };
|
|
58708
58859
|
});
|
|
58709
58860
|
router.get("/api/cycles/:id/logs", (ctx) => {
|
|
@@ -58716,10 +58867,8 @@ function registerRoomRoutes(router) {
|
|
|
58716
58867
|
router.delete("/api/rooms/:id", (ctx) => {
|
|
58717
58868
|
const roomId = Number(ctx.params.id);
|
|
58718
58869
|
try {
|
|
58719
|
-
|
|
58720
|
-
|
|
58721
|
-
pauseAgent(ctx.db, w.id);
|
|
58722
|
-
}
|
|
58870
|
+
setRoomLaunchEnabled(roomId, false);
|
|
58871
|
+
stopRoomRuntime(ctx.db, roomId, "Room deleted");
|
|
58723
58872
|
deleteRoom2(ctx.db, roomId);
|
|
58724
58873
|
eventBus.emit(`room:${roomId}`, "room:deleted", { roomId });
|
|
58725
58874
|
emitRoomsUpdated("room_deleted", { roomId });
|
|
@@ -58799,6 +58948,9 @@ function registerWorkerRoutes(router) {
|
|
|
58799
58948
|
const room = getRoom(ctx.db, worker.roomId);
|
|
58800
58949
|
if (!room) return { status: 404, error: "Room not found" };
|
|
58801
58950
|
if (room.status !== "active") return { status: 400, error: "Room is not active" };
|
|
58951
|
+
if (!isRoomLaunchEnabled(worker.roomId)) {
|
|
58952
|
+
return { status: 409, error: "Room runtime is not started. Start the room first." };
|
|
58953
|
+
}
|
|
58802
58954
|
triggerAgent(ctx.db, worker.roomId, id, {
|
|
58803
58955
|
onCycleLogEntry: (entry) => eventBus.emit(`cycle:${entry.cycleId}`, "cycle:log", entry),
|
|
58804
58956
|
onCycleLifecycle: (event, cycleId) => eventBus.emit(`room:${worker.roomId}`, `cycle:${event}`, { cycleId, roomId: worker.roomId })
|
|
@@ -59759,7 +59911,7 @@ function semverGt(a, b) {
|
|
|
59759
59911
|
}
|
|
59760
59912
|
function getCurrentVersion() {
|
|
59761
59913
|
try {
|
|
59762
|
-
return true ? "0.1.
|
|
59914
|
+
return true ? "0.1.39" : null.version;
|
|
59763
59915
|
} catch {
|
|
59764
59916
|
return "0.0.0";
|
|
59765
59917
|
}
|
|
@@ -59966,7 +60118,7 @@ var init_updateChecker = __esm({
|
|
|
59966
60118
|
function getVersion3() {
|
|
59967
60119
|
if (cachedVersion) return cachedVersion;
|
|
59968
60120
|
try {
|
|
59969
|
-
cachedVersion = true ? "0.1.
|
|
60121
|
+
cachedVersion = true ? "0.1.39" : null.version;
|
|
59970
60122
|
} catch {
|
|
59971
60123
|
cachedVersion = "unknown";
|
|
59972
60124
|
}
|
|
@@ -66232,7 +66384,7 @@ __export(update_exports, {
|
|
|
66232
66384
|
});
|
|
66233
66385
|
function getCurrentVersion2() {
|
|
66234
66386
|
try {
|
|
66235
|
-
return true ? "0.1.
|
|
66387
|
+
return true ? "0.1.39" : null.version;
|
|
66236
66388
|
} catch {
|
|
66237
66389
|
return "0.0.0";
|
|
66238
66390
|
}
|
package/out/mcp/server.js
CHANGED
|
@@ -46264,13 +46264,14 @@ Every cycle:
|
|
|
46264
46264
|
1. Check if workers reported results (messages, completed goals)
|
|
46265
46265
|
2. If work is done \u2192 send results to keeper, take next step
|
|
46266
46266
|
3. If work is stuck \u2192 help unblock (new instructions, different approach)
|
|
46267
|
-
4. If
|
|
46268
|
-
5. If
|
|
46267
|
+
4. If no workers exist yet \u2192 create an executor worker first
|
|
46268
|
+
5. If new work is needed \u2192 delegate to a worker with clear instructions, then poke/follow up
|
|
46269
|
+
6. If a decision needs input \u2192 announce it and process objections/votes (announce/object flow)
|
|
46269
46270
|
|
|
46270
46271
|
Talk to the keeper regularly \u2014 they are your client.
|
|
46271
46272
|
|
|
46272
|
-
Do NOT
|
|
46273
|
-
|
|
46273
|
+
Do NOT execute tasks directly (research, form filling, account creation, browser automation).
|
|
46274
|
+
Stay control-plane only: create workers, delegate, monitor, unblock, report.`;
|
|
46274
46275
|
function createRoom2(db2, input) {
|
|
46275
46276
|
const config2 = { ...DEFAULT_ROOM_CONFIG, ...input.config };
|
|
46276
46277
|
const room = createRoom(db2, input.name, input.goal, config2, input.referredByCode);
|
|
@@ -49085,7 +49086,7 @@ init_db();
|
|
|
49085
49086
|
async function main() {
|
|
49086
49087
|
const server = new McpServer({
|
|
49087
49088
|
name: "quoroom",
|
|
49088
|
-
version: true ? "0.1.
|
|
49089
|
+
version: true ? "0.1.39" : "0.0.0"
|
|
49089
49090
|
});
|
|
49090
49091
|
registerMemoryTools(server);
|
|
49091
49092
|
registerSchedulerTools(server);
|