@agentmeshhq/agent 0.4.0 → 0.4.2
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.d.ts +1 -0
- package/dist/__tests__/bootstrap.test.js +39 -0
- package/dist/__tests__/bootstrap.test.js.map +1 -0
- package/dist/__tests__/claims.integration.test.d.ts +1 -0
- package/dist/__tests__/claims.integration.test.js +128 -0
- package/dist/__tests__/claims.integration.test.js.map +1 -0
- package/dist/__tests__/context-template.test.js +15 -0
- package/dist/__tests__/context-template.test.js.map +1 -1
- package/dist/__tests__/handoff-sla.test.d.ts +1 -0
- package/dist/__tests__/handoff-sla.test.js +29 -0
- package/dist/__tests__/handoff-sla.test.js.map +1 -0
- package/dist/__tests__/handoff.integration.test.d.ts +1 -0
- package/dist/__tests__/handoff.integration.test.js +77 -0
- package/dist/__tests__/handoff.integration.test.js.map +1 -0
- package/dist/__tests__/injector.test.js +31 -2
- package/dist/__tests__/injector.test.js.map +1 -1
- package/dist/__tests__/registry.claims.test.d.ts +1 -0
- package/dist/__tests__/registry.claims.test.js +70 -0
- package/dist/__tests__/registry.claims.test.js.map +1 -0
- package/dist/__tests__/registry.retry.test.d.ts +1 -0
- package/dist/__tests__/registry.retry.test.js +33 -0
- package/dist/__tests__/registry.retry.test.js.map +1 -0
- package/dist/__tests__/status.test.d.ts +1 -0
- package/dist/__tests__/status.test.js +55 -0
- package/dist/__tests__/status.test.js.map +1 -0
- package/dist/__tests__/worker.test.d.ts +1 -0
- package/dist/__tests__/worker.test.js +51 -0
- package/dist/__tests__/worker.test.js.map +1 -0
- package/dist/cli/autopilot.d.ts +17 -0
- package/dist/cli/autopilot.js +20 -0
- package/dist/cli/autopilot.js.map +1 -0
- package/dist/cli/claims.d.ts +18 -0
- package/dist/cli/claims.js +133 -0
- package/dist/cli/claims.js.map +1 -0
- package/dist/cli/handoff.d.ts +22 -0
- package/dist/cli/handoff.js +147 -0
- package/dist/cli/handoff.js.map +1 -0
- package/dist/cli/index.js +211 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/start.d.ts +2 -0
- package/dist/cli/start.js +3 -0
- package/dist/cli/start.js.map +1 -1
- package/dist/cli/status.d.ts +14 -1
- package/dist/cli/status.js +46 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/worker.d.ts +2 -0
- package/dist/cli/worker.js +52 -0
- package/dist/cli/worker.js.map +1 -0
- package/dist/config/schema.d.ts +12 -0
- package/dist/core/daemon/bootstrap.d.ts +2 -0
- package/dist/core/daemon/bootstrap.js +10 -1
- package/dist/core/daemon/bootstrap.js.map +1 -1
- package/dist/core/daemon/context-template.d.ts +7 -2
- package/dist/core/daemon/context-template.js +18 -11
- package/dist/core/daemon/context-template.js.map +1 -1
- package/dist/core/daemon/workspace.d.ts +5 -0
- package/dist/core/daemon/workspace.js +47 -7
- package/dist/core/daemon/workspace.js.map +1 -1
- package/dist/core/daemon.d.ts +19 -0
- package/dist/core/daemon.js +286 -20
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/handoff-sla.d.ts +6 -0
- package/dist/core/handoff-sla.js +29 -0
- package/dist/core/handoff-sla.js.map +1 -0
- package/dist/core/injector.js +2 -2
- package/dist/core/injector.js.map +1 -1
- package/dist/core/registry.d.ts +47 -0
- package/dist/core/registry.js +105 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/tmux-runtime.js +5 -2
- package/dist/core/tmux-runtime.js.map +1 -1
- package/package.json +12 -11
- package/LICENSE +0 -21
|
@@ -4,10 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
7
|
-
|
|
8
|
-
// Pure renderer
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
export function generateClaudeMd(onboard) {
|
|
7
|
+
export function generateClaudeMd(onboard, options) {
|
|
11
8
|
const lines = [];
|
|
12
9
|
lines.push("<!-- AUTO-GENERATED by AgentMesh — do not edit manually -->");
|
|
13
10
|
lines.push("");
|
|
@@ -71,12 +68,22 @@ export function generateClaudeMd(onboard) {
|
|
|
71
68
|
// ── Hub API Reference ─────────────────────────────────────────────
|
|
72
69
|
lines.push("## Hub API");
|
|
73
70
|
lines.push("");
|
|
71
|
+
if (options?.hubUrl) {
|
|
72
|
+
lines.push(`- **Hub URL**: \`${options.hubUrl}\``);
|
|
73
|
+
}
|
|
74
|
+
if (options?.token) {
|
|
75
|
+
lines.push(`- **Auth**: \`Authorization: Bearer ${options.token}\``);
|
|
76
|
+
}
|
|
77
|
+
lines.push("");
|
|
74
78
|
const api = onboard.hub_api;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
lines.push(`- **
|
|
79
|
-
lines.push(`- **
|
|
79
|
+
const base = options?.hubUrl ?? "";
|
|
80
|
+
const stripMethod = (s) => s.replace(/^(GET|POST|PUT|PATCH|DELETE)\s+/, "");
|
|
81
|
+
const method = (s) => s.split(" ")[0];
|
|
82
|
+
lines.push(`- **Handoffs**: \`${method(api.handoffs)} ${base}${stripMethod(api.handoffs)}\``);
|
|
83
|
+
lines.push(`- **Claims**: \`${method(api.claims)} ${base}${stripMethod(api.claims)}\``);
|
|
84
|
+
lines.push(`- **Blockers**: \`${method(api.blockers)} ${base}${stripMethod(api.blockers)}\``);
|
|
85
|
+
lines.push(`- **Inbox**: \`${method(api.inbox)} ${base}${stripMethod(api.inbox)}\``);
|
|
86
|
+
lines.push(`- **Heartbeat**: \`${method(api.heartbeat)} ${base}${stripMethod(api.heartbeat)}\``);
|
|
80
87
|
lines.push("");
|
|
81
88
|
// ── Repositories (NO credentials) ─────────────────────────────────
|
|
82
89
|
if (onboard.repos.length > 0) {
|
|
@@ -103,12 +110,12 @@ export function generateClaudeMd(onboard) {
|
|
|
103
110
|
// ---------------------------------------------------------------------------
|
|
104
111
|
// File I/O
|
|
105
112
|
// ---------------------------------------------------------------------------
|
|
106
|
-
export function writeClaudeMd({ workdir, onboard, }) {
|
|
113
|
+
export function writeClaudeMd({ workdir, onboard, options, }) {
|
|
107
114
|
if (!fs.existsSync(workdir)) {
|
|
108
115
|
return null;
|
|
109
116
|
}
|
|
110
117
|
const filePath = path.join(workdir, "CLAUDE.md");
|
|
111
|
-
const content = generateClaudeMd(onboard);
|
|
118
|
+
const content = generateClaudeMd(onboard, options);
|
|
112
119
|
fs.writeFileSync(filePath, content, { encoding: "utf-8" });
|
|
113
120
|
// Ensure CLAUDE.md is in .gitignore
|
|
114
121
|
const gitignorePath = path.join(workdir, ".gitignore");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-template.js","sourceRoot":"","sources":["../../../src/core/daemon/context-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"context-template.js","sourceRoot":"","sources":["../../../src/core/daemon/context-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,11 @@ export interface WorkspaceSetupInput {
|
|
|
4
4
|
defaultBranch: string;
|
|
5
5
|
projectName: string;
|
|
6
6
|
}
|
|
7
|
+
export declare function updateWorkspaceFromRemote(workspacePath: string, defaultBranch: string): {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
autoStashed: boolean;
|
|
10
|
+
stashRef?: string;
|
|
11
|
+
};
|
|
7
12
|
/**
|
|
8
13
|
* Ensures a project workspace exists and is on the target default branch.
|
|
9
14
|
*/
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
function shouldAutoStashOnPullError(message) {
|
|
5
|
+
return (message.includes("would be overwritten by merge") ||
|
|
6
|
+
message.includes("Your local changes to the following files would be overwritten"));
|
|
7
|
+
}
|
|
8
|
+
export function updateWorkspaceFromRemote(workspacePath, defaultBranch) {
|
|
9
|
+
try {
|
|
10
|
+
execSync("git fetch origin", { cwd: workspacePath, stdio: "pipe", timeout: 30_000 });
|
|
11
|
+
execSync(`git checkout ${defaultBranch}`, { cwd: workspacePath, stdio: "pipe", timeout: 30_000 });
|
|
12
|
+
execSync(`git pull origin ${defaultBranch}`, { cwd: workspacePath, stdio: "pipe", timeout: 30_000 });
|
|
13
|
+
return { ok: true, autoStashed: false };
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const message = error instanceof Error
|
|
17
|
+
? [error.message, error.stderr ?? ""].join("\n")
|
|
18
|
+
: String(error);
|
|
19
|
+
if (!shouldAutoStashOnPullError(message)) {
|
|
20
|
+
return { ok: false, autoStashed: false };
|
|
21
|
+
}
|
|
22
|
+
const stashLabel = `agentmesh-autostash-${new Date().toISOString()}`;
|
|
23
|
+
try {
|
|
24
|
+
execSync(`git stash push -u -m "${stashLabel}"`, {
|
|
25
|
+
cwd: workspacePath,
|
|
26
|
+
stdio: "pipe",
|
|
27
|
+
timeout: 30_000,
|
|
28
|
+
});
|
|
29
|
+
execSync(`git pull origin ${defaultBranch}`, {
|
|
30
|
+
cwd: workspacePath,
|
|
31
|
+
stdio: "pipe",
|
|
32
|
+
timeout: 30_000,
|
|
33
|
+
});
|
|
34
|
+
return { ok: true, autoStashed: true, stashRef: stashLabel };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { ok: false, autoStashed: false };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
4
41
|
/**
|
|
5
42
|
* Ensures a project workspace exists and is on the target default branch.
|
|
6
43
|
*/
|
|
@@ -13,14 +50,17 @@ export function setupWorkspace(input) {
|
|
|
13
50
|
if (fs.existsSync(gitDir)) {
|
|
14
51
|
console.log(`✓ Workspace already exists: ${workspacePath}`);
|
|
15
52
|
console.log(" Updating from remote...");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
53
|
+
const update = updateWorkspaceFromRemote(workspacePath, defaultBranch);
|
|
54
|
+
if (update.ok) {
|
|
55
|
+
if (update.autoStashed) {
|
|
56
|
+
console.log(`✓ Workspace updated after auto-stash (${update.stashRef})\n`);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
console.log("✓ Workspace updated successfully\n");
|
|
60
|
+
}
|
|
21
61
|
}
|
|
22
|
-
|
|
23
|
-
console.warn(
|
|
62
|
+
else {
|
|
63
|
+
console.warn("⚠ Could not update workspace from remote");
|
|
24
64
|
console.log(" Continuing with existing state...\n");
|
|
25
65
|
}
|
|
26
66
|
return workspacePath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/core/daemon/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/core/daemon/workspace.ts"],"names":[],"mappings":"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"}
|
package/dist/core/daemon.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface DaemonOptions {
|
|
|
26
26
|
project?: string;
|
|
27
27
|
/** Role for auto-assignment (default: dev) */
|
|
28
28
|
role?: string;
|
|
29
|
+
/** Auto-accept pending handoffs in worker mode (default: enabled for --worker) */
|
|
30
|
+
autoAcceptHandoffs?: boolean;
|
|
29
31
|
}
|
|
30
32
|
export declare class AgentDaemon {
|
|
31
33
|
private agentName;
|
|
@@ -52,17 +54,34 @@ export declare class AgentDaemon {
|
|
|
52
54
|
private sandbox;
|
|
53
55
|
private projectCode;
|
|
54
56
|
private projectRole;
|
|
57
|
+
private autoAcceptHandoffs;
|
|
55
58
|
private healthCheckInterval;
|
|
56
59
|
private _preStartSessionId;
|
|
57
60
|
private _attemptedResumeSessionId;
|
|
58
61
|
private stuckSince;
|
|
59
62
|
private nudgeSentAt;
|
|
63
|
+
private lastPendingHandoffAlertAt;
|
|
64
|
+
private remoteAutomationPaused;
|
|
65
|
+
private lastAutonomyPolicyFetchAt;
|
|
66
|
+
private pendingClaimCreations;
|
|
60
67
|
constructor(options: DaemonOptions);
|
|
61
68
|
start(): Promise<void>;
|
|
62
69
|
/**
|
|
63
70
|
* Starts periodic health monitoring for the tmux session
|
|
64
71
|
* Includes auto-restart logic and progress watchdog
|
|
65
72
|
*/
|
|
73
|
+
private autoAcceptPendingHandoffs;
|
|
74
|
+
private autoAcceptHandoffFromEvent;
|
|
75
|
+
private isAutomationPaused;
|
|
76
|
+
private refreshRemoteAutonomyPolicy;
|
|
77
|
+
private acceptHandoffWithRetry;
|
|
78
|
+
private checkPendingHandoffSla;
|
|
79
|
+
private getAutoClaimScope;
|
|
80
|
+
private getAutoClaimPaths;
|
|
81
|
+
private ensureClaimForHandoff;
|
|
82
|
+
private releaseClaimForHandoff;
|
|
83
|
+
private releaseAllAutoClaims;
|
|
84
|
+
private reconcileAutoClaims;
|
|
66
85
|
private startHealthMonitor;
|
|
67
86
|
/**
|
|
68
87
|
* Handles session death - logs crash and attempts auto-restart
|
package/dist/core/daemon.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
@@ -13,10 +13,11 @@ import { getNudgeMessage, getStuckDetail, isWithinNudgeWaitWindow, } from "./dae
|
|
|
13
13
|
import { writeSandboxOpencodeConfig } from "./daemon/sandbox-config.js";
|
|
14
14
|
import { captureAgentChildPids, persistRunningState } from "./daemon/state.js";
|
|
15
15
|
import { startTmuxRuntimeSession } from "./daemon/tmux-session.js";
|
|
16
|
-
import { configureGitIdentity, setupWorkspace, validatePushAccess } from "./daemon/workspace.js";
|
|
16
|
+
import { configureGitIdentity, setupWorkspace, updateWorkspaceFromRemote, validatePushAccess, } from "./daemon/workspace.js";
|
|
17
|
+
import { findPendingHandoffBreaches } from "./handoff-sla.js";
|
|
17
18
|
import { Heartbeat } from "./heartbeat.js";
|
|
18
19
|
import { handleWebSocketEvent, injectOnboardMessage, injectRestoredContext, injectStartupMessage, } from "./injector.js";
|
|
19
|
-
import { checkInbox, createSelfAssignment, fetchAssignments, fetchOnboard, fetchProjectByCode, registerAgent, } from "./registry.js";
|
|
20
|
+
import { checkInbox, createClaim, createSelfAssignment, fetchAssignments, fetchOnboard, fetchProjectByCode, getAgentAutonomyState, getHandoff, listClaims, registerAgent, releaseClaim, updateHandoffStatusWithRetry, } from "./registry.js";
|
|
20
21
|
import { getRunnerDisplayName } from "./runner.js";
|
|
21
22
|
import { DockerSandbox } from "./sandbox.js";
|
|
22
23
|
import { getLatestSessionId, waitForNewSessionId } from "./session-id.js";
|
|
@@ -26,6 +27,10 @@ import { checkAgentProgress, cleanupOrphanContainers, isProcessRunning, sendNudg
|
|
|
26
27
|
import { AgentWebSocket } from "./websocket.js";
|
|
27
28
|
// Time to wait after nudging before marking as stuck (2 minutes)
|
|
28
29
|
const NUDGE_WAIT_MS = 2 * 60 * 1000;
|
|
30
|
+
const PENDING_HANDOFF_SLA_MINUTES = 5;
|
|
31
|
+
const PENDING_HANDOFF_ALERT_COOLDOWN_MS = 5 * 60 * 1000;
|
|
32
|
+
const AUTO_CLAIM_SCOPE_PREFIX = "handoff:";
|
|
33
|
+
const AUTO_CLAIM_TTL_SECONDS = 1800;
|
|
29
34
|
// Path to the sandbox OpenCode config (permissive permissions)
|
|
30
35
|
const SANDBOX_OPENCODE_CONFIG_PATH = path.join(os.homedir(), ".agentmesh", "opencode-sandbox.json");
|
|
31
36
|
// Sandbox OpenCode config content - allow everything since container is sandboxed
|
|
@@ -58,6 +63,7 @@ export class AgentDaemon {
|
|
|
58
63
|
sandbox = null;
|
|
59
64
|
projectCode;
|
|
60
65
|
projectRole;
|
|
66
|
+
autoAcceptHandoffs;
|
|
61
67
|
healthCheckInterval = null;
|
|
62
68
|
// Session resume tracking
|
|
63
69
|
_preStartSessionId;
|
|
@@ -65,6 +71,10 @@ export class AgentDaemon {
|
|
|
65
71
|
// Stuck detection tracking
|
|
66
72
|
stuckSince = null;
|
|
67
73
|
nudgeSentAt = null;
|
|
74
|
+
lastPendingHandoffAlertAt = null;
|
|
75
|
+
remoteAutomationPaused = false;
|
|
76
|
+
lastAutonomyPolicyFetchAt = null;
|
|
77
|
+
pendingClaimCreations = new Set();
|
|
68
78
|
constructor(options) {
|
|
69
79
|
const boot = bootstrapDaemon(options);
|
|
70
80
|
this.config = boot.config;
|
|
@@ -81,6 +91,7 @@ export class AgentDaemon {
|
|
|
81
91
|
this.sandboxMemory = boot.sandboxMemory;
|
|
82
92
|
this.projectCode = boot.projectCode;
|
|
83
93
|
this.projectRole = boot.projectRole;
|
|
94
|
+
this.autoAcceptHandoffs = boot.autoAcceptHandoffs;
|
|
84
95
|
this.runnerConfig = boot.runnerConfig;
|
|
85
96
|
const runnerName = getRunnerDisplayName(this.runnerConfig.type);
|
|
86
97
|
console.log(`Runner: ${runnerName}`);
|
|
@@ -286,6 +297,7 @@ export class AgentDaemon {
|
|
|
286
297
|
token: newToken,
|
|
287
298
|
onMessage: (event) => {
|
|
288
299
|
console.log(`[WS] Received event: ${event.type}`);
|
|
300
|
+
this.autoAcceptHandoffFromEvent(event);
|
|
289
301
|
handleWebSocketEvent(this.agentName, event);
|
|
290
302
|
},
|
|
291
303
|
onConnect: () => {
|
|
@@ -311,6 +323,7 @@ export class AgentDaemon {
|
|
|
311
323
|
token: this.token,
|
|
312
324
|
onMessage: (event) => {
|
|
313
325
|
console.log(`[WS] Received event: ${event.type}`);
|
|
326
|
+
this.autoAcceptHandoffFromEvent(event);
|
|
314
327
|
handleWebSocketEvent(this.agentName, event, {
|
|
315
328
|
hubUrl: this.config.hubUrl,
|
|
316
329
|
token: this.token ?? undefined,
|
|
@@ -327,11 +340,15 @@ export class AgentDaemon {
|
|
|
327
340
|
},
|
|
328
341
|
});
|
|
329
342
|
this.ws.connect();
|
|
343
|
+
// Wait for TUI to initialize before injecting messages
|
|
344
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
345
|
+
await this.refreshRemoteAutonomyPolicy(true);
|
|
330
346
|
// Check inbox and auto-nudge with full handoff details
|
|
331
347
|
console.log("Checking inbox...");
|
|
332
348
|
try {
|
|
333
349
|
const inboxItems = await checkInbox(this.config.hubUrl, this.config.workspace, this.token);
|
|
334
|
-
|
|
350
|
+
const remainingItems = await this.autoAcceptPendingHandoffs(inboxItems);
|
|
351
|
+
injectStartupMessage(this.agentName, remainingItems.length, remainingItems);
|
|
335
352
|
}
|
|
336
353
|
catch (error) {
|
|
337
354
|
console.error("Failed to check inbox:", error);
|
|
@@ -339,6 +356,7 @@ export class AgentDaemon {
|
|
|
339
356
|
}
|
|
340
357
|
// Inject onboard project context
|
|
341
358
|
if (this.onboardData?.project) {
|
|
359
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
342
360
|
injectOnboardMessage(this.agentName, this.onboardData);
|
|
343
361
|
}
|
|
344
362
|
// Write persistent CLAUDE.md context file
|
|
@@ -346,6 +364,10 @@ export class AgentDaemon {
|
|
|
346
364
|
const mdPath = writeClaudeMd({
|
|
347
365
|
workdir: this.agentConfig.workdir,
|
|
348
366
|
onboard: this.onboardData,
|
|
367
|
+
options: {
|
|
368
|
+
hubUrl: this.config.hubUrl,
|
|
369
|
+
token: this.token ?? "",
|
|
370
|
+
},
|
|
349
371
|
});
|
|
350
372
|
if (mdPath) {
|
|
351
373
|
console.log(`✓ CLAUDE.md written: ${mdPath}`);
|
|
@@ -431,6 +453,250 @@ Nudge agent:
|
|
|
431
453
|
* Starts periodic health monitoring for the tmux session
|
|
432
454
|
* Includes auto-restart logic and progress watchdog
|
|
433
455
|
*/
|
|
456
|
+
async autoAcceptPendingHandoffs(inboxItems) {
|
|
457
|
+
await this.refreshRemoteAutonomyPolicy();
|
|
458
|
+
if (!this.autoAcceptHandoffs ||
|
|
459
|
+
this.isAutomationPaused() ||
|
|
460
|
+
!this.token ||
|
|
461
|
+
inboxItems.length === 0) {
|
|
462
|
+
return inboxItems;
|
|
463
|
+
}
|
|
464
|
+
const accepted = new Set();
|
|
465
|
+
for (const item of inboxItems) {
|
|
466
|
+
if (item.type && item.type !== "handoff") {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
const ok = await this.acceptHandoffWithRetry(item.id);
|
|
471
|
+
if (ok) {
|
|
472
|
+
accepted.add(item.id);
|
|
473
|
+
console.log(`[AUTO-ACCEPT] Accepted handoff ${item.id}`);
|
|
474
|
+
await this.ensureClaimForHandoff(item.id, item.scope);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
catch (error) {
|
|
478
|
+
console.warn(`[AUTO-ACCEPT] Failed to accept handoff ${item.id}: ${error.message}`);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return inboxItems.filter((item) => !accepted.has(item.id));
|
|
482
|
+
}
|
|
483
|
+
autoAcceptHandoffFromEvent(event) {
|
|
484
|
+
if (!this.autoAcceptHandoffs || !this.token) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const eventType = event.type ?? "";
|
|
488
|
+
const acceptsSingleHandoff = eventType === "handoff_received" || eventType === "handoff.received";
|
|
489
|
+
const shouldSweepInbox = eventType === "handoff.created" ||
|
|
490
|
+
eventType === "handoff.updated" ||
|
|
491
|
+
eventType === "handoffs.updated";
|
|
492
|
+
const isClosureEvent = shouldSweepInbox && (event.status === "completed" || event.status === "rejected");
|
|
493
|
+
if (!acceptsSingleHandoff && !shouldSweepInbox) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
void (async () => {
|
|
497
|
+
await this.refreshRemoteAutonomyPolicy();
|
|
498
|
+
if (this.isAutomationPaused()) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
// Legacy event path where handoff id is present directly.
|
|
502
|
+
if (acceptsSingleHandoff) {
|
|
503
|
+
const handoffId = event.handoff_id || event.id;
|
|
504
|
+
if (!handoffId) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
return this.acceptHandoffWithRetry(handoffId)
|
|
508
|
+
.then((ok) => {
|
|
509
|
+
if (ok) {
|
|
510
|
+
console.log(`[AUTO-ACCEPT] Accepted incoming handoff ${handoffId}`);
|
|
511
|
+
}
|
|
512
|
+
})
|
|
513
|
+
.catch((error) => {
|
|
514
|
+
console.warn(`[AUTO-ACCEPT] Failed to accept incoming handoff ${handoffId}: ${error.message}`);
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
// Current Hub broadcasts handoff lifecycle updates via `handoff.created`/`handoffs.updated`.
|
|
518
|
+
// Re-check inbox and accept all pending handoffs assigned to this agent.
|
|
519
|
+
try {
|
|
520
|
+
if (isClosureEvent) {
|
|
521
|
+
const handoffId = event.handoff_id || event.id;
|
|
522
|
+
if (handoffId) {
|
|
523
|
+
await this.releaseClaimForHandoff(handoffId);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const inboxItems = await checkInbox(this.config.hubUrl, this.config.workspace, this.token);
|
|
527
|
+
await this.autoAcceptPendingHandoffs(inboxItems);
|
|
528
|
+
}
|
|
529
|
+
catch (error) {
|
|
530
|
+
console.warn(`[AUTO-ACCEPT] Failed inbox sweep on ${eventType}: ${error.message}`);
|
|
531
|
+
}
|
|
532
|
+
})();
|
|
533
|
+
}
|
|
534
|
+
isAutomationPaused() {
|
|
535
|
+
const state = getAgentState(this.agentName);
|
|
536
|
+
return state?.automationPaused === true || this.remoteAutomationPaused;
|
|
537
|
+
}
|
|
538
|
+
async refreshRemoteAutonomyPolicy(force = false) {
|
|
539
|
+
if (!this.token || !this.agentId) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const now = Date.now();
|
|
543
|
+
if (!force &&
|
|
544
|
+
this.lastAutonomyPolicyFetchAt &&
|
|
545
|
+
now - this.lastAutonomyPolicyFetchAt.getTime() < 15_000) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
this.lastAutonomyPolicyFetchAt = new Date(now);
|
|
549
|
+
const policy = await getAgentAutonomyState(this.config.hubUrl, this.config.workspace, this.agentId, this.token);
|
|
550
|
+
if (policy) {
|
|
551
|
+
this.remoteAutomationPaused = policy.paused;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
async acceptHandoffWithRetry(handoffId) {
|
|
555
|
+
if (!this.token) {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
return updateHandoffStatusWithRetry(this.config.hubUrl, this.config.workspace, handoffId, "accepted", this.token);
|
|
559
|
+
}
|
|
560
|
+
async checkPendingHandoffSla() {
|
|
561
|
+
if (!this.token || !this.isWorkerAgent) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
await this.refreshRemoteAutonomyPolicy();
|
|
566
|
+
if (this.isAutomationPaused()) {
|
|
567
|
+
await this.releaseAllAutoClaims("automation paused");
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const items = await checkInbox(this.config.hubUrl, this.config.workspace, this.token);
|
|
571
|
+
const remainingItems = await this.autoAcceptPendingHandoffs(items);
|
|
572
|
+
await this.reconcileAutoClaims();
|
|
573
|
+
const breaches = findPendingHandoffBreaches(remainingItems, PENDING_HANDOFF_SLA_MINUTES);
|
|
574
|
+
const oldest = breaches[0];
|
|
575
|
+
updateAgentInState(this.agentName, {
|
|
576
|
+
pendingHandoffCheckedAt: new Date().toISOString(),
|
|
577
|
+
pendingHandoffBreachCount: breaches.length,
|
|
578
|
+
pendingHandoffOldestId: oldest?.handoffId,
|
|
579
|
+
pendingHandoffOldestAgeMinutes: oldest?.ageMinutes,
|
|
580
|
+
});
|
|
581
|
+
if (!oldest) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const now = new Date();
|
|
585
|
+
if (this.lastPendingHandoffAlertAt &&
|
|
586
|
+
now.getTime() - this.lastPendingHandoffAlertAt.getTime() < PENDING_HANDOFF_ALERT_COOLDOWN_MS) {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
this.lastPendingHandoffAlertAt = now;
|
|
590
|
+
updateAgentInState(this.agentName, { pendingHandoffAlertAt: now.toISOString() });
|
|
591
|
+
console.warn(`[SLA] ${this.agentName} has pending handoff breach: ${oldest.handoffId} (${oldest.ageMinutes}m)`);
|
|
592
|
+
if (this.isAutomationPaused()) {
|
|
593
|
+
sendNudge(this.agentName, `SLA alert: pending handoff ${oldest.handoffId} is ${oldest.ageMinutes} minutes old. ` +
|
|
594
|
+
"Automation is paused; accept/reject manually or ask PO to resume.");
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
// Non-fatal: health loop should continue.
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
getAutoClaimScope(handoffId) {
|
|
602
|
+
return `${AUTO_CLAIM_SCOPE_PREFIX}${handoffId}`;
|
|
603
|
+
}
|
|
604
|
+
getAutoClaimPaths(handoffId) {
|
|
605
|
+
const workdirBase = this.agentConfig.workdir
|
|
606
|
+
? path.basename(path.resolve(this.agentConfig.workdir))
|
|
607
|
+
: this.agentName;
|
|
608
|
+
return [`agent/${this.agentName}/${workdirBase}/handoff/${handoffId}/**`];
|
|
609
|
+
}
|
|
610
|
+
async ensureClaimForHandoff(handoffId, handoffScope) {
|
|
611
|
+
if (!this.token || !this.agentId) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (this.pendingClaimCreations.has(handoffId)) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
this.pendingClaimCreations.add(handoffId);
|
|
618
|
+
const claimScope = this.getAutoClaimScope(handoffId);
|
|
619
|
+
try {
|
|
620
|
+
const claims = await listClaims(this.config.hubUrl, this.config.workspace, this.token);
|
|
621
|
+
const existing = claims.find((claim) => claim.status === "active" &&
|
|
622
|
+
claim.agent_id === this.agentId &&
|
|
623
|
+
claim.scope === claimScope);
|
|
624
|
+
if (existing) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const result = await createClaim(this.config.hubUrl, this.config.workspace, this.token, {
|
|
628
|
+
scope: claimScope,
|
|
629
|
+
paths: this.getAutoClaimPaths(handoffId),
|
|
630
|
+
ttlSeconds: AUTO_CLAIM_TTL_SECONDS,
|
|
631
|
+
});
|
|
632
|
+
if (result.claimId) {
|
|
633
|
+
console.log(`[AUTO-CLAIM] Created claim ${result.claimId} for handoff ${handoffId}`);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (result.conflict) {
|
|
637
|
+
console.warn(`[AUTO-CLAIM] Conflict while claiming handoff ${handoffId}${handoffScope ? ` (${handoffScope})` : ""}`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
finally {
|
|
641
|
+
this.pendingClaimCreations.delete(handoffId);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
async releaseClaimForHandoff(handoffId) {
|
|
645
|
+
if (!this.token || !this.agentId) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const claimScope = this.getAutoClaimScope(handoffId);
|
|
649
|
+
const claims = await listClaims(this.config.hubUrl, this.config.workspace, this.token);
|
|
650
|
+
const releasable = claims.filter((claim) => claim.status === "active" && claim.agent_id === this.agentId && claim.scope === claimScope);
|
|
651
|
+
for (const claim of releasable) {
|
|
652
|
+
const ok = await releaseClaim(this.config.hubUrl, claim.claim_id, this.token);
|
|
653
|
+
if (ok) {
|
|
654
|
+
console.log(`[AUTO-CLAIM] Released claim ${claim.claim_id} for handoff ${handoffId}`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async releaseAllAutoClaims(reason) {
|
|
659
|
+
if (!this.token || !this.agentId) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const claims = await listClaims(this.config.hubUrl, this.config.workspace, this.token);
|
|
663
|
+
const releasable = claims.filter((claim) => claim.status === "active" &&
|
|
664
|
+
claim.agent_id === this.agentId &&
|
|
665
|
+
claim.scope.startsWith(AUTO_CLAIM_SCOPE_PREFIX));
|
|
666
|
+
for (const claim of releasable) {
|
|
667
|
+
const ok = await releaseClaim(this.config.hubUrl, claim.claim_id, this.token);
|
|
668
|
+
if (ok) {
|
|
669
|
+
console.log(`[AUTO-CLAIM] Released claim ${claim.claim_id} (${reason})`);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async reconcileAutoClaims() {
|
|
674
|
+
if (!this.token || !this.agentId) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const claims = await listClaims(this.config.hubUrl, this.config.workspace, this.token);
|
|
678
|
+
const activeAutoClaims = claims.filter((claim) => claim.status === "active" &&
|
|
679
|
+
claim.agent_id === this.agentId &&
|
|
680
|
+
claim.scope.startsWith(AUTO_CLAIM_SCOPE_PREFIX));
|
|
681
|
+
for (const claim of activeAutoClaims) {
|
|
682
|
+
const handoffId = claim.scope.slice(AUTO_CLAIM_SCOPE_PREFIX.length);
|
|
683
|
+
if (!handoffId) {
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
const handoff = await getHandoff(this.config.hubUrl, this.config.workspace, handoffId, this.token);
|
|
687
|
+
const shouldRelease = !handoff ||
|
|
688
|
+
handoff.status === "completed" ||
|
|
689
|
+
handoff.status === "rejected" ||
|
|
690
|
+
(handoff.to_agent_id && handoff.to_agent_id !== this.agentId);
|
|
691
|
+
if (!shouldRelease) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const ok = await releaseClaim(this.config.hubUrl, claim.claim_id, this.token);
|
|
695
|
+
if (ok) {
|
|
696
|
+
console.log(`[AUTO-CLAIM] Released stale claim ${claim.claim_id} for handoff ${handoffId}`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
434
700
|
startHealthMonitor() {
|
|
435
701
|
// Skip health monitoring for serve mode (no tmux session)
|
|
436
702
|
if (this.serveMode)
|
|
@@ -442,6 +708,7 @@ Nudge agent:
|
|
|
442
708
|
this.healthCheckInterval = setInterval(async () => {
|
|
443
709
|
if (!this.isRunning)
|
|
444
710
|
return;
|
|
711
|
+
await this.checkPendingHandoffSla();
|
|
445
712
|
// For sandbox mode, pass container name so health check looks inside container
|
|
446
713
|
const containerName = this.sandboxMode ? this.sandbox?.getContainerName() : undefined;
|
|
447
714
|
const health = isSessionHealthy(this.agentName, containerName);
|
|
@@ -556,6 +823,11 @@ Nudge agent:
|
|
|
556
823
|
}
|
|
557
824
|
// Nudge grace period expired — log warning but do NOT restart
|
|
558
825
|
console.log(`[HEALTH] Agent still stuck after nudge. Manual intervention required.`);
|
|
826
|
+
updateAgentInState(this.agentName, {
|
|
827
|
+
status: "waiting",
|
|
828
|
+
});
|
|
829
|
+
void this.releaseAllAutoClaims("worker waiting for human intervention");
|
|
830
|
+
sendNudge(this.agentName, "[AgentMesh] Worker still blocked after nudge. Please request human intervention or resume once approvals are available.");
|
|
559
831
|
}
|
|
560
832
|
}
|
|
561
833
|
async stop() {
|
|
@@ -862,15 +1134,12 @@ Logs: docker logs ${containerName}
|
|
|
862
1134
|
}
|
|
863
1135
|
else {
|
|
864
1136
|
console.log(`Workspace exists, pulling latest...`);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
stdio: "pipe",
|
|
869
|
-
timeout: 30_000,
|
|
870
|
-
});
|
|
1137
|
+
const update = updateWorkspaceFromRemote(uniqueWorkdir, repo.default_branch ?? project.default_branch ?? "main");
|
|
1138
|
+
if (!update.ok) {
|
|
1139
|
+
console.warn("Could not pull latest; continuing with current workspace state.");
|
|
871
1140
|
}
|
|
872
|
-
|
|
873
|
-
console.
|
|
1141
|
+
else if (update.autoStashed) {
|
|
1142
|
+
console.log(`Workspace updated after auto-stash (${update.stashRef})`);
|
|
874
1143
|
}
|
|
875
1144
|
this.agentConfig.workdir = uniqueWorkdir;
|
|
876
1145
|
}
|
|
@@ -973,15 +1242,12 @@ Logs: docker logs ${containerName}
|
|
|
973
1242
|
else {
|
|
974
1243
|
// Directory exists with .git — pull latest
|
|
975
1244
|
console.log(`Workspace exists, pulling latest...`);
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
stdio: "pipe",
|
|
980
|
-
timeout: 30_000,
|
|
981
|
-
});
|
|
1245
|
+
const update = updateWorkspaceFromRemote(uniqueWorkdir, assignmentWithWorkdir.project.default_branch ?? "main");
|
|
1246
|
+
if (!update.ok) {
|
|
1247
|
+
console.warn("Could not pull latest; continuing with current workspace state.");
|
|
982
1248
|
}
|
|
983
|
-
|
|
984
|
-
console.
|
|
1249
|
+
else if (update.autoStashed) {
|
|
1250
|
+
console.log(`Workspace updated after auto-stash (${update.stashRef})`);
|
|
985
1251
|
}
|
|
986
1252
|
this.agentConfig.workdir = uniqueWorkdir;
|
|
987
1253
|
}
|