chatroom-cli 1.11.0 → 1.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +164 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14908,7 +14908,8 @@ async function initDaemon() {
|
|
|
14908
14908
|
events,
|
|
14909
14909
|
agentServices,
|
|
14910
14910
|
activeWorkingDirs: new Set,
|
|
14911
|
-
lastPushedGitState: new Map
|
|
14911
|
+
lastPushedGitState: new Map,
|
|
14912
|
+
agentEndedTurn: new Map
|
|
14912
14913
|
};
|
|
14913
14914
|
registerEventListeners(ctx);
|
|
14914
14915
|
logStartup(ctx, availableModels);
|
|
@@ -15032,6 +15033,8 @@ async function executeStartAgent(ctx, args) {
|
|
|
15032
15033
|
const { pid } = spawnResult;
|
|
15033
15034
|
const msg = `Agent spawned (PID: ${pid})`;
|
|
15034
15035
|
console.log(` ✅ ${msg}`);
|
|
15036
|
+
const agentEndKey = `${chatroomId}:${role.toLowerCase()}`;
|
|
15037
|
+
ctx.agentEndedTurn.delete(agentEndKey);
|
|
15035
15038
|
ctx.deps.spawning.recordSpawn(chatroomId);
|
|
15036
15039
|
try {
|
|
15037
15040
|
await ctx.deps.backend.mutation(api.machines.updateSpawnedAgent, {
|
|
@@ -15072,6 +15075,7 @@ async function executeStartAgent(ctx, args) {
|
|
|
15072
15075
|
});
|
|
15073
15076
|
if (spawnResult.onAgentEnd) {
|
|
15074
15077
|
spawnResult.onAgentEnd(() => {
|
|
15078
|
+
ctx.agentEndedTurn.set(agentEndKey, true);
|
|
15075
15079
|
try {
|
|
15076
15080
|
ctx.deps.processes.kill(-pid, "SIGTERM");
|
|
15077
15081
|
} catch {}
|
|
@@ -15729,6 +15733,162 @@ var init_git_heartbeat = __esm(() => {
|
|
|
15729
15733
|
init_git_polling();
|
|
15730
15734
|
});
|
|
15731
15735
|
|
|
15736
|
+
// src/infrastructure/services/remote-agents/harness-restart-policy.ts
|
|
15737
|
+
class OpenCodeRestartPolicy {
|
|
15738
|
+
id = "opencode";
|
|
15739
|
+
shouldStartAgent(params) {
|
|
15740
|
+
const { task, agentConfig } = params;
|
|
15741
|
+
if (agentConfig.desiredState !== "running") {
|
|
15742
|
+
return false;
|
|
15743
|
+
}
|
|
15744
|
+
if (agentConfig.circuitState === "open") {
|
|
15745
|
+
return false;
|
|
15746
|
+
}
|
|
15747
|
+
if (task.status === "in_progress") {
|
|
15748
|
+
return agentConfig.spawnedAgentPid == null;
|
|
15749
|
+
}
|
|
15750
|
+
if (task.status === "pending" || task.status === "acknowledged") {
|
|
15751
|
+
return agentConfig.spawnedAgentPid == null;
|
|
15752
|
+
}
|
|
15753
|
+
return false;
|
|
15754
|
+
}
|
|
15755
|
+
}
|
|
15756
|
+
|
|
15757
|
+
class PiRestartPolicy {
|
|
15758
|
+
id = "pi";
|
|
15759
|
+
shouldStartAgent(params, context) {
|
|
15760
|
+
const { task, agentConfig } = params;
|
|
15761
|
+
if (agentConfig.desiredState !== "running") {
|
|
15762
|
+
return false;
|
|
15763
|
+
}
|
|
15764
|
+
if (agentConfig.circuitState === "open") {
|
|
15765
|
+
return false;
|
|
15766
|
+
}
|
|
15767
|
+
if (task.status === "in_progress") {
|
|
15768
|
+
return agentConfig.spawnedAgentPid == null;
|
|
15769
|
+
}
|
|
15770
|
+
if (task.status === "pending" || task.status === "acknowledged") {
|
|
15771
|
+
if (agentConfig.spawnedAgentPid == null) {
|
|
15772
|
+
return true;
|
|
15773
|
+
}
|
|
15774
|
+
}
|
|
15775
|
+
if (task.status !== "pending" && task.status !== "acknowledged") {
|
|
15776
|
+
return false;
|
|
15777
|
+
}
|
|
15778
|
+
if (!context?.agentEndedTurn) {
|
|
15779
|
+
return false;
|
|
15780
|
+
}
|
|
15781
|
+
const key = `${task.chatroomId}:${agentConfig.role}`;
|
|
15782
|
+
const hasEndedTurn = context.agentEndedTurn.get(key);
|
|
15783
|
+
return hasEndedTurn === true;
|
|
15784
|
+
}
|
|
15785
|
+
}
|
|
15786
|
+
function getRestartPolicyForHarness(harness) {
|
|
15787
|
+
switch (harness) {
|
|
15788
|
+
case "opencode":
|
|
15789
|
+
return new OpenCodeRestartPolicy;
|
|
15790
|
+
case "pi":
|
|
15791
|
+
return new PiRestartPolicy;
|
|
15792
|
+
default:
|
|
15793
|
+
return {
|
|
15794
|
+
id: harness,
|
|
15795
|
+
shouldStartAgent: () => false
|
|
15796
|
+
};
|
|
15797
|
+
}
|
|
15798
|
+
}
|
|
15799
|
+
|
|
15800
|
+
// src/commands/machine/daemon-start/task-monitor.ts
|
|
15801
|
+
function canAttemptRestart(chatroomId, role, now) {
|
|
15802
|
+
const key = `${chatroomId}:${role.toLowerCase()}`;
|
|
15803
|
+
const lastAttempt = lastRestartAttempt.get(key) ?? 0;
|
|
15804
|
+
return now - lastAttempt >= RESTART_COOLDOWN_MS;
|
|
15805
|
+
}
|
|
15806
|
+
function recordRestartAttempt(chatroomId, role, now) {
|
|
15807
|
+
const key = `${chatroomId}:${role.toLowerCase()}`;
|
|
15808
|
+
lastRestartAttempt.set(key, now);
|
|
15809
|
+
}
|
|
15810
|
+
function startTaskMonitor(ctx) {
|
|
15811
|
+
let unsubscribe = null;
|
|
15812
|
+
let isRunning = true;
|
|
15813
|
+
const startMonitoring = async () => {
|
|
15814
|
+
try {
|
|
15815
|
+
const wsClient2 = await getConvexWsClient();
|
|
15816
|
+
const agentEndContext = {
|
|
15817
|
+
agentEndedTurn: ctx.agentEndedTurn
|
|
15818
|
+
};
|
|
15819
|
+
unsubscribe = wsClient2.onUpdate(api.machines.getAssignedTasks, {
|
|
15820
|
+
sessionId: ctx.sessionId,
|
|
15821
|
+
machineId: ctx.machineId
|
|
15822
|
+
}, async (result) => {
|
|
15823
|
+
if (!result?.tasks || result.tasks.length === 0)
|
|
15824
|
+
return;
|
|
15825
|
+
const now = Date.now();
|
|
15826
|
+
for (const taskInfo of result.tasks) {
|
|
15827
|
+
const { task, agentConfig } = {
|
|
15828
|
+
task: {
|
|
15829
|
+
taskId: taskInfo.taskId,
|
|
15830
|
+
chatroomId: taskInfo.chatroomId,
|
|
15831
|
+
status: taskInfo.status,
|
|
15832
|
+
assignedTo: taskInfo.assignedTo,
|
|
15833
|
+
updatedAt: taskInfo.updatedAt,
|
|
15834
|
+
createdAt: taskInfo.createdAt
|
|
15835
|
+
},
|
|
15836
|
+
agentConfig: taskInfo.agentConfig
|
|
15837
|
+
};
|
|
15838
|
+
const policy = getRestartPolicyForHarness(agentConfig.agentHarness);
|
|
15839
|
+
const shouldStart = policy.shouldStartAgent({ task, agentConfig }, agentEndContext);
|
|
15840
|
+
if (!shouldStart)
|
|
15841
|
+
continue;
|
|
15842
|
+
if (!canAttemptRestart(task.chatroomId, agentConfig.role, now)) {
|
|
15843
|
+
continue;
|
|
15844
|
+
}
|
|
15845
|
+
if (!agentConfig.workingDir) {
|
|
15846
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Missing workingDir for ${task.chatroomId}/${agentConfig.role}` + ` — skipping`);
|
|
15847
|
+
continue;
|
|
15848
|
+
}
|
|
15849
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCE1 Task monitor: starting agent for ` + `${task.chatroomId}/${agentConfig.role} (harness: ${agentConfig.agentHarness})`);
|
|
15850
|
+
recordRestartAttempt(task.chatroomId, agentConfig.role, now);
|
|
15851
|
+
try {
|
|
15852
|
+
await executeStartAgent(ctx, {
|
|
15853
|
+
chatroomId: task.chatroomId,
|
|
15854
|
+
role: agentConfig.role,
|
|
15855
|
+
agentHarness: agentConfig.agentHarness,
|
|
15856
|
+
model: agentConfig.model,
|
|
15857
|
+
workingDir: agentConfig.workingDir,
|
|
15858
|
+
reason: "daemon.task_monitor"
|
|
15859
|
+
});
|
|
15860
|
+
} catch (err) {
|
|
15861
|
+
console.error(`[${formatTimestamp()}] ❌ Task monitor failed to start agent ` + `for ${task.chatroomId}/${agentConfig.role}: ${err.message}`);
|
|
15862
|
+
}
|
|
15863
|
+
}
|
|
15864
|
+
});
|
|
15865
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD0D Task monitor started`);
|
|
15866
|
+
} catch (err) {
|
|
15867
|
+
if (isRunning) {
|
|
15868
|
+
console.error(`[${formatTimestamp()}] ❌ Task monitor error: ${err.message}`);
|
|
15869
|
+
setTimeout(startMonitoring, 5000);
|
|
15870
|
+
}
|
|
15871
|
+
}
|
|
15872
|
+
};
|
|
15873
|
+
startMonitoring();
|
|
15874
|
+
return {
|
|
15875
|
+
stop: () => {
|
|
15876
|
+
isRunning = false;
|
|
15877
|
+
if (unsubscribe) {
|
|
15878
|
+
unsubscribe();
|
|
15879
|
+
unsubscribe = null;
|
|
15880
|
+
}
|
|
15881
|
+
}
|
|
15882
|
+
};
|
|
15883
|
+
}
|
|
15884
|
+
var RESTART_COOLDOWN_MS = 60000, lastRestartAttempt;
|
|
15885
|
+
var init_task_monitor = __esm(() => {
|
|
15886
|
+
init_api3();
|
|
15887
|
+
init_client2();
|
|
15888
|
+
init_start_agent();
|
|
15889
|
+
lastRestartAttempt = new Map;
|
|
15890
|
+
});
|
|
15891
|
+
|
|
15732
15892
|
// src/commands/machine/daemon-start/command-loop.ts
|
|
15733
15893
|
async function refreshModels(ctx) {
|
|
15734
15894
|
if (!ctx.config)
|
|
@@ -15816,12 +15976,14 @@ async function startCommandLoop(ctx) {
|
|
|
15816
15976
|
}, DAEMON_HEARTBEAT_INTERVAL_MS);
|
|
15817
15977
|
heartbeatTimer.unref();
|
|
15818
15978
|
const gitPollingHandle = startGitPollingLoop(ctx);
|
|
15979
|
+
const taskMonitorHandle = startTaskMonitor(ctx);
|
|
15819
15980
|
pushGitState(ctx).catch(() => {});
|
|
15820
15981
|
const shutdown = async () => {
|
|
15821
15982
|
console.log(`
|
|
15822
15983
|
[${formatTimestamp()}] Shutting down...`);
|
|
15823
15984
|
clearInterval(heartbeatTimer);
|
|
15824
15985
|
gitPollingHandle.stop();
|
|
15986
|
+
taskMonitorHandle.stop();
|
|
15825
15987
|
await onDaemonShutdown(ctx);
|
|
15826
15988
|
releaseLock();
|
|
15827
15989
|
process.exit(0);
|
|
@@ -15873,6 +16035,7 @@ var init_command_loop = __esm(() => {
|
|
|
15873
16035
|
init_pid();
|
|
15874
16036
|
init_git_polling();
|
|
15875
16037
|
init_git_heartbeat();
|
|
16038
|
+
init_task_monitor();
|
|
15876
16039
|
MODEL_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
15877
16040
|
});
|
|
15878
16041
|
|