@agentmeshhq/agent 0.4.15 → 0.4.18
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/__tests__/bootstrap.test.js +24 -0
- package/dist/__tests__/bootstrap.test.js.map +1 -1
- package/dist/__tests__/injection-verify.test.d.ts +1 -0
- package/dist/__tests__/injection-verify.test.js +93 -0
- package/dist/__tests__/injection-verify.test.js.map +1 -0
- package/dist/__tests__/injector.test.js +115 -1
- package/dist/__tests__/injector.test.js.map +1 -1
- package/dist/__tests__/lead-loop.test.d.ts +1 -0
- package/dist/__tests__/lead-loop.test.js +170 -0
- package/dist/__tests__/lead-loop.test.js.map +1 -0
- package/dist/__tests__/relay.test.d.ts +1 -0
- package/dist/__tests__/relay.test.js +17 -0
- package/dist/__tests__/relay.test.js.map +1 -0
- package/dist/__tests__/roles.test.d.ts +1 -0
- package/dist/__tests__/roles.test.js +78 -0
- package/dist/__tests__/roles.test.js.map +1 -0
- package/dist/__tests__/runner.test.js +17 -0
- package/dist/__tests__/runner.test.js.map +1 -1
- package/dist/__tests__/session-recovery.test.js +214 -11
- package/dist/__tests__/session-recovery.test.js.map +1 -1
- package/dist/__tests__/start-team-id.test.js +8 -3
- package/dist/__tests__/start-team-id.test.js.map +1 -1
- package/dist/__tests__/startup-diagnostics.test.d.ts +1 -0
- package/dist/__tests__/startup-diagnostics.test.js +250 -0
- package/dist/__tests__/startup-diagnostics.test.js.map +1 -0
- package/dist/__tests__/tmux-runtime.test.js +13 -0
- package/dist/__tests__/tmux-runtime.test.js.map +1 -1
- package/dist/__tests__/watcher-queue.test.d.ts +1 -0
- package/dist/__tests__/watcher-queue.test.js +85 -0
- package/dist/__tests__/watcher-queue.test.js.map +1 -0
- package/dist/__tests__/watcher-state.test.d.ts +1 -0
- package/dist/__tests__/watcher-state.test.js +159 -0
- package/dist/__tests__/watcher-state.test.js.map +1 -0
- package/dist/cli/commands.d.ts +32 -0
- package/dist/cli/commands.js +165 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.js +95 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/relay.d.ts +1 -0
- package/dist/cli/relay.js +16 -2
- package/dist/cli/relay.js.map +1 -1
- package/dist/cli/start.d.ts +2 -0
- package/dist/cli/start.js +42 -7
- package/dist/cli/start.js.map +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/core/chat-output-parser.d.ts +24 -0
- package/dist/core/chat-output-parser.js +134 -0
- package/dist/core/chat-output-parser.js.map +1 -0
- package/dist/core/chat-output-parser.test.d.ts +7 -0
- package/dist/core/chat-output-parser.test.js +130 -0
- package/dist/core/chat-output-parser.test.js.map +1 -0
- package/dist/core/daemon/bootstrap.js +14 -4
- package/dist/core/daemon/bootstrap.js.map +1 -1
- package/dist/core/daemon/context-template.d.ts +5 -1
- package/dist/core/daemon/context-template.js +16 -2
- package/dist/core/daemon/context-template.js.map +1 -1
- package/dist/core/daemon/crash-log.js +5 -0
- package/dist/core/daemon/crash-log.js.map +1 -1
- package/dist/core/daemon/injection-verify.d.ts +25 -0
- package/dist/core/daemon/injection-verify.js +94 -0
- package/dist/core/daemon/injection-verify.js.map +1 -0
- package/dist/core/daemon/lead-loop.d.ts +22 -0
- package/dist/core/daemon/lead-loop.js +155 -0
- package/dist/core/daemon/lead-loop.js.map +1 -0
- package/dist/core/daemon/roles.d.ts +25 -0
- package/dist/core/daemon/roles.js +46 -0
- package/dist/core/daemon/roles.js.map +1 -0
- package/dist/core/daemon/session-recovery.d.ts +18 -1
- package/dist/core/daemon/session-recovery.js +89 -5
- package/dist/core/daemon/session-recovery.js.map +1 -1
- package/dist/core/daemon/startup-diagnostics.d.ts +76 -0
- package/dist/core/daemon/startup-diagnostics.js +277 -0
- package/dist/core/daemon/startup-diagnostics.js.map +1 -0
- package/dist/core/daemon/watcher-loop.d.ts +27 -0
- package/dist/core/daemon/watcher-loop.js +134 -0
- package/dist/core/daemon/watcher-loop.js.map +1 -0
- package/dist/core/daemon/watcher-queue.d.ts +33 -0
- package/dist/core/daemon/watcher-queue.js +71 -0
- package/dist/core/daemon/watcher-queue.js.map +1 -0
- package/dist/core/daemon/watcher-state.d.ts +66 -0
- package/dist/core/daemon/watcher-state.js +151 -0
- package/dist/core/daemon/watcher-state.js.map +1 -0
- package/dist/core/daemon.d.ts +14 -0
- package/dist/core/daemon.js +182 -3
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/injector.js +198 -1
- package/dist/core/injector.js.map +1 -1
- package/dist/core/registry.d.ts +5 -0
- package/dist/core/registry.js +19 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/runner/build.js +7 -31
- package/dist/core/runner/build.js.map +1 -1
- package/dist/core/runner/detect.js +2 -8
- package/dist/core/runner/detect.js.map +1 -1
- package/dist/core/runner/index.d.ts +3 -1
- package/dist/core/runner/index.js +2 -0
- package/dist/core/runner/index.js.map +1 -1
- package/dist/core/runner/kimi-models.d.ts +4 -0
- package/dist/core/runner/kimi-models.js +24 -0
- package/dist/core/runner/kimi-models.js.map +1 -0
- package/dist/core/runner/registry.d.ts +3 -0
- package/dist/core/runner/registry.js +75 -0
- package/dist/core/runner/registry.js.map +1 -0
- package/dist/core/runner/types.d.ts +17 -1
- package/dist/core/tmux-runtime.d.ts +1 -1
- package/dist/core/tmux-runtime.js +13 -0
- package/dist/core/tmux-runtime.js.map +1 -1
- package/dist/core/tmux.d.ts +4 -0
- package/dist/core/tmux.js +49 -11
- package/dist/core/tmux.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches project state from hub APIs and formats it for LLM injection.
|
|
3
|
+
*
|
|
4
|
+
* Each tick sends a full snapshot — the LLM reasons from a single tick
|
|
5
|
+
* without needing conversation history.
|
|
6
|
+
*
|
|
7
|
+
* Epic #883
|
|
8
|
+
*/
|
|
9
|
+
async function fetchJson(url, token) {
|
|
10
|
+
const res = await fetch(url, {
|
|
11
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
12
|
+
});
|
|
13
|
+
if (!res.ok) {
|
|
14
|
+
throw new Error(`${url} returned ${res.status}`);
|
|
15
|
+
}
|
|
16
|
+
return res.json();
|
|
17
|
+
}
|
|
18
|
+
export async function fetchWatcherState(ctx) {
|
|
19
|
+
const base = `${ctx.hubUrl}/api/v1/workspaces/${ctx.workspace}`;
|
|
20
|
+
const [handoffsRes, claimsRes, blockersRes, agentsRes, membersRes] = await Promise.all([
|
|
21
|
+
fetchJson(`${base}/handoffs?limit=50`, ctx.token).catch(() => ({
|
|
22
|
+
data: [],
|
|
23
|
+
})),
|
|
24
|
+
fetchJson(`${base}/claims`, ctx.token).catch(() => ({
|
|
25
|
+
data: [],
|
|
26
|
+
})),
|
|
27
|
+
fetchJson(`${base}/blockers`, ctx.token).catch(() => ({
|
|
28
|
+
data: [],
|
|
29
|
+
})),
|
|
30
|
+
fetchJson(`${base}/agents`, ctx.token).catch(() => ({
|
|
31
|
+
data: [],
|
|
32
|
+
})),
|
|
33
|
+
fetchJson(`${ctx.hubUrl}/api/v1/workspaces/${ctx.workspace}/teams/${ctx.teamId}/members`, ctx.token).catch(() => ({ data: [] })),
|
|
34
|
+
]);
|
|
35
|
+
return {
|
|
36
|
+
fetchedAt: new Date().toISOString(),
|
|
37
|
+
handoffs: handoffsRes.data,
|
|
38
|
+
claims: claimsRes.data.filter((c) => c.status === "active"),
|
|
39
|
+
blockers: blockersRes.data.filter((b) => b.status === "open"),
|
|
40
|
+
agents: agentsRes.data,
|
|
41
|
+
teamMembers: membersRes.data,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function timeSince(iso) {
|
|
45
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
46
|
+
const seconds = Math.floor(ms / 1000);
|
|
47
|
+
if (seconds < 60)
|
|
48
|
+
return `${seconds}s`;
|
|
49
|
+
if (seconds < 3600)
|
|
50
|
+
return `${Math.floor(seconds / 60)}m`;
|
|
51
|
+
if (seconds < 86400)
|
|
52
|
+
return `${Math.floor(seconds / 3600)}h`;
|
|
53
|
+
return `${Math.floor(seconds / 86400)}d`;
|
|
54
|
+
}
|
|
55
|
+
function agentHealth(agent) {
|
|
56
|
+
if (!agent.last_heartbeat_at)
|
|
57
|
+
return "unknown";
|
|
58
|
+
const ago = (Date.now() - new Date(agent.last_heartbeat_at).getTime()) / 1000;
|
|
59
|
+
if (ago < 15)
|
|
60
|
+
return "online";
|
|
61
|
+
if (ago < 45)
|
|
62
|
+
return "idle";
|
|
63
|
+
if (ago < 120)
|
|
64
|
+
return "stale";
|
|
65
|
+
return "offline";
|
|
66
|
+
}
|
|
67
|
+
export function formatStateForLlm(state, tickNumber, triggerReasons) {
|
|
68
|
+
const lines = [];
|
|
69
|
+
lines.push(`[AgentMesh Watcher] === TICK #${tickNumber} === ${state.fetchedAt}`);
|
|
70
|
+
lines.push(`Triggered by: ${triggerReasons.join(" + ")}`);
|
|
71
|
+
lines.push("");
|
|
72
|
+
// Build agent ID → display name lookup
|
|
73
|
+
const agentNames = new Map();
|
|
74
|
+
for (const a of state.agents) {
|
|
75
|
+
agentNames.set(a.agent_id, a.display_name);
|
|
76
|
+
}
|
|
77
|
+
const resolveName = (id) => {
|
|
78
|
+
if (!id)
|
|
79
|
+
return "-";
|
|
80
|
+
return agentNames.get(id) ?? id.slice(0, 12);
|
|
81
|
+
};
|
|
82
|
+
// Handoffs — only non-completed
|
|
83
|
+
const activeHandoffs = state.handoffs.filter((h) => h.status !== "completed");
|
|
84
|
+
lines.push(`## Handoffs (${activeHandoffs.length} active)`);
|
|
85
|
+
if (activeHandoffs.length === 0) {
|
|
86
|
+
lines.push("No active handoffs.");
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lines.push("| ID | From | To | Status | Age |");
|
|
90
|
+
lines.push("|---|---|---|---|---|");
|
|
91
|
+
for (const h of activeHandoffs.slice(0, 20)) {
|
|
92
|
+
const age = h.created_at ? timeSince(h.created_at) : "?";
|
|
93
|
+
lines.push(`| ${h.handoff_id.slice(0, 12)} | ${resolveName(h.from_agent_id)} | ${resolveName(h.to_agent_id)} | ${h.status} | ${age} |`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
lines.push("");
|
|
97
|
+
// Agents
|
|
98
|
+
lines.push(`## Agents (${state.agents.length})`);
|
|
99
|
+
if (state.agents.length > 0) {
|
|
100
|
+
lines.push("| Name | Status | Health | Last Seen |");
|
|
101
|
+
lines.push("|---|---|---|---|");
|
|
102
|
+
for (const a of state.agents.slice(0, 20)) {
|
|
103
|
+
const health = agentHealth(a);
|
|
104
|
+
const lastSeen = a.last_heartbeat_at ? timeSince(a.last_heartbeat_at) : "never";
|
|
105
|
+
lines.push(`| ${a.display_name} | ${a.status} | ${health} | ${lastSeen} ago |`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
lines.push("");
|
|
109
|
+
// Team members
|
|
110
|
+
if (state.teamMembers.length > 0) {
|
|
111
|
+
lines.push(`## Team Members (${state.teamMembers.length})`);
|
|
112
|
+
for (const m of state.teamMembers) {
|
|
113
|
+
lines.push(`- ${m.display_name} (${m.role})`);
|
|
114
|
+
}
|
|
115
|
+
lines.push("");
|
|
116
|
+
}
|
|
117
|
+
// Blockers
|
|
118
|
+
lines.push(`## Blockers (${state.blockers.length} open)`);
|
|
119
|
+
if (state.blockers.length === 0) {
|
|
120
|
+
lines.push("No open blockers.");
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
for (const b of state.blockers.slice(0, 10)) {
|
|
124
|
+
const age = b.created_at ? timeSince(b.created_at) : "?";
|
|
125
|
+
lines.push(`- ${b.blocker_id.slice(0, 12)}: "${b.description.slice(0, 80)}" (${b.category}, ${age}, agent: ${resolveName(b.agent_id)})`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push("");
|
|
129
|
+
// Claims
|
|
130
|
+
lines.push(`## Claims (${state.claims.length} active)`);
|
|
131
|
+
if (state.claims.length === 0) {
|
|
132
|
+
lines.push("No active claims.");
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
for (const c of state.claims.slice(0, 10)) {
|
|
136
|
+
lines.push(`- ${resolveName(c.agent_id)}: ${c.paths.join(", ")} (${c.scope})`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
lines.push("");
|
|
140
|
+
// Available actions
|
|
141
|
+
lines.push("## Available Actions");
|
|
142
|
+
lines.push('- `agentmesh nudge <agent-name> "<message>"` — nudge an agent');
|
|
143
|
+
lines.push("- `agentmesh handoff reassign <handoff-id> --to <agent-id>` — reassign a handoff");
|
|
144
|
+
lines.push("- `agentmesh handoff complete <handoff-id>` — mark handoff done");
|
|
145
|
+
lines.push('- `agentmesh blocker create "<description>"` — create a blocker');
|
|
146
|
+
lines.push("- `agentmesh claims release <claim-id>` — release a stale claim");
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push("Analyze the above state and take any necessary actions. When done, wait for the next tick.");
|
|
149
|
+
return lines.join("\n");
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=watcher-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher-state.js","sourceRoot":"","sources":["../../../src/core/daemon/watcher-state.ts"],"names":[],"mappings":"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"}
|
package/dist/core/daemon.d.ts
CHANGED
|
@@ -62,6 +62,11 @@ export declare class AgentDaemon {
|
|
|
62
62
|
private autonomous;
|
|
63
63
|
private healthCheckInterval;
|
|
64
64
|
private inboxPollInterval;
|
|
65
|
+
private commandPollInterval;
|
|
66
|
+
private leadInterval;
|
|
67
|
+
private leadContext;
|
|
68
|
+
private watcherLoopHandle;
|
|
69
|
+
private watcherQueue;
|
|
65
70
|
private stopCleanupScheduler;
|
|
66
71
|
private authHealthWatcher;
|
|
67
72
|
private _preStartSessionId;
|
|
@@ -69,12 +74,14 @@ export declare class AgentDaemon {
|
|
|
69
74
|
private stuckSince;
|
|
70
75
|
private lastPendingHandoffAlertAt;
|
|
71
76
|
private remoteAutomationPaused;
|
|
77
|
+
private rateLimitDetected;
|
|
72
78
|
private lastAutonomyPolicyFetchAt;
|
|
73
79
|
private teamId;
|
|
74
80
|
private pendingClaimCreations;
|
|
75
81
|
private sessionRecoveryAttempts;
|
|
76
82
|
private lastSessionRecoveryAt;
|
|
77
83
|
private initialInboxCheckComplete;
|
|
84
|
+
private commandPollInFlight;
|
|
78
85
|
constructor(options: DaemonOptions);
|
|
79
86
|
start(): Promise<void>;
|
|
80
87
|
/**
|
|
@@ -82,6 +89,11 @@ export declare class AgentDaemon {
|
|
|
82
89
|
* Includes auto-restart logic and progress watchdog
|
|
83
90
|
*/
|
|
84
91
|
private autoAcceptPendingHandoffs;
|
|
92
|
+
/**
|
|
93
|
+
* GH-887: After accepting a handoff, verify the LLM acknowledged it.
|
|
94
|
+
* If rate-limited, pause auto-acceptance. If stuck after retries, log warning.
|
|
95
|
+
*/
|
|
96
|
+
private verifyHandoffInjection;
|
|
85
97
|
private autoAcceptHandoffFromEvent;
|
|
86
98
|
private isAutomationPaused;
|
|
87
99
|
private sweepInboxOnWebSocketConnect;
|
|
@@ -95,6 +107,8 @@ export declare class AgentDaemon {
|
|
|
95
107
|
private releaseAllAutoClaims;
|
|
96
108
|
private reconcileAutoClaims;
|
|
97
109
|
private startHealthMonitor;
|
|
110
|
+
private startCommandPoller;
|
|
111
|
+
private pollNextCommand;
|
|
98
112
|
/**
|
|
99
113
|
* Handles session death - logs crash and attempts auto-restart
|
|
100
114
|
*/
|
package/dist/core/daemon.js
CHANGED
|
@@ -13,15 +13,20 @@ import { formatCrashLog } from "./daemon/crash-log.js";
|
|
|
13
13
|
import { evaluateRestartState, filterActiveClaimsForAgent, filterCompletedHandoffsForAgent, formatRestartLifecycleLog, } from "./daemon/done-state-guard.js";
|
|
14
14
|
import { cleanupGitAuth, setupGitAuth } from "./daemon/git-auth.js";
|
|
15
15
|
import { getStuckDetail } from "./daemon/health-policy.js";
|
|
16
|
+
import { verifyInjection } from "./daemon/injection-verify.js";
|
|
17
|
+
import { runLeadTick } from "./daemon/lead-loop.js";
|
|
18
|
+
import { isKnownRole, ROLE_DAEMON_BEHAVIOUR, VALID_ROLES } from "./daemon/roles.js";
|
|
16
19
|
import { writeSandboxOpencodeConfig } from "./daemon/sandbox-config.js";
|
|
17
20
|
import { isRecoverableSessionFailure } from "./daemon/session-recovery.js";
|
|
18
21
|
import { captureAgentChildPids, persistRunningState } from "./daemon/state.js";
|
|
19
22
|
import { startTmuxRuntimeSession } from "./daemon/tmux-session.js";
|
|
23
|
+
import { handleWatcherWebSocketEvent, startWatcherLoop, } from "./daemon/watcher-loop.js";
|
|
24
|
+
import { WatcherTickQueue } from "./daemon/watcher-queue.js";
|
|
20
25
|
import { configureGitIdentity, setupWorkspace, updateWorkspaceFromRemote, validatePushAccess, } from "./daemon/workspace.js";
|
|
21
26
|
import { findPendingHandoffBreaches } from "./handoff-sla.js";
|
|
22
27
|
import { Heartbeat } from "./heartbeat.js";
|
|
23
28
|
import { handleWebSocketEvent, injectInboxItems, injectOnboardMessage, injectRestoredContext, injectStartupMessage, } from "./injector.js";
|
|
24
|
-
import { checkInbox, createClaim, createSelfAssignment, fetchAssignments, fetchHandoffsForAgent, fetchOnboard, fetchProjectByCode, getAgentAutonomyState, getHandoff, listClaims, registerAgent, releaseClaim, updateHandoffStatusWithRetry, } from "./registry.js";
|
|
29
|
+
import { checkInbox, createClaim, createSelfAssignment, fetchAssignments, fetchHandoffsForAgent, fetchOnboard, fetchProjectByCode, getAgentAutonomyState, getHandoff, listClaims, patchAgentMetadata, registerAgent, releaseClaim, updateHandoffStatusWithRetry, } from "./registry.js";
|
|
25
30
|
import { getRunnerDisplayName } from "./runner.js";
|
|
26
31
|
import { DockerSandbox } from "./sandbox.js";
|
|
27
32
|
import { getLatestSessionId, waitForNewSessionId } from "./session-id.js";
|
|
@@ -72,6 +77,11 @@ export class AgentDaemon {
|
|
|
72
77
|
autonomous;
|
|
73
78
|
healthCheckInterval = null;
|
|
74
79
|
inboxPollInterval = null;
|
|
80
|
+
commandPollInterval = null;
|
|
81
|
+
leadInterval = null;
|
|
82
|
+
leadContext = null;
|
|
83
|
+
watcherLoopHandle = null;
|
|
84
|
+
watcherQueue = null;
|
|
75
85
|
stopCleanupScheduler = null;
|
|
76
86
|
authHealthWatcher = null;
|
|
77
87
|
// Session resume tracking
|
|
@@ -81,12 +91,14 @@ export class AgentDaemon {
|
|
|
81
91
|
stuckSince = null;
|
|
82
92
|
lastPendingHandoffAlertAt = null;
|
|
83
93
|
remoteAutomationPaused = false;
|
|
94
|
+
rateLimitDetected = false;
|
|
84
95
|
lastAutonomyPolicyFetchAt = null;
|
|
85
96
|
teamId;
|
|
86
97
|
pendingClaimCreations = new Set();
|
|
87
98
|
sessionRecoveryAttempts = 0;
|
|
88
99
|
lastSessionRecoveryAt = null;
|
|
89
100
|
initialInboxCheckComplete = false;
|
|
101
|
+
commandPollInFlight = false;
|
|
90
102
|
constructor(options) {
|
|
91
103
|
const boot = bootstrapDaemon(options);
|
|
92
104
|
this.config = boot.config;
|
|
@@ -107,6 +119,18 @@ export class AgentDaemon {
|
|
|
107
119
|
this.autonomous = options.autonomous ?? false;
|
|
108
120
|
this.teamId = options.teamId || boot.agentConfig.teamId;
|
|
109
121
|
this.runnerConfig = boot.runnerConfig;
|
|
122
|
+
// Fail fast for unknown predefined roles — free-form strings are fine
|
|
123
|
+
if (this.projectRole && this.projectRole !== "dev" && isKnownRole(this.projectRole)) {
|
|
124
|
+
// Valid predefined role — log it
|
|
125
|
+
console.log(`Role: ${this.projectRole} (${ROLE_DAEMON_BEHAVIOUR[this.projectRole]})`);
|
|
126
|
+
}
|
|
127
|
+
else if (this.projectRole &&
|
|
128
|
+
this.projectRole !== "dev" &&
|
|
129
|
+
!isKnownRole(this.projectRole) &&
|
|
130
|
+
options.role === this.projectRole) {
|
|
131
|
+
// Explicitly passed unknown role — warn but don't fail (may be free-form auto-assignment role)
|
|
132
|
+
console.log(`Role: ${this.projectRole} (free-form; predefined roles: ${VALID_ROLES.join(", ")})`);
|
|
133
|
+
}
|
|
110
134
|
const runnerName = getRunnerDisplayName(this.runnerConfig.type);
|
|
111
135
|
console.log(`Runner: ${runnerName}`);
|
|
112
136
|
console.log(`Effective model: ${this.runnerConfig.model}`);
|
|
@@ -162,6 +186,12 @@ export class AgentDaemon {
|
|
|
162
186
|
});
|
|
163
187
|
this.agentId = registration.agentId;
|
|
164
188
|
this.token = registration.token;
|
|
189
|
+
// Best-effort: store role in hub agent metadata (once at startup, GH-829)
|
|
190
|
+
if (this.projectRole && isKnownRole(this.projectRole)) {
|
|
191
|
+
patchAgentMetadata(this.config.hubUrl, this.agentId, this.token, {
|
|
192
|
+
role: this.projectRole,
|
|
193
|
+
}).catch(() => { }); // fire-and-forget; non-fatal
|
|
194
|
+
}
|
|
165
195
|
// Persist the agentId back to config.json so it survives state.json wipes.
|
|
166
196
|
// On next startup, agentConfig.agentId is the fallback when existingState is absent.
|
|
167
197
|
upsertAgentConfig({ ...this.agentConfig, agentId: this.agentId });
|
|
@@ -335,8 +365,14 @@ export class AgentDaemon {
|
|
|
335
365
|
token: newToken,
|
|
336
366
|
onMessage: (event) => {
|
|
337
367
|
console.log(`[WS] Received event: ${event.type}`);
|
|
368
|
+
if (this.watcherQueue) {
|
|
369
|
+
handleWatcherWebSocketEvent(event, this.watcherQueue);
|
|
370
|
+
}
|
|
338
371
|
this.autoAcceptHandoffFromEvent(event);
|
|
339
|
-
handleWebSocketEvent(this.agentName, event
|
|
372
|
+
handleWebSocketEvent(this.agentName, event, {
|
|
373
|
+
hubUrl: this.config.hubUrl,
|
|
374
|
+
token: this.token ?? undefined,
|
|
375
|
+
});
|
|
340
376
|
},
|
|
341
377
|
onConnect: () => {
|
|
342
378
|
console.log("WebSocket reconnected with new token");
|
|
@@ -362,6 +398,10 @@ export class AgentDaemon {
|
|
|
362
398
|
token: this.token,
|
|
363
399
|
onMessage: (event) => {
|
|
364
400
|
console.log(`[WS] Received event: ${event.type}`);
|
|
401
|
+
// Feed watcher queue before standard handlers (GH-883)
|
|
402
|
+
if (this.watcherQueue) {
|
|
403
|
+
handleWatcherWebSocketEvent(event, this.watcherQueue);
|
|
404
|
+
}
|
|
365
405
|
this.autoAcceptHandoffFromEvent(event);
|
|
366
406
|
handleWebSocketEvent(this.agentName, event, {
|
|
367
407
|
hubUrl: this.config.hubUrl,
|
|
@@ -427,6 +467,7 @@ export class AgentDaemon {
|
|
|
427
467
|
const mdPath = writeClaudeMd({
|
|
428
468
|
workdir: this.agentConfig.workdir,
|
|
429
469
|
onboard: this.onboardData,
|
|
470
|
+
role: this.projectRole,
|
|
430
471
|
options: {
|
|
431
472
|
hubUrl: this.config.hubUrl,
|
|
432
473
|
token: this.token ?? "",
|
|
@@ -497,6 +538,40 @@ export class AgentDaemon {
|
|
|
497
538
|
this.isRunning = true;
|
|
498
539
|
// Start session health monitoring (every 60 seconds)
|
|
499
540
|
this.startHealthMonitor();
|
|
541
|
+
this.startCommandPoller();
|
|
542
|
+
// Start lead coordination loop for lead/coordinator roles (GH-824, GH-829)
|
|
543
|
+
const roleBehaviour = isKnownRole(this.projectRole)
|
|
544
|
+
? ROLE_DAEMON_BEHAVIOUR[this.projectRole]
|
|
545
|
+
: null;
|
|
546
|
+
if (roleBehaviour === "lead-loop" && this.token) {
|
|
547
|
+
this.leadContext = {
|
|
548
|
+
hubUrl: this.config.hubUrl,
|
|
549
|
+
token: this.token,
|
|
550
|
+
workspace: this.config.workspace,
|
|
551
|
+
teamId: this.teamId ?? "",
|
|
552
|
+
log: (msg) => console.log(msg),
|
|
553
|
+
};
|
|
554
|
+
const leadCtx = this.leadContext;
|
|
555
|
+
this.leadInterval = setInterval(() => {
|
|
556
|
+
runLeadTick(leadCtx).catch((err) => {
|
|
557
|
+
console.error(`[lead-loop] tick error: ${err.message}`);
|
|
558
|
+
});
|
|
559
|
+
}, 120_000);
|
|
560
|
+
console.log("[lead-loop] Lead coordination loop started (2-minute tick)");
|
|
561
|
+
}
|
|
562
|
+
// Start LLM watcher loop for watcher role (GH-883)
|
|
563
|
+
if (roleBehaviour === "watcher-loop" && this.token) {
|
|
564
|
+
this.watcherQueue = new WatcherTickQueue();
|
|
565
|
+
this.watcherLoopHandle = startWatcherLoop({
|
|
566
|
+
hubUrl: this.config.hubUrl,
|
|
567
|
+
token: this.token,
|
|
568
|
+
workspace: this.config.workspace,
|
|
569
|
+
teamId: this.teamId ?? "",
|
|
570
|
+
agentName: this.agentName,
|
|
571
|
+
log: (msg) => console.log(msg),
|
|
572
|
+
}, this.watcherQueue);
|
|
573
|
+
console.log("[watcher-loop] LLM watcher loop started (2-minute tick)");
|
|
574
|
+
}
|
|
500
575
|
// Start evicted-agent cleanup scheduler (GH-421)
|
|
501
576
|
if (this.token) {
|
|
502
577
|
this.stopCleanupScheduler = startCleanupScheduler({
|
|
@@ -536,6 +611,11 @@ Nudge agent:
|
|
|
536
611
|
inboxItems.length === 0) {
|
|
537
612
|
return inboxItems;
|
|
538
613
|
}
|
|
614
|
+
// GH-887: Skip auto-accept if rate-limited
|
|
615
|
+
if (this.rateLimitDetected) {
|
|
616
|
+
console.log("[AUTO-ACCEPT] Skipping — agent is rate-limited");
|
|
617
|
+
return inboxItems;
|
|
618
|
+
}
|
|
539
619
|
const accepted = new Set();
|
|
540
620
|
for (const item of inboxItems) {
|
|
541
621
|
if (item.type && item.type !== "handoff") {
|
|
@@ -547,6 +627,8 @@ Nudge agent:
|
|
|
547
627
|
accepted.add(item.id);
|
|
548
628
|
console.log(`[AUTO-ACCEPT] Accepted handoff ${item.id}`);
|
|
549
629
|
await this.ensureClaimForHandoff(item.id, item.scope);
|
|
630
|
+
// GH-887: Verify the LLM actually started processing
|
|
631
|
+
void this.verifyHandoffInjection(item.id, item.scope);
|
|
550
632
|
}
|
|
551
633
|
}
|
|
552
634
|
catch (error) {
|
|
@@ -555,6 +637,34 @@ Nudge agent:
|
|
|
555
637
|
}
|
|
556
638
|
return inboxItems.filter((item) => !accepted.has(item.id));
|
|
557
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* GH-887: After accepting a handoff, verify the LLM acknowledged it.
|
|
642
|
+
* If rate-limited, pause auto-acceptance. If stuck after retries, log warning.
|
|
643
|
+
*/
|
|
644
|
+
async verifyHandoffInjection(handoffId, scope) {
|
|
645
|
+
try {
|
|
646
|
+
const result = await verifyInjection(this.agentName, handoffId, scope);
|
|
647
|
+
if (result.rateLimited) {
|
|
648
|
+
this.rateLimitDetected = true;
|
|
649
|
+
console.warn(`[INJECT-VERIFY] Rate limit detected — pausing auto-accept for ${this.agentName}`);
|
|
650
|
+
// Clear rate limit after 5 minutes (check again then)
|
|
651
|
+
setTimeout(() => {
|
|
652
|
+
this.rateLimitDetected = false;
|
|
653
|
+
console.log("[INJECT-VERIFY] Rate limit cooldown expired — resuming auto-accept");
|
|
654
|
+
}, 300_000);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (result.verified) {
|
|
658
|
+
console.log(`[INJECT-VERIFY] Handoff ${handoffId} verified (${result.attempts} attempt${result.attempts > 1 ? "s" : ""})`);
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
console.warn(`[INJECT-VERIFY] Handoff ${handoffId} NOT verified after ${result.attempts} attempts — LLM may be stuck`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
console.warn(`[INJECT-VERIFY] Error verifying ${handoffId}: ${error.message}`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
558
668
|
autoAcceptHandoffFromEvent(event) {
|
|
559
669
|
if (!this.autoAcceptHandoffs || !this.token) {
|
|
560
670
|
return;
|
|
@@ -868,6 +978,58 @@ Nudge agent:
|
|
|
868
978
|
}, 5 * 60 * 1000); // Poll every 5 minutes
|
|
869
979
|
}
|
|
870
980
|
}
|
|
981
|
+
startCommandPoller() {
|
|
982
|
+
if (!this.token || !this.agentId || !this.isWorkerAgent) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
// Poll every 5s so commands created during WS disconnects are still picked up.
|
|
986
|
+
this.commandPollInterval = setInterval(() => {
|
|
987
|
+
void this.pollNextCommand();
|
|
988
|
+
}, 5000);
|
|
989
|
+
void this.pollNextCommand();
|
|
990
|
+
}
|
|
991
|
+
async pollNextCommand() {
|
|
992
|
+
if (!this.isRunning || this.commandPollInFlight || !this.token || !this.agentId) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
this.commandPollInFlight = true;
|
|
996
|
+
try {
|
|
997
|
+
const params = new URLSearchParams({ target_agent_id: this.agentId });
|
|
998
|
+
const res = await fetch(`${this.config.hubUrl}/api/v1/workspaces/${this.config.workspace}/commands/next?${params.toString()}`, {
|
|
999
|
+
headers: {
|
|
1000
|
+
Authorization: `Bearer ${this.token}`,
|
|
1001
|
+
},
|
|
1002
|
+
});
|
|
1003
|
+
if (!res.ok) {
|
|
1004
|
+
if (res.status !== 404) {
|
|
1005
|
+
const body = await res.text();
|
|
1006
|
+
console.warn(`[command-poller] hub returned ${res.status}: ${body}`);
|
|
1007
|
+
}
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const payload = (await res.json());
|
|
1011
|
+
if (!payload.command) {
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
handleWebSocketEvent(this.agentName, {
|
|
1015
|
+
type: "control.command",
|
|
1016
|
+
command_id: payload.command.command_id,
|
|
1017
|
+
workspace_id: payload.command.workspace_id,
|
|
1018
|
+
target_agent_id: payload.command.target_agent_id,
|
|
1019
|
+
command_type: payload.command.command_type,
|
|
1020
|
+
payload: payload.command.payload ?? {},
|
|
1021
|
+
}, {
|
|
1022
|
+
hubUrl: this.config.hubUrl,
|
|
1023
|
+
token: this.token ?? undefined,
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
catch (err) {
|
|
1027
|
+
console.warn(`[command-poller] error: ${err.message}`);
|
|
1028
|
+
}
|
|
1029
|
+
finally {
|
|
1030
|
+
this.commandPollInFlight = false;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
871
1033
|
/**
|
|
872
1034
|
* Handles session death - logs crash and attempts auto-restart
|
|
873
1035
|
*/
|
|
@@ -923,12 +1085,21 @@ Nudge agent:
|
|
|
923
1085
|
clearInterval(this.inboxPollInterval);
|
|
924
1086
|
this.inboxPollInterval = null;
|
|
925
1087
|
}
|
|
1088
|
+
if (this.commandPollInterval) {
|
|
1089
|
+
clearInterval(this.commandPollInterval);
|
|
1090
|
+
this.commandPollInterval = null;
|
|
1091
|
+
}
|
|
1092
|
+
if (this.watcherLoopHandle) {
|
|
1093
|
+
this.watcherLoopHandle.stop();
|
|
1094
|
+
this.watcherLoopHandle = null;
|
|
1095
|
+
this.watcherQueue = null;
|
|
1096
|
+
}
|
|
926
1097
|
}
|
|
927
1098
|
async tryRecoverSession(reason) {
|
|
928
1099
|
if (!this.isWorkerAgent || this.serveMode || this.sandboxMode) {
|
|
929
1100
|
return false;
|
|
930
1101
|
}
|
|
931
|
-
if (!isRecoverableSessionFailure(reason)) {
|
|
1102
|
+
if (!isRecoverableSessionFailure(reason, this.agentName)) {
|
|
932
1103
|
return false;
|
|
933
1104
|
}
|
|
934
1105
|
const now = Date.now();
|
|
@@ -1026,6 +1197,14 @@ Nudge agent:
|
|
|
1026
1197
|
clearInterval(this.inboxPollInterval);
|
|
1027
1198
|
this.inboxPollInterval = null;
|
|
1028
1199
|
}
|
|
1200
|
+
if (this.commandPollInterval) {
|
|
1201
|
+
clearInterval(this.commandPollInterval);
|
|
1202
|
+
this.commandPollInterval = null;
|
|
1203
|
+
}
|
|
1204
|
+
if (this.leadInterval) {
|
|
1205
|
+
clearInterval(this.leadInterval);
|
|
1206
|
+
this.leadInterval = null;
|
|
1207
|
+
}
|
|
1029
1208
|
// Stop evicted-agent cleanup scheduler
|
|
1030
1209
|
if (this.stopCleanupScheduler) {
|
|
1031
1210
|
this.stopCleanupScheduler();
|