quoroom 0.1.30 → 0.1.31
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 +266 -185
- package/out/mcp/cli.js +312 -187
- package/out/mcp/server.js +82 -19
- package/package.json +4 -3
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.31",
|
|
9918
9918
|
description: "Autonomous AI agent collective engine \u2014 Queen, Workers, Quorum",
|
|
9919
9919
|
main: "./out/mcp/server.js",
|
|
9920
9920
|
bin: {
|
|
@@ -9962,13 +9962,14 @@ var require_package = __commonJS({
|
|
|
9962
9962
|
"test:watch": "npm run rebuild:native:node && vitest --pool=forks",
|
|
9963
9963
|
"test:quick": "npm run typecheck && npm run test",
|
|
9964
9964
|
"test:smart-e2e": "node scripts/smart-e2e.js",
|
|
9965
|
-
"test:e2e": "npm run build && npx playwright test",
|
|
9966
|
-
"test:e2e:fast": "npm run build:fast && npx playwright test --retries=0",
|
|
9965
|
+
"test:e2e": "npm run build && npx playwright test --project=chromium",
|
|
9966
|
+
"test:e2e:fast": "npm run build:fast && npx playwright test --project=chromium --retries=0",
|
|
9967
9967
|
"test:e2e:smoke": "npm run build:fast && npx playwright test --retries=0 e2e/api.test.ts e2e/crud-decisions.test.ts e2e/crud-goals.test.ts e2e/crud-rooms.test.ts e2e/crud-tasks.test.ts e2e/crud-workers.test.ts e2e/security.test.ts e2e/websocket.test.ts e2e/referral.test.ts",
|
|
9968
9968
|
"test:e2e:ui": "npm run build && npx playwright test e2e/ui.test.ts",
|
|
9969
9969
|
"test:e2e:setup": "npm run build && npx playwright test e2e/setup-flow.test.ts",
|
|
9970
9970
|
"test:e2e:setup:headed": "npm run build && npx playwright test e2e/setup-flow.test.ts --headed --project=chromium",
|
|
9971
9971
|
"test:e2e:providers": "npm run build && npx playwright test e2e/provider-flows.test.ts",
|
|
9972
|
+
"test:e2e:demo": "npm run build && npx playwright test --project=demo",
|
|
9972
9973
|
"rebuild:native:node": `node -e "try{require('better-sqlite3')(':memory:').close()}catch{process.exit(1)}" || npx --yes node-gyp rebuild --directory=node_modules/better-sqlite3`,
|
|
9973
9974
|
prepublishOnly: "npm run build:mcp",
|
|
9974
9975
|
"social:rotate": "node scripts/rotate-social-image.js",
|
|
@@ -11298,46 +11299,59 @@ var CHATGPT_DEFAULTS_BY_PLAN = {
|
|
|
11298
11299
|
var WORKER_ROLE_PRESETS = {
|
|
11299
11300
|
guardian: {
|
|
11300
11301
|
cycleGapMs: 3e4,
|
|
11301
|
-
maxTurns:
|
|
11302
|
+
maxTurns: 30,
|
|
11302
11303
|
systemPromptPrefix: "Check quoroom_list_skills and quoroom_recall first. Monitor and observe. Do not spawn workers or make purchases. Focus on detecting anomalies in tasks, stations, and worker activity."
|
|
11303
11304
|
},
|
|
11304
11305
|
analyst: {
|
|
11305
|
-
cycleGapMs:
|
|
11306
|
-
maxTurns:
|
|
11307
|
-
systemPromptPrefix:
|
|
11306
|
+
cycleGapMs: 6e4,
|
|
11307
|
+
maxTurns: 100,
|
|
11308
|
+
systemPromptPrefix: `Check quoroom_list_skills and quoroom_recall first \u2014 don't repeat research the team has already done.
|
|
11309
|
+
|
|
11310
|
+
Perform deep analysis. Work to COMPLETION \u2014 you have plenty of turns, so finish what you start.
|
|
11311
|
+
Before your cycle ends, save what you accomplished with quoroom_save_wip so the next cycle continues forward.`
|
|
11308
11312
|
},
|
|
11309
11313
|
writer: {
|
|
11310
|
-
cycleGapMs:
|
|
11311
|
-
maxTurns:
|
|
11312
|
-
systemPromptPrefix:
|
|
11314
|
+
cycleGapMs: 6e4,
|
|
11315
|
+
maxTurns: 100,
|
|
11316
|
+
systemPromptPrefix: `Check quoroom_list_skills for existing drafts or style guides.
|
|
11317
|
+
|
|
11318
|
+
Produce high-quality written output. Work to COMPLETION \u2014 you have plenty of turns, so finish the full piece.
|
|
11319
|
+
Before your cycle ends, save what you accomplished with quoroom_save_wip so the next cycle continues forward.`
|
|
11313
11320
|
},
|
|
11314
11321
|
executor: {
|
|
11315
|
-
cycleGapMs:
|
|
11316
|
-
maxTurns:
|
|
11317
|
-
systemPromptPrefix: `
|
|
11322
|
+
cycleGapMs: 15e3,
|
|
11323
|
+
maxTurns: 200,
|
|
11324
|
+
systemPromptPrefix: `You are an execution agent. Your ONLY job is to DO things \u2014 not plan, not vote, not coordinate.
|
|
11325
|
+
|
|
11326
|
+
If you have a WIP from last cycle (shown at top of context), continue from where it says. Take the NEXT action.
|
|
11327
|
+
If you don't have a WIP, look at your assigned tasks and start executing immediately.
|
|
11328
|
+
|
|
11329
|
+
Run your full action chain to completion. You have plenty of turns \u2014 don't rush.
|
|
11330
|
+
When done, save what you accomplished with quoroom_save_wip so the next cycle continues forward.
|
|
11318
11331
|
|
|
11319
|
-
|
|
11332
|
+
Check quoroom_list_skills and quoroom_recall FIRST \u2014 another agent may have already documented what you need.
|
|
11320
11333
|
|
|
11321
11334
|
Browser rules:
|
|
11322
11335
|
- First call: omit sessionId. Note the returned sessionId and pass it on every follow-up call.
|
|
11323
11336
|
- Use fill for standard inputs, type for JS-heavy SPAs. For checkboxes: click text first; if intercepted by label, use CSS selector (e.g. input[type=checkbox]).
|
|
11324
11337
|
- Take snapshot after every action sequence to see page state.
|
|
11325
11338
|
- Store ALL results immediately with quoroom_remember("YourName: [what]", ...).
|
|
11326
|
-
|
|
11327
|
-
The mandatory execution report (quoroom_create_skill) is required every cycle \u2014 see instructions below.`
|
|
11339
|
+
- Create a skill when you COMPLETE a significant action (working recipe for future use).`
|
|
11328
11340
|
},
|
|
11329
11341
|
researcher: {
|
|
11330
|
-
cycleGapMs:
|
|
11331
|
-
maxTurns:
|
|
11342
|
+
cycleGapMs: 3e4,
|
|
11343
|
+
maxTurns: 100,
|
|
11332
11344
|
systemPromptPrefix: `Check quoroom_list_skills and quoroom_recall FIRST \u2014 don't duplicate work the team has already done.
|
|
11333
11345
|
|
|
11334
11346
|
You are a research specialist. Be data-driven: real numbers, URLs, competitor names, pricing data.
|
|
11347
|
+
Work to COMPLETION \u2014 you have plenty of turns, so finish your research before the cycle ends.
|
|
11335
11348
|
|
|
11336
11349
|
Rules:
|
|
11337
11350
|
- Name memories clearly: "YourName: [topic]" so teammates can find them.
|
|
11338
11351
|
- Build on existing research \u2014 quoroom_recall before starting any topic.
|
|
11339
11352
|
- Message key findings to the keeper: quoroom_send_message(to="keeper").
|
|
11340
|
-
- Create a skill when you find a reliable source or repeatable research technique
|
|
11353
|
+
- Create a skill when you find a reliable source or repeatable research technique.
|
|
11354
|
+
- Before your cycle ends, save what you accomplished with quoroom_save_wip.`
|
|
11341
11355
|
}
|
|
11342
11356
|
};
|
|
11343
11357
|
var DEFAULT_ROOM_CONFIG = {
|
|
@@ -11665,10 +11679,14 @@ function mapWorkerRow(row) {
|
|
|
11665
11679
|
agentState: row.agent_state ?? "idle",
|
|
11666
11680
|
votesCast: row.votes_cast ?? 0,
|
|
11667
11681
|
votesMissed: row.votes_missed ?? 0,
|
|
11682
|
+
wip: row.wip ?? null,
|
|
11668
11683
|
createdAt: row.created_at,
|
|
11669
11684
|
updatedAt: row.updated_at
|
|
11670
11685
|
};
|
|
11671
11686
|
}
|
|
11687
|
+
function updateWorkerWip(db2, workerId, wip) {
|
|
11688
|
+
db2.prepare("UPDATE workers SET wip = ?, updated_at = datetime('now','localtime') WHERE id = ?").run(wip, workerId);
|
|
11689
|
+
}
|
|
11672
11690
|
function createTask(db2, input) {
|
|
11673
11691
|
const result = db2.prepare(
|
|
11674
11692
|
`INSERT INTO tasks (name, description, prompt, cron_expression, trigger_type, trigger_config, webhook_token, scheduled_at, executor, max_runs, worker_id, session_continuity, timeout_minutes, max_turns, allowed_tools, disallowed_tools, room_id)
|
|
@@ -13221,7 +13239,8 @@ function countProductiveToolCalls(db2, workerId, lastNCycles = 2) {
|
|
|
13221
13239
|
AND (content LIKE '%web_search%' OR content LIKE '%web_fetch%' OR content LIKE '%remember%'
|
|
13222
13240
|
OR content LIKE '%send_message%' OR content LIKE '%inbox_send%'
|
|
13223
13241
|
OR content LIKE '%update_progress%' OR content LIKE '%complete_goal%'
|
|
13224
|
-
OR content LIKE '%set_goal%' OR content LIKE '%delegate_task%' OR content LIKE '%propose%' OR content LIKE '%vote%'
|
|
13242
|
+
OR content LIKE '%set_goal%' OR content LIKE '%delegate_task%' OR content LIKE '%propose%' OR content LIKE '%vote%'
|
|
13243
|
+
OR content LIKE '%browser%' OR content LIKE '%save_wip%')
|
|
13225
13244
|
`).get(workerId, lastNCycles);
|
|
13226
13245
|
return row.cnt;
|
|
13227
13246
|
}
|
|
@@ -23600,7 +23619,7 @@ async function executeOpenAiWithTools(options) {
|
|
|
23600
23619
|
content: isResume ? `NEW CYCLE. Updated room state:
|
|
23601
23620
|
${options.prompt}
|
|
23602
23621
|
|
|
23603
|
-
|
|
23622
|
+
Take the next action. Do not repeat what was already accomplished (see WIP/context above). Execute to completion.` : options.prompt
|
|
23604
23623
|
}
|
|
23605
23624
|
];
|
|
23606
23625
|
let finalOutput = "";
|
|
@@ -23689,7 +23708,7 @@ async function executeAnthropicWithTools(options) {
|
|
|
23689
23708
|
content: isResume ? `NEW CYCLE. Updated room state:
|
|
23690
23709
|
${options.prompt}
|
|
23691
23710
|
|
|
23692
|
-
|
|
23711
|
+
Take the next action. Do not repeat what was already accomplished (see WIP/context above). Execute to completion.` : options.prompt
|
|
23693
23712
|
}
|
|
23694
23713
|
];
|
|
23695
23714
|
let finalOutput = "";
|
|
@@ -24600,7 +24619,7 @@ async function closeBrowser() {
|
|
|
24600
24619
|
}
|
|
24601
24620
|
}
|
|
24602
24621
|
var _sessions = /* @__PURE__ */ new Map();
|
|
24603
|
-
var SESSION_IDLE_TIMEOUT =
|
|
24622
|
+
var SESSION_IDLE_TIMEOUT = 30 * 60 * 1e3;
|
|
24604
24623
|
var _cleanupInterval = null;
|
|
24605
24624
|
function startSessionCleanup() {
|
|
24606
24625
|
if (_cleanupInterval) return;
|
|
@@ -25277,6 +25296,24 @@ var QUEEN_TOOL_DEFINITIONS = [
|
|
|
25277
25296
|
}
|
|
25278
25297
|
}
|
|
25279
25298
|
}
|
|
25299
|
+
},
|
|
25300
|
+
// ── WIP (Work-In-Progress) ──────────────────────────────────
|
|
25301
|
+
{
|
|
25302
|
+
type: "function",
|
|
25303
|
+
function: {
|
|
25304
|
+
name: "quoroom_save_wip",
|
|
25305
|
+
description: `Save what you accomplished this cycle and what should happen next. This is injected at the TOP of your next cycle's context so you (or a teammate) can continue forward without repeating work. Call this before your cycle ends. Pass "done" or empty string to clear WIP.`,
|
|
25306
|
+
parameters: {
|
|
25307
|
+
type: "object",
|
|
25308
|
+
properties: {
|
|
25309
|
+
status: {
|
|
25310
|
+
type: "string",
|
|
25311
|
+
description: 'What you accomplished and what to do next. Example: "Registered tuta account (user: agent42@tuta.com, pwd in memory). Next: set up email forwarding and notify keeper."'
|
|
25312
|
+
}
|
|
25313
|
+
},
|
|
25314
|
+
required: ["status"]
|
|
25315
|
+
}
|
|
25316
|
+
}
|
|
25280
25317
|
}
|
|
25281
25318
|
];
|
|
25282
25319
|
async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
@@ -25577,6 +25614,13 @@ async function executeQueenTool(db2, roomId, workerId, toolName, args) {
|
|
|
25577
25614
|
const lines = txs.map((tx) => `[${tx.type}] ${tx.amount} USDC \u2014 ${tx.description ?? ""} (${tx.status})`).join("\n");
|
|
25578
25615
|
return { content: lines };
|
|
25579
25616
|
}
|
|
25617
|
+
// ── WIP (Work-In-Progress) ────────────────────────────────
|
|
25618
|
+
case "quoroom_save_wip": {
|
|
25619
|
+
const status2 = String(args.status ?? "").trim();
|
|
25620
|
+
const isDone = !status2 || status2.toLowerCase() === "done" || status2.toLowerCase() === "complete" || status2.toLowerCase() === "completed";
|
|
25621
|
+
updateWorkerWip(db2, workerId, isDone ? null : status2.slice(0, 2e3));
|
|
25622
|
+
return { content: isDone ? "WIP cleared." : "WIP saved. Next cycle will see it at the top of context." };
|
|
25623
|
+
}
|
|
25580
25624
|
default:
|
|
25581
25625
|
return { content: `Unknown tool: ${toolName}`, isError: true };
|
|
25582
25626
|
}
|
|
@@ -25721,7 +25765,8 @@ async function startAgentLoop(db2, roomId, workerId, options) {
|
|
|
25721
25765
|
continue;
|
|
25722
25766
|
}
|
|
25723
25767
|
try {
|
|
25724
|
-
|
|
25768
|
+
const effectiveMaxTurns = Math.max(currentWorker.maxTurns ?? currentRoom.queenMaxTurns, 50);
|
|
25769
|
+
await runCycle(db2, roomId, currentWorker, effectiveMaxTurns, options);
|
|
25725
25770
|
} catch (err) {
|
|
25726
25771
|
if (!loop.running) break;
|
|
25727
25772
|
if (err instanceof RateLimitError) {
|
|
@@ -25760,7 +25805,10 @@ async function startAgentLoop(db2, roomId, workerId, options) {
|
|
|
25760
25805
|
updateAgentState(db2, workerId, "idle");
|
|
25761
25806
|
}
|
|
25762
25807
|
if (!loop.running) break;
|
|
25763
|
-
const
|
|
25808
|
+
const MOMENTUM_GAP = 1e4;
|
|
25809
|
+
const baseGap = currentWorker.cycleGapMs ?? currentRoom.queenCycleGapMs;
|
|
25810
|
+
const freshWorker = getWorker(db2, workerId);
|
|
25811
|
+
const gap = freshWorker?.wip ? Math.min(baseGap, MOMENTUM_GAP) : baseGap;
|
|
25764
25812
|
try {
|
|
25765
25813
|
const abort = new AbortController();
|
|
25766
25814
|
loop.abort = abort;
|
|
@@ -25972,21 +26020,23 @@ ${skillContent}` : ""
|
|
|
25972
26020
|
logBuffer2.flush();
|
|
25973
26021
|
}
|
|
25974
26022
|
const contextParts = [];
|
|
26023
|
+
const isExecutor = worker.role === "executor";
|
|
25975
26024
|
contextParts.push(
|
|
25976
26025
|
`## Your Identity
|
|
25977
26026
|
- Room ID: ${roomId}
|
|
25978
26027
|
- Your Worker ID: ${worker.id}
|
|
25979
26028
|
- Your Name: ${worker.name}`
|
|
25980
26029
|
);
|
|
25981
|
-
const
|
|
25982
|
-
if (
|
|
25983
|
-
|
|
25984
|
-
|
|
25985
|
-
|
|
25986
|
-
|
|
25987
|
-
|
|
25988
|
-
|
|
25989
|
-
|
|
26030
|
+
const wip = worker.wip;
|
|
26031
|
+
if (wip) {
|
|
26032
|
+
contextParts.push(`## >>> CONTINUE FORWARD <<<
|
|
26033
|
+
Last cycle you accomplished / were working on:
|
|
26034
|
+
|
|
26035
|
+
${wip}
|
|
26036
|
+
|
|
26037
|
+
NOW take the NEXT action. Do NOT repeat what's already done \u2014 build on it.
|
|
26038
|
+
If the above action is complete, start a new one toward the room objective.
|
|
26039
|
+
At the end of this cycle, call quoroom_save_wip to save your updated position.`);
|
|
25990
26040
|
}
|
|
25991
26041
|
if (status2.room.goal) {
|
|
25992
26042
|
contextParts.push(`## Room Objective
|
|
@@ -26021,128 +26071,32 @@ These tasks were delegated to you. Prioritize completing them and report progres
|
|
|
26021
26071
|
${memLines.join("\n")}`);
|
|
26022
26072
|
}
|
|
26023
26073
|
}
|
|
26024
|
-
const votingDecisions = listDecisions(db2, roomId, "voting");
|
|
26025
|
-
if (votingDecisions.length > 0) {
|
|
26026
|
-
const decisionLines = votingDecisions.map((d) => {
|
|
26027
|
-
const votes = getVotes(db2, d.id);
|
|
26028
|
-
const alreadyVoted = votes.some((v) => v.workerId === worker.id);
|
|
26029
|
-
const proposerW = d.proposerId ? roomWorkers.find((w) => w.id === d.proposerId) : null;
|
|
26030
|
-
const by = proposerW ? ` (by ${proposerW.name})` : "";
|
|
26031
|
-
const voteStatus = alreadyVoted ? " \u2713 you voted" : " \u2190 VOTE NEEDED";
|
|
26032
|
-
return `- #${d.id}: ${d.proposal}${by} [${votes.length} votes so far, need ${d.minVoters}+]${voteStatus}`;
|
|
26033
|
-
});
|
|
26034
|
-
contextParts.push(`## Pending Proposals \u2014 Use quoroom_vote to cast your vote
|
|
26035
|
-
${decisionLines.join("\n")}`);
|
|
26036
|
-
}
|
|
26037
|
-
const recentResolved = listRecentDecisions(db2, roomId, 5);
|
|
26038
|
-
if (recentResolved.length > 0) {
|
|
26039
|
-
contextParts.push(`## Recent Decisions (already done \u2014 do NOT repeat these)
|
|
26040
|
-
${recentResolved.map((d) => {
|
|
26041
|
-
const icon = d.status === "approved" ? "\u2713" : "\u2717";
|
|
26042
|
-
return `- ${icon} ${d.status}: "${d.proposal.slice(0, 120)}"`;
|
|
26043
|
-
}).join("\n")}`);
|
|
26044
|
-
}
|
|
26045
|
-
const myKeeperMessages = pendingEscalations.filter((e) => e.fromAgentId === worker.id && !e.toAgentId);
|
|
26046
|
-
const incomingWorkerMessages = pendingEscalations.filter((e) => e.toAgentId === worker.id && e.fromAgentId !== worker.id);
|
|
26047
|
-
if (myKeeperMessages.length > 0) {
|
|
26048
|
-
contextParts.push(`## Pending Messages to Keeper (awaiting reply)
|
|
26049
|
-
${myKeeperMessages.map(
|
|
26050
|
-
(e) => `- #${e.id}: ${e.question}`
|
|
26051
|
-
).join("\n")}`);
|
|
26052
|
-
}
|
|
26053
|
-
if (incomingWorkerMessages.length > 0) {
|
|
26054
|
-
const senderNames = new Map(roomWorkers.map((w) => [w.id, w.name]));
|
|
26055
|
-
contextParts.push(`## Messages from Other Workers
|
|
26056
|
-
${incomingWorkerMessages.map((e) => {
|
|
26057
|
-
const sender = senderNames.get(e.fromAgentId ?? 0) ?? `Worker #${e.fromAgentId}`;
|
|
26058
|
-
return `- #${e.id} from ${sender}: ${e.question}`;
|
|
26059
|
-
}).join("\n")}`);
|
|
26060
|
-
}
|
|
26061
|
-
if (recentKeeperAnswers.length > 0) {
|
|
26062
|
-
contextParts.push(`## Keeper Answers (recent)
|
|
26063
|
-
${recentKeeperAnswers.map(
|
|
26064
|
-
(e) => `- Q: ${e.question}
|
|
26065
|
-
A: ${e.answer}`
|
|
26066
|
-
).join("\n")}`);
|
|
26067
|
-
}
|
|
26068
|
-
const activitySlice = recentActivity.slice(0, 15);
|
|
26069
|
-
if (activitySlice.length > 0) {
|
|
26070
|
-
contextParts.push(`## Recent Activity
|
|
26071
|
-
${activitySlice.map(
|
|
26072
|
-
(a) => `- [${a.eventType}] ${a.summary}`
|
|
26073
|
-
).join("\n")}`);
|
|
26074
|
-
}
|
|
26075
|
-
if (roomWorkers.length > 0) {
|
|
26076
|
-
contextParts.push(`## Room Workers
|
|
26077
|
-
${roomWorkers.map(
|
|
26078
|
-
(w) => `- #${w.id} ${w.name}${w.role ? ` (${w.role})` : ""} \u2014 ${w.agentState}`
|
|
26079
|
-
).join("\n")}`);
|
|
26080
|
-
}
|
|
26081
|
-
if (roomTasks.length > 0) {
|
|
26082
|
-
contextParts.push(`## Room Tasks
|
|
26083
|
-
${roomTasks.map(
|
|
26084
|
-
(t) => `- #${t.id} "${t.name}" [${t.triggerType}] \u2014 ${t.status}`
|
|
26085
|
-
).join("\n")}`);
|
|
26086
|
-
}
|
|
26087
|
-
const wallet = getWalletByRoom(db2, roomId);
|
|
26088
|
-
if (wallet) {
|
|
26089
|
-
const summary = getWalletTransactionSummary(db2, wallet.id);
|
|
26090
|
-
const net = (parseFloat(summary.received) - parseFloat(summary.sent)).toFixed(2);
|
|
26091
|
-
contextParts.push(`## Wallet
|
|
26092
|
-
Address: ${wallet.address}
|
|
26093
|
-
Balance: ${net} USDC (received: ${summary.received}, spent: ${summary.sent})`);
|
|
26094
|
-
}
|
|
26095
|
-
if (unreadMessages.length > 0) {
|
|
26096
|
-
contextParts.push(`## Unread Messages
|
|
26097
|
-
${unreadMessages.map(
|
|
26098
|
-
(m) => `- #${m.id} from ${m.fromRoomId ?? "unknown"}: ${m.subject}`
|
|
26099
|
-
).join("\n")}`);
|
|
26100
|
-
}
|
|
26101
|
-
const activeStations = cloudStations.filter((s) => s.status === "active");
|
|
26102
|
-
if (cloudStations.length > 0) {
|
|
26103
|
-
const stationLines = cloudStations.map(
|
|
26104
|
-
(s) => `- #${s.id} "${s.stationName}" (${s.tier}) \u2014 ${s.status} \u2014 $${s.monthlyCost}/mo`
|
|
26105
|
-
);
|
|
26106
|
-
contextParts.push(`## Stations (${activeStations.length} active)
|
|
26107
|
-
${stationLines.join("\n")}`);
|
|
26108
|
-
}
|
|
26109
|
-
if (publicRooms.length > 0) {
|
|
26110
|
-
const top3 = publicRooms.slice(0, 3);
|
|
26111
|
-
contextParts.push(
|
|
26112
|
-
`## Public Rooms (cross-room learning)
|
|
26113
|
-
Other rooms you can learn strategies from:
|
|
26114
|
-
${top3.map(
|
|
26115
|
-
(r, i) => `${i + 1}. "${r.name}" \u2014 ${r.earnings} USDC | Goal: ${r.goal ?? "No goal set"}`
|
|
26116
|
-
).join("\n")}`
|
|
26117
|
-
);
|
|
26118
|
-
}
|
|
26119
|
-
const rateLimitEvents = recentActivity.filter(
|
|
26120
|
-
(a) => a.eventType === "system" && a.summary.includes("rate limited")
|
|
26121
|
-
);
|
|
26122
|
-
const settingsParts = [
|
|
26123
|
-
`- Cycle gap: ${Math.round(status2.room.queenCycleGapMs / 1e3)}s`,
|
|
26124
|
-
`- Max turns per cycle: ${status2.room.queenMaxTurns}`,
|
|
26125
|
-
`- Max concurrent tasks: ${status2.room.maxConcurrentTasks}`
|
|
26126
|
-
];
|
|
26127
|
-
if (rateLimitEvents.length > 0) {
|
|
26128
|
-
settingsParts.push(`- **Rate limits hit recently: ${rateLimitEvents.length}** (in last ${recentActivity.length} events)`);
|
|
26129
|
-
}
|
|
26130
|
-
contextParts.push(`## Execution Settings
|
|
26131
|
-
${settingsParts.join("\n")}`);
|
|
26132
26074
|
const STUCK_THRESHOLD_CYCLES = 2;
|
|
26133
26075
|
const productiveCallCount = countProductiveToolCalls(db2, worker.id, STUCK_THRESHOLD_CYCLES);
|
|
26134
26076
|
const recentCompletedCycles = listRoomCycles(db2, roomId, 5).filter((c) => c.workerId === worker.id && c.status === "completed");
|
|
26135
26077
|
const isStuck = recentCompletedCycles.length >= STUCK_THRESHOLD_CYCLES && productiveCallCount === 0;
|
|
26136
26078
|
if (isStuck) {
|
|
26137
|
-
|
|
26138
|
-
|
|
26139
|
-
|
|
26140
|
-
-
|
|
26141
|
-
-
|
|
26142
|
-
-
|
|
26143
|
-
Do NOT
|
|
26144
|
-
|
|
26079
|
+
if (wip) {
|
|
26080
|
+
contextParts.push(`## \u26A0 ACTION STALLED
|
|
26081
|
+
Your last ${STUCK_THRESHOLD_CYCLES} cycles had a WIP but produced no external results. Your current approach may be blocked.
|
|
26082
|
+
- Try a different method to accomplish the same goal
|
|
26083
|
+
- Save what you learned (quoroom_remember) and report the blocker (quoroom_send_message to keeper)
|
|
26084
|
+
- Clear your WIP (quoroom_save_wip with a new plan) and try an alternate approach
|
|
26085
|
+
Do NOT keep retrying the same failing steps.`);
|
|
26086
|
+
} else {
|
|
26087
|
+
contextParts.push(`## \u26A0 STUCK \u2014 TAKE ACTION NOW
|
|
26088
|
+
Your last ${STUCK_THRESHOLD_CYCLES} cycles produced no external results (no web searches, no memories stored, no goal progress, no keeper messages). You are circling.
|
|
26089
|
+
STOP planning and ideating. Pick ONE concrete action and execute it NOW:
|
|
26090
|
+
- Use quoroom_browser to register an account, buy a domain, or fill a form
|
|
26091
|
+
- Use web search to find a specific lead or service
|
|
26092
|
+
- Use quoroom_send_message to report what is blocking you
|
|
26093
|
+
Then save your progress: quoroom_save_wip("Doing X, reached step Y")`);
|
|
26094
|
+
}
|
|
26095
|
+
logBuffer2.addSynthetic("system", `Stuck detector: 0 productive tool calls in last ${STUCK_THRESHOLD_CYCLES} cycles \u2014 injecting ${wip ? "stalled" : "stuck"} directive`);
|
|
26145
26096
|
}
|
|
26097
|
+
const rateLimitEvents = recentActivity.filter(
|
|
26098
|
+
(a) => a.eventType === "system" && a.summary.includes("rate limited")
|
|
26099
|
+
);
|
|
26146
26100
|
const selfRegulateHint = rateLimitEvents.length > 0 ? "\n- **Self-regulate**: You are hitting rate limits. Use quoroom_configure_room to increase your cycle gap or reduce max turns to stay within API limits." : "";
|
|
26147
26101
|
const isClaude = model === "claude" || model.startsWith("claude-");
|
|
26148
26102
|
const toolCallInstruction = isClaude ? "Always call tools to take action \u2014 do not just describe what you would do." : "IMPORTANT: You MUST call at least one tool in your response. Respond ONLY with a tool call \u2014 do not write explanatory text without a tool call.";
|
|
@@ -26152,16 +26106,20 @@ Do NOT repeat the same approach. Pivot immediately.`);
|
|
|
26152
26106
|
const toolLines = [];
|
|
26153
26107
|
const goalTools = ["quoroom_set_goal", "quoroom_update_progress", "quoroom_create_subgoal", "quoroom_delegate_task", "quoroom_complete_goal", "quoroom_abandon_goal"].filter(has);
|
|
26154
26108
|
if (goalTools.length) toolLines.push(`**Goals:** ${goalTools.join(", ")}`);
|
|
26155
|
-
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
26109
|
+
if (!isExecutor) {
|
|
26110
|
+
const govTools = ["quoroom_propose", "quoroom_vote"].filter(has);
|
|
26111
|
+
if (govTools.length) toolLines.push(`**Governance:** ${govTools.join(", ")}`);
|
|
26112
|
+
const workerTools = ["quoroom_create_worker", "quoroom_update_worker"].filter(has);
|
|
26113
|
+
if (workerTools.length) toolLines.push(`**Workers:** ${workerTools.join(", ")}`);
|
|
26114
|
+
if (has("quoroom_schedule")) toolLines.push("**Tasks:** quoroom_schedule");
|
|
26115
|
+
}
|
|
26160
26116
|
const memTools = ["quoroom_remember", "quoroom_recall"].filter(has);
|
|
26161
26117
|
if (memTools.length) toolLines.push(`**Memory:** ${memTools.join(", ")}`);
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26118
|
+
if (!isExecutor) {
|
|
26119
|
+
const walletToolNames = isCli ? ["quoroom_wallet_balance", "quoroom_wallet_send", "quoroom_wallet_history", "quoroom_wallet_topup"] : ["quoroom_wallet_balance", "quoroom_wallet_send", "quoroom_wallet_history"];
|
|
26120
|
+
const filteredWallet = walletToolNames.filter(has);
|
|
26121
|
+
if (filteredWallet.length) toolLines.push(`**Wallet:** ${filteredWallet.join(", ")}`);
|
|
26122
|
+
}
|
|
26165
26123
|
const webToolNames = isCli ? null : ["quoroom_web_search", "quoroom_web_fetch", "quoroom_browser"];
|
|
26166
26124
|
if (isCli) {
|
|
26167
26125
|
if (has("quoroom_web_search") || has("quoroom_web_fetch")) toolLines.push("**Web:** (use your built-in web search and fetch tools)");
|
|
@@ -26176,18 +26134,25 @@ Do NOT repeat the same approach. Pivot immediately.`);
|
|
|
26176
26134
|
if (isCli) toolLines.push(`**Comms:** ${filteredComms.map((t) => t === "quoroom_send_message" ? `${t} (message keeper or worker)` : t === "quoroom_inbox_list" ? `${t} (inter-room)` : t).join(", ")}`);
|
|
26177
26135
|
else toolLines.push(`**Comms:** ${filteredComms.join(", ")}`);
|
|
26178
26136
|
}
|
|
26179
|
-
if (has("quoroom_configure_room")) toolLines.push(`**Settings:** quoroom_configure_room${selfRegulateHint}`);
|
|
26137
|
+
if (!isExecutor && has("quoroom_configure_room")) toolLines.push(`**Settings:** quoroom_configure_room${selfRegulateHint}`);
|
|
26180
26138
|
const skillToolNames = ["quoroom_create_skill", "quoroom_list_skills", "quoroom_edit_skill", "quoroom_activate_skill", "quoroom_deactivate_skill"].filter(has);
|
|
26181
|
-
if (skillToolNames.length) toolLines.push(`**Skills:** ${skillToolNames.join(", ")}
|
|
26139
|
+
if (skillToolNames.length) toolLines.push(`**Skills:** ${skillToolNames.join(", ")}`);
|
|
26140
|
+
if (has("quoroom_save_wip")) toolLines.push("**WIP:** quoroom_save_wip \u2014 save what you accomplished so the next cycle continues forward");
|
|
26182
26141
|
const toolList = toolLines.join("\n");
|
|
26142
|
+
const hasWip = !!wip;
|
|
26143
|
+
const actionPriority = hasWip ? "You have an active WIP above \u2014 your #1 priority is to CONTINUE that action. Only handle housekeeping (votes, messages) after your main action is done or blocked." : "Take concrete action toward the room objective. Execute \u2014 do not just plan or analyze.";
|
|
26183
26144
|
contextParts.push(`## Instructions
|
|
26184
|
-
|
|
26145
|
+
${actionPriority}
|
|
26146
|
+
|
|
26147
|
+
Available tools:
|
|
26185
26148
|
|
|
26186
26149
|
${toolList}
|
|
26187
26150
|
|
|
26188
|
-
|
|
26151
|
+
You have plenty of turns \u2014 run your action to completion. Don't rush.
|
|
26152
|
+
Before your cycle ends, save what you accomplished: quoroom_save_wip(...).
|
|
26153
|
+
Create a skill (quoroom_create_skill) when you COMPLETE a significant action \u2014 this shares the recipe with the whole team.
|
|
26189
26154
|
|
|
26190
|
-
Revenue is always a priority.
|
|
26155
|
+
Revenue is always a priority. Actively seek ways to earn: offer services to the keeper, propose paid work to other rooms, or find monetizable opportunities.
|
|
26191
26156
|
|
|
26192
26157
|
${toolCallInstruction}`);
|
|
26193
26158
|
if (has("quoroom_browser")) {
|
|
@@ -26197,7 +26162,7 @@ You have a persistent browser tool. Use it to interact with websites: navigate,
|
|
|
26197
26162
|
**Session persistence:**
|
|
26198
26163
|
- First call: omit sessionId \u2192 starts fresh session. Note the returned sessionId.
|
|
26199
26164
|
- Follow-up calls: pass sessionId back \u2192 keeps cookies, login state, localStorage.
|
|
26200
|
-
- Sessions expire after
|
|
26165
|
+
- Sessions expire after 30 min of inactivity. If expired, start a new session and re-login.
|
|
26201
26166
|
|
|
26202
26167
|
**Reliable form filling (learned techniques):**
|
|
26203
26168
|
- Always end action sequences with a \`snapshot\` to see current page state.
|
|
@@ -26218,31 +26183,139 @@ You have a persistent browser tool. Use it to interact with websites: navigate,
|
|
|
26218
26183
|
}
|
|
26219
26184
|
if (skillToolNames.length || memTools.length) {
|
|
26220
26185
|
const parts = ["## Knowledge Persistence"];
|
|
26221
|
-
parts.push("
|
|
26186
|
+
parts.push("Save important discoveries so you and your teammates can reuse them.");
|
|
26222
26187
|
if (memTools.length) {
|
|
26223
26188
|
parts.push(`
|
|
26224
|
-
**Memory** (quoroom_remember/recall): Store facts, credentials, contacts, research results. Check memory with quoroom_recall before starting work \u2014 don't repeat what's already done
|
|
26189
|
+
**Memory** (quoroom_remember/recall): Store facts, credentials, contacts, research results. Check memory with quoroom_recall before starting work \u2014 don't repeat what's already done.`);
|
|
26225
26190
|
}
|
|
26226
26191
|
if (skillToolNames.length) {
|
|
26227
26192
|
parts.push(`
|
|
26228
|
-
**Skills
|
|
26229
|
-
|
|
26230
|
-
**MANDATORY: Before your cycle ends, call quoroom_create_skill with a report:**
|
|
26231
|
-
- Title: "[task]: [site/service name]" (e.g. "Tuta signup", "Email scraping from GitHub")
|
|
26232
|
-
- Body: step-by-step algorithm you used, including:
|
|
26233
|
-
1. What you tried FIRST (and why it failed, with exact error or behavior)
|
|
26234
|
-
2. What you tried NEXT (and whether it worked)
|
|
26235
|
-
3. The WORKING approach with exact selectors, URLs, field names
|
|
26236
|
-
4. Gotchas and warnings (e.g. "checkbox click intercepted by label \u2014 use CSS selector instead")
|
|
26237
|
-
- Set \`autoActivate: true\` and \`activationContext\` with keywords (e.g. ["tuta", "signup", "email", "checkbox"])
|
|
26238
|
-
|
|
26239
|
-
Example skill body:
|
|
26240
|
-
"## Tuta Free Account Signup\\n1. Navigate to app.tuta.com \u2192 click 'Sign up'\\n2. Select Free plan (3rd radio) \u2192 click 'Continue'\\n3. Username: use 'fill' on textbox labeled 'Email address'\\n4. Password: use 'type' (not fill) \u2014 SPA input needs char-by-char\\n5. Checkboxes: text click fails (label intercepts). USE CSS: input[type=checkbox]:nth-of-type(1) and :nth-of-type(2)\\n6. CAPTCHA: clock type \u2014 screenshot it, read time as hh:mm\\nFAILED: Ctrl+A to clear field (doesn't work in this SPA). Use triple-click or reload page instead."
|
|
26241
|
-
|
|
26242
|
-
This is NOT optional \u2014 every cycle must produce at least one skill report.`);
|
|
26193
|
+
**Skills** (quoroom_create_skill): When you COMPLETE a significant action, create a skill documenting the working recipe (step-by-step algorithm, exact selectors, gotchas). Skills are injected into every agent's prompt \u2014 the whole team benefits.`);
|
|
26243
26194
|
}
|
|
26244
26195
|
contextParts.push(parts.join("\n"));
|
|
26245
26196
|
}
|
|
26197
|
+
if (!isExecutor) {
|
|
26198
|
+
const housekeepingParts = [];
|
|
26199
|
+
const votingDecisions = listDecisions(db2, roomId, "voting");
|
|
26200
|
+
if (votingDecisions.length > 0) {
|
|
26201
|
+
const decisionLines = votingDecisions.map((d) => {
|
|
26202
|
+
const votes = getVotes(db2, d.id);
|
|
26203
|
+
const alreadyVoted = votes.some((v) => v.workerId === worker.id);
|
|
26204
|
+
const proposerW = d.proposerId ? roomWorkers.find((w) => w.id === d.proposerId) : null;
|
|
26205
|
+
const by = proposerW ? ` (by ${proposerW.name})` : "";
|
|
26206
|
+
const voteStatus = alreadyVoted ? " \u2713 you voted" : " \u2190 VOTE NEEDED";
|
|
26207
|
+
return `- #${d.id}: ${d.proposal}${by} [${votes.length} votes so far, need ${d.minVoters}+]${voteStatus}`;
|
|
26208
|
+
});
|
|
26209
|
+
housekeepingParts.push(`**Pending Proposals** \u2014 Use quoroom_vote to cast your vote
|
|
26210
|
+
${decisionLines.join("\n")}`);
|
|
26211
|
+
}
|
|
26212
|
+
const recentResolved = listRecentDecisions(db2, roomId, 5);
|
|
26213
|
+
if (recentResolved.length > 0) {
|
|
26214
|
+
housekeepingParts.push(`**Recent Decisions** (already done \u2014 do NOT repeat)
|
|
26215
|
+
${recentResolved.map((d) => {
|
|
26216
|
+
const icon = d.status === "approved" ? "\u2713" : "\u2717";
|
|
26217
|
+
return `- ${icon} ${d.status}: "${d.proposal.slice(0, 120)}"`;
|
|
26218
|
+
}).join("\n")}`);
|
|
26219
|
+
}
|
|
26220
|
+
const myKeeperMessages = pendingEscalations.filter((e) => e.fromAgentId === worker.id && !e.toAgentId);
|
|
26221
|
+
const incomingWorkerMessages = pendingEscalations.filter((e) => e.toAgentId === worker.id && e.fromAgentId !== worker.id);
|
|
26222
|
+
if (myKeeperMessages.length > 0) {
|
|
26223
|
+
housekeepingParts.push(`**Pending Messages to Keeper** (awaiting reply)
|
|
26224
|
+
${myKeeperMessages.map(
|
|
26225
|
+
(e) => `- #${e.id}: ${e.question}`
|
|
26226
|
+
).join("\n")}`);
|
|
26227
|
+
}
|
|
26228
|
+
if (incomingWorkerMessages.length > 0) {
|
|
26229
|
+
const senderNames = new Map(roomWorkers.map((w) => [w.id, w.name]));
|
|
26230
|
+
housekeepingParts.push(`**Messages from Other Workers**
|
|
26231
|
+
${incomingWorkerMessages.map((e) => {
|
|
26232
|
+
const sender = senderNames.get(e.fromAgentId ?? 0) ?? `Worker #${e.fromAgentId}`;
|
|
26233
|
+
return `- #${e.id} from ${sender}: ${e.question}`;
|
|
26234
|
+
}).join("\n")}`);
|
|
26235
|
+
}
|
|
26236
|
+
if (recentKeeperAnswers.length > 0) {
|
|
26237
|
+
housekeepingParts.push(`**Keeper Answers** (recent)
|
|
26238
|
+
${recentKeeperAnswers.map(
|
|
26239
|
+
(e) => `- Q: ${e.question}
|
|
26240
|
+
A: ${e.answer}`
|
|
26241
|
+
).join("\n")}`);
|
|
26242
|
+
}
|
|
26243
|
+
const activitySlice = recentActivity.slice(0, 15);
|
|
26244
|
+
if (activitySlice.length > 0) {
|
|
26245
|
+
housekeepingParts.push(`**Recent Activity**
|
|
26246
|
+
${activitySlice.map(
|
|
26247
|
+
(a) => `- [${a.eventType}] ${a.summary}`
|
|
26248
|
+
).join("\n")}`);
|
|
26249
|
+
}
|
|
26250
|
+
if (roomWorkers.length > 0) {
|
|
26251
|
+
housekeepingParts.push(`**Room Workers**
|
|
26252
|
+
${roomWorkers.map(
|
|
26253
|
+
(w) => `- #${w.id} ${w.name}${w.role ? ` (${w.role})` : ""} \u2014 ${w.agentState}`
|
|
26254
|
+
).join("\n")}`);
|
|
26255
|
+
}
|
|
26256
|
+
if (roomTasks.length > 0) {
|
|
26257
|
+
housekeepingParts.push(`**Room Tasks**
|
|
26258
|
+
${roomTasks.map(
|
|
26259
|
+
(t) => `- #${t.id} "${t.name}" [${t.triggerType}] \u2014 ${t.status}`
|
|
26260
|
+
).join("\n")}`);
|
|
26261
|
+
}
|
|
26262
|
+
if (housekeepingParts.length > 0) {
|
|
26263
|
+
contextParts.push(`## Housekeeping (handle after your main action)
|
|
26264
|
+
${housekeepingParts.join("\n\n")}`);
|
|
26265
|
+
}
|
|
26266
|
+
}
|
|
26267
|
+
const wallet = getWalletByRoom(db2, roomId);
|
|
26268
|
+
if (wallet) {
|
|
26269
|
+
const summary = getWalletTransactionSummary(db2, wallet.id);
|
|
26270
|
+
const net = (parseFloat(summary.received) - parseFloat(summary.sent)).toFixed(2);
|
|
26271
|
+
contextParts.push(`## Wallet
|
|
26272
|
+
Address: ${wallet.address}
|
|
26273
|
+
Balance: ${net} USDC (received: ${summary.received}, spent: ${summary.sent})`);
|
|
26274
|
+
}
|
|
26275
|
+
if (unreadMessages.length > 0) {
|
|
26276
|
+
contextParts.push(`## Unread Messages
|
|
26277
|
+
${unreadMessages.map(
|
|
26278
|
+
(m) => `- #${m.id} from ${m.fromRoomId ?? "unknown"}: ${m.subject}`
|
|
26279
|
+
).join("\n")}`);
|
|
26280
|
+
}
|
|
26281
|
+
const activeStations = cloudStations.filter((s) => s.status === "active");
|
|
26282
|
+
if (cloudStations.length > 0) {
|
|
26283
|
+
const stationLines = cloudStations.map(
|
|
26284
|
+
(s) => `- #${s.id} "${s.stationName}" (${s.tier}) \u2014 ${s.status} \u2014 $${s.monthlyCost}/mo`
|
|
26285
|
+
);
|
|
26286
|
+
contextParts.push(`## Stations (${activeStations.length} active)
|
|
26287
|
+
${stationLines.join("\n")}`);
|
|
26288
|
+
}
|
|
26289
|
+
if (publicRooms.length > 0) {
|
|
26290
|
+
const top3 = publicRooms.slice(0, 3);
|
|
26291
|
+
contextParts.push(
|
|
26292
|
+
`## Public Rooms (cross-room learning)
|
|
26293
|
+
Other rooms you can learn strategies from:
|
|
26294
|
+
${top3.map(
|
|
26295
|
+
(r, i) => `${i + 1}. "${r.name}" \u2014 ${r.earnings} USDC | Goal: ${r.goal ?? "No goal set"}`
|
|
26296
|
+
).join("\n")}`
|
|
26297
|
+
);
|
|
26298
|
+
}
|
|
26299
|
+
const keeperReferralCode = getSetting(db2, "keeper_referral_code")?.trim();
|
|
26300
|
+
if (keeperReferralCode) {
|
|
26301
|
+
const encodedKeeperCode = encodeURIComponent(keeperReferralCode);
|
|
26302
|
+
contextParts.push(
|
|
26303
|
+
`## Keeper Referral
|
|
26304
|
+
- Keeper code: ${keeperReferralCode}
|
|
26305
|
+
- Invite link: https://quoroom.ai/invite/${encodedKeeperCode}
|
|
26306
|
+
- Share link: https://quoroom.ai/share/v2/${encodedKeeperCode}`
|
|
26307
|
+
);
|
|
26308
|
+
}
|
|
26309
|
+
const settingsParts = [
|
|
26310
|
+
`- Cycle gap: ${Math.round(status2.room.queenCycleGapMs / 1e3)}s`,
|
|
26311
|
+
`- Max turns per cycle: ${status2.room.queenMaxTurns}`,
|
|
26312
|
+
`- Max concurrent tasks: ${status2.room.maxConcurrentTasks}`
|
|
26313
|
+
];
|
|
26314
|
+
if (rateLimitEvents.length > 0) {
|
|
26315
|
+
settingsParts.push(`- **Rate limits hit recently: ${rateLimitEvents.length}** (in last ${recentActivity.length} events)`);
|
|
26316
|
+
}
|
|
26317
|
+
contextParts.push(`## Execution Settings
|
|
26318
|
+
${settingsParts.join("\n")}`);
|
|
26246
26319
|
const prompt = contextParts.join("\n\n");
|
|
26247
26320
|
updateAgentState(db2, worker.id, "acting");
|
|
26248
26321
|
const promptTokenEstimate = Math.round(prompt.length / 4);
|
|
@@ -26265,8 +26338,8 @@ This is NOT optional \u2014 every cycle must produce at least one skill report.`
|
|
|
26265
26338
|
prompt,
|
|
26266
26339
|
systemPrompt,
|
|
26267
26340
|
apiKey,
|
|
26268
|
-
timeoutMs:
|
|
26269
|
-
maxTurns: maxTurns ??
|
|
26341
|
+
timeoutMs: worker.role === "executor" ? 30 * 60 * 1e3 : 15 * 60 * 1e3,
|
|
26342
|
+
maxTurns: maxTurns ?? 50,
|
|
26270
26343
|
onConsoleLog: logBuffer2.onConsoleLog,
|
|
26271
26344
|
// CLI models: block non-quoroom MCP tools (daymon, etc.)
|
|
26272
26345
|
disallowedTools: isCli ? "mcp__daymon*" : void 0,
|
|
@@ -32412,6 +32485,7 @@ CREATE TABLE IF NOT EXISTS workers (
|
|
|
32412
32485
|
agent_state TEXT NOT NULL DEFAULT 'idle',
|
|
32413
32486
|
votes_cast INTEGER NOT NULL DEFAULT 0,
|
|
32414
32487
|
votes_missed INTEGER NOT NULL DEFAULT 0,
|
|
32488
|
+
wip TEXT,
|
|
32415
32489
|
created_at DATETIME DEFAULT (datetime('now','localtime')),
|
|
32416
32490
|
updated_at DATETIME DEFAULT (datetime('now','localtime'))
|
|
32417
32491
|
);
|
|
@@ -32955,6 +33029,13 @@ function runMigrations(database, log = console.log) {
|
|
|
32955
33029
|
database.exec(`ALTER TABLE rooms ADD COLUMN allowed_tools TEXT`);
|
|
32956
33030
|
log("Migrated: added allowed_tools column to rooms");
|
|
32957
33031
|
}
|
|
33032
|
+
const hasWorkerWip = database.prepare(
|
|
33033
|
+
`SELECT name FROM pragma_table_info('workers') WHERE name='wip'`
|
|
33034
|
+
).get()?.name;
|
|
33035
|
+
if (!hasWorkerWip) {
|
|
33036
|
+
database.exec(`ALTER TABLE workers ADD COLUMN wip TEXT`);
|
|
33037
|
+
log("Migrated: added wip column to workers");
|
|
33038
|
+
}
|
|
32958
33039
|
const ollamaWorkers = database.prepare(`SELECT id FROM workers WHERE model LIKE 'ollama:%'`).all();
|
|
32959
33040
|
if (ollamaWorkers.length > 0) {
|
|
32960
33041
|
database.prepare(`UPDATE workers SET model = 'claude' WHERE model LIKE 'ollama:%'`).run();
|
|
@@ -33178,7 +33259,7 @@ function semverGt(a, b) {
|
|
|
33178
33259
|
}
|
|
33179
33260
|
function getCurrentVersion() {
|
|
33180
33261
|
try {
|
|
33181
|
-
return true ? "0.1.
|
|
33262
|
+
return true ? "0.1.31" : null.version;
|
|
33182
33263
|
} catch {
|
|
33183
33264
|
return "0.0.0";
|
|
33184
33265
|
}
|
|
@@ -33337,7 +33418,7 @@ var cachedVersion = null;
|
|
|
33337
33418
|
function getVersion3() {
|
|
33338
33419
|
if (cachedVersion) return cachedVersion;
|
|
33339
33420
|
try {
|
|
33340
|
-
cachedVersion = true ? "0.1.
|
|
33421
|
+
cachedVersion = true ? "0.1.31" : null.version;
|
|
33341
33422
|
} catch {
|
|
33342
33423
|
cachedVersion = "unknown";
|
|
33343
33424
|
}
|