@integrity-labs/agt-cli 0.7.7 → 0.7.8
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.
|
@@ -6,9 +6,10 @@ import {
|
|
|
6
6
|
getFramework,
|
|
7
7
|
getHostId,
|
|
8
8
|
provision,
|
|
9
|
+
provisionStopHook,
|
|
9
10
|
requireHost,
|
|
10
11
|
resolveChannels
|
|
11
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-B47JW4MM.js";
|
|
12
13
|
import {
|
|
13
14
|
findTaskByTemplate,
|
|
14
15
|
getProjectDir,
|
|
@@ -20,10 +21,42 @@ import {
|
|
|
20
21
|
|
|
21
22
|
// src/lib/manager-worker.ts
|
|
22
23
|
import { createHash } from "crypto";
|
|
23
|
-
import { readFileSync as
|
|
24
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync2, rmSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
24
25
|
import https from "https";
|
|
25
26
|
import { join as join2 } from "path";
|
|
26
27
|
|
|
28
|
+
// src/lib/mcp-sanitize.ts
|
|
29
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
30
|
+
function sanitizeMcpJson(mcpConfigPath, apiHost) {
|
|
31
|
+
try {
|
|
32
|
+
const mcpRaw = JSON.parse(readFileSync(mcpConfigPath, "utf-8"));
|
|
33
|
+
const servers = mcpRaw.mcpServers;
|
|
34
|
+
if (!servers) return false;
|
|
35
|
+
let changed = false;
|
|
36
|
+
for (const [key, val] of Object.entries(servers)) {
|
|
37
|
+
if (typeof val?.url !== "string") continue;
|
|
38
|
+
if (val.url.startsWith("/")) {
|
|
39
|
+
if (apiHost) {
|
|
40
|
+
val.url = `${apiHost}${val.url}`;
|
|
41
|
+
changed = true;
|
|
42
|
+
} else {
|
|
43
|
+
delete servers[key];
|
|
44
|
+
changed = true;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!val.type) {
|
|
49
|
+
val.type = "sse";
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (changed) writeFileSync(mcpConfigPath, JSON.stringify(mcpRaw, null, 2));
|
|
54
|
+
return changed;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
27
60
|
// src/lib/gateway-client.ts
|
|
28
61
|
import { EventEmitter } from "events";
|
|
29
62
|
import WebSocket from "ws";
|
|
@@ -297,7 +330,7 @@ var GatewayClientPool = class extends EventEmitter {
|
|
|
297
330
|
import { spawn, execSync } from "child_process";
|
|
298
331
|
import { join } from "path";
|
|
299
332
|
import { homedir } from "os";
|
|
300
|
-
import { existsSync, readFileSync } from "fs";
|
|
333
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
301
334
|
var sessions = /* @__PURE__ */ new Map();
|
|
302
335
|
function startPersistentSession(config2) {
|
|
303
336
|
const existing = sessions.get(config2.codeName);
|
|
@@ -318,6 +351,7 @@ function startPersistentSession(config2) {
|
|
|
318
351
|
}
|
|
319
352
|
function spawnSession(config2, session) {
|
|
320
353
|
const { codeName, projectDir, mcpConfigPath, claudeMdPath, channels, devChannels, log: log2 } = config2;
|
|
354
|
+
sanitizeMcpJson(mcpConfigPath);
|
|
321
355
|
const args = [];
|
|
322
356
|
if (channels.length > 0) {
|
|
323
357
|
args.push("--channels", ...channels);
|
|
@@ -346,7 +380,7 @@ function spawnSession(config2, session) {
|
|
|
346
380
|
let envPrefix = "";
|
|
347
381
|
if (existsSync(envIntegrationsPath)) {
|
|
348
382
|
try {
|
|
349
|
-
const envContent =
|
|
383
|
+
const envContent = readFileSync2(envIntegrationsPath, "utf-8");
|
|
350
384
|
const envVars = envContent.split("\n").filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
351
385
|
const eqIdx = line.indexOf("=");
|
|
352
386
|
const key = line.slice(0, eqIdx);
|
|
@@ -490,6 +524,32 @@ function resetRestartCount(codeName) {
|
|
|
490
524
|
const session = sessions.get(codeName);
|
|
491
525
|
if (session) session.restartCount = 0;
|
|
492
526
|
}
|
|
527
|
+
async function stopAllSessionsAndWait(log2, opts) {
|
|
528
|
+
const codeNames = [...sessions.keys()];
|
|
529
|
+
if (codeNames.length === 0) return;
|
|
530
|
+
const exitPromises = [];
|
|
531
|
+
for (const codeName of codeNames) {
|
|
532
|
+
const session = sessions.get(codeName);
|
|
533
|
+
const proc = session?.process;
|
|
534
|
+
stopPersistentSession(codeName, log2);
|
|
535
|
+
if (proc && !proc.killed && proc.exitCode === null) {
|
|
536
|
+
exitPromises.push(
|
|
537
|
+
new Promise((resolve) => {
|
|
538
|
+
proc.on("close", resolve);
|
|
539
|
+
proc.on("error", resolve);
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (exitPromises.length === 0) return;
|
|
545
|
+
await Promise.race([
|
|
546
|
+
Promise.all(exitPromises),
|
|
547
|
+
new Promise((resolve) => setTimeout(() => {
|
|
548
|
+
log2(`[persistent-session] Shutdown timeout (${opts.timeoutMs}ms), force-killing remaining sessions`);
|
|
549
|
+
resolve();
|
|
550
|
+
}, opts.timeoutMs))
|
|
551
|
+
]);
|
|
552
|
+
}
|
|
493
553
|
function getProjectDir2(codeName) {
|
|
494
554
|
return join(homedir(), ".augmented", codeName, "project");
|
|
495
555
|
}
|
|
@@ -934,14 +994,14 @@ function checkClaudeAuth(execFileSync) {
|
|
|
934
994
|
}
|
|
935
995
|
function loadGatewayPorts() {
|
|
936
996
|
try {
|
|
937
|
-
return JSON.parse(
|
|
997
|
+
return JSON.parse(readFileSync3(GATEWAY_PORTS_FILE, "utf-8"));
|
|
938
998
|
} catch {
|
|
939
999
|
return {};
|
|
940
1000
|
}
|
|
941
1001
|
}
|
|
942
1002
|
function saveGatewayPorts(ports) {
|
|
943
1003
|
mkdirSync(AUGMENTED_DIR, { recursive: true });
|
|
944
|
-
|
|
1004
|
+
writeFileSync2(GATEWAY_PORTS_FILE, JSON.stringify(ports, null, 2));
|
|
945
1005
|
}
|
|
946
1006
|
function allocatePort(codeName) {
|
|
947
1007
|
const ports = loadGatewayPorts();
|
|
@@ -967,7 +1027,7 @@ var STATE_FILE = join2(process.env["HOME"] ?? "/tmp", ".augmented", "manager-sta
|
|
|
967
1027
|
function send(msg) {
|
|
968
1028
|
if (msg.type === "state-update") {
|
|
969
1029
|
try {
|
|
970
|
-
|
|
1030
|
+
writeFileSync2(STATE_FILE, JSON.stringify(msg.state, null, 2));
|
|
971
1031
|
} catch {
|
|
972
1032
|
}
|
|
973
1033
|
}
|
|
@@ -989,7 +1049,7 @@ function sha256(content) {
|
|
|
989
1049
|
}
|
|
990
1050
|
function hashFile(filePath) {
|
|
991
1051
|
try {
|
|
992
|
-
const content =
|
|
1052
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
993
1053
|
return sha256(content);
|
|
994
1054
|
} catch {
|
|
995
1055
|
return null;
|
|
@@ -1000,7 +1060,7 @@ async function migrateToProfiles() {
|
|
|
1000
1060
|
const sharedConfigPath = join2(homeDir, ".openclaw", "openclaw.json");
|
|
1001
1061
|
let sharedConfig;
|
|
1002
1062
|
try {
|
|
1003
|
-
sharedConfig = JSON.parse(
|
|
1063
|
+
sharedConfig = JSON.parse(readFileSync3(sharedConfigPath, "utf-8"));
|
|
1004
1064
|
} catch {
|
|
1005
1065
|
return;
|
|
1006
1066
|
}
|
|
@@ -1024,8 +1084,8 @@ async function migrateToProfiles() {
|
|
|
1024
1084
|
const authFile = join2(sharedAuthDir, "auth-profiles.json");
|
|
1025
1085
|
if (existsSync2(authFile)) {
|
|
1026
1086
|
mkdirSync(profileAuthDir, { recursive: true });
|
|
1027
|
-
const authContent =
|
|
1028
|
-
|
|
1087
|
+
const authContent = readFileSync3(authFile, "utf-8");
|
|
1088
|
+
writeFileSync2(join2(profileAuthDir, "auth-profiles.json"), authContent);
|
|
1029
1089
|
}
|
|
1030
1090
|
allocatePort(codeName);
|
|
1031
1091
|
migrated++;
|
|
@@ -1063,7 +1123,7 @@ function readGatewayToken(codeName) {
|
|
|
1063
1123
|
}
|
|
1064
1124
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1065
1125
|
try {
|
|
1066
|
-
const cfg = JSON.parse(
|
|
1126
|
+
const cfg = JSON.parse(readFileSync3(join2(homeDir, `.openclaw-${codeName}`, "openclaw.json"), "utf-8"));
|
|
1067
1127
|
return cfg?.gateway?.auth?.token;
|
|
1068
1128
|
} catch {
|
|
1069
1129
|
return void 0;
|
|
@@ -1075,7 +1135,7 @@ function isGatewayHung(codeName) {
|
|
|
1075
1135
|
const jobsPath = join2(homeDir, `.openclaw-${codeName}`, "cron", "jobs.json");
|
|
1076
1136
|
if (!existsSync2(jobsPath)) return false;
|
|
1077
1137
|
try {
|
|
1078
|
-
const data = JSON.parse(
|
|
1138
|
+
const data = JSON.parse(readFileSync3(jobsPath, "utf-8"));
|
|
1079
1139
|
const jobs = data.jobs ?? data;
|
|
1080
1140
|
if (!Array.isArray(jobs)) return false;
|
|
1081
1141
|
const now = Date.now();
|
|
@@ -1116,11 +1176,11 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1116
1176
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1117
1177
|
const configPath = join2(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
1118
1178
|
if (existsSync2(configPath)) {
|
|
1119
|
-
const cfg = JSON.parse(
|
|
1179
|
+
const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1120
1180
|
if (cfg.gateway?.port !== status.port) {
|
|
1121
1181
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1122
1182
|
cfg.gateway.port = status.port;
|
|
1123
|
-
|
|
1183
|
+
writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
|
|
1124
1184
|
}
|
|
1125
1185
|
}
|
|
1126
1186
|
} catch {
|
|
@@ -1142,10 +1202,10 @@ async function ensureGatewayRunning(codeName, adapter) {
|
|
|
1142
1202
|
const homeDir = process.env["HOME"] ?? "/tmp";
|
|
1143
1203
|
const configPath = join2(homeDir, `.openclaw-${codeName}`, "openclaw.json");
|
|
1144
1204
|
if (existsSync2(configPath)) {
|
|
1145
|
-
const cfg = JSON.parse(
|
|
1205
|
+
const cfg = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1146
1206
|
if (!cfg.gateway) cfg.gateway = {};
|
|
1147
1207
|
cfg.gateway.port = port;
|
|
1148
|
-
|
|
1208
|
+
writeFileSync2(configPath, JSON.stringify(cfg, null, 2));
|
|
1149
1209
|
}
|
|
1150
1210
|
} catch {
|
|
1151
1211
|
}
|
|
@@ -1545,7 +1605,7 @@ async function processAgent(agent, agentStates) {
|
|
|
1545
1605
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
1546
1606
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
1547
1607
|
for (const file of changedFiles) {
|
|
1548
|
-
|
|
1608
|
+
writeFileSync2(join2(agentDir, file.relativePath), file.content);
|
|
1549
1609
|
}
|
|
1550
1610
|
lastProvisionAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1551
1611
|
knownVersions.set(agent.agent_id, { charterVersion, toolsVersion });
|
|
@@ -1737,6 +1797,44 @@ async function processAgent(agent, agentStates) {
|
|
|
1737
1797
|
}
|
|
1738
1798
|
knownIntegrationHashes.set(agent.agent_id, intHash);
|
|
1739
1799
|
log(`Integrations provisioned for '${agent.code_name}' (${integrations.length} integration(s))`);
|
|
1800
|
+
const managedIntegrations = integrations.filter((i) => i.auth_type === "managed");
|
|
1801
|
+
if (managedIntegrations.length > 0 && frameworkAdapter.writeMcpServer) {
|
|
1802
|
+
try {
|
|
1803
|
+
const toolkitData = await api.post("/host/managed-toolkits", { agent_ids: [agent.agent_id] });
|
|
1804
|
+
const expectedServerIds = /* @__PURE__ */ new Set();
|
|
1805
|
+
for (const tk of toolkitData.toolkits) {
|
|
1806
|
+
if (tk.agent_id !== agent.agent_id) continue;
|
|
1807
|
+
const serverId = tk.toolkit_id.replace(/\//g, "-");
|
|
1808
|
+
expectedServerIds.add(serverId);
|
|
1809
|
+
const proxyUrl = tk.proxy_url.startsWith("/") ? `${requireHost()}${tk.proxy_url}` : tk.proxy_url;
|
|
1810
|
+
frameworkAdapter.writeMcpServer(agent.code_name, serverId, {
|
|
1811
|
+
url: proxyUrl
|
|
1812
|
+
});
|
|
1813
|
+
log(`[managed-toolkit] ${agent.code_name}: ${tk.toolkit_name} \u2192 ${proxyUrl}`);
|
|
1814
|
+
}
|
|
1815
|
+
if (frameworkAdapter.removeMcpServer) {
|
|
1816
|
+
try {
|
|
1817
|
+
const { readFileSync: readFileSync4 } = await import("fs");
|
|
1818
|
+
const { join: join3 } = await import("path");
|
|
1819
|
+
const { homedir: homedir2 } = await import("os");
|
|
1820
|
+
const mcpPath = join3(homedir2(), ".augmented", "agents", agent.code_name, "provision", ".mcp.json");
|
|
1821
|
+
const mcpConfig = JSON.parse(readFileSync4(mcpPath, "utf-8"));
|
|
1822
|
+
if (mcpConfig.mcpServers) {
|
|
1823
|
+
const managedPrefixes = ["composio-", "one-", "nango-", "paragon-"];
|
|
1824
|
+
for (const key of Object.keys(mcpConfig.mcpServers)) {
|
|
1825
|
+
if (managedPrefixes.some((p) => key.startsWith(p)) && !expectedServerIds.has(key)) {
|
|
1826
|
+
frameworkAdapter.removeMcpServer(agent.code_name, key);
|
|
1827
|
+
log(`[managed-toolkit] Removed stale MCP server '${key}' for '${agent.code_name}'`);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
} catch {
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
log(`[managed-toolkit] Failed to provision for '${agent.code_name}': ${err.message}`);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1740
1838
|
needsGatewayRestart = true;
|
|
1741
1839
|
const hasLcm = integrations.some((i) => i.definition_id === "lossless-claw");
|
|
1742
1840
|
if (hasLcm && !losslessClawInstalled.get(agent.code_name)) {
|
|
@@ -1983,8 +2081,6 @@ async function processAgent(agent, agentStates) {
|
|
|
1983
2081
|
agentDisplayName: agent.display_name
|
|
1984
2082
|
});
|
|
1985
2083
|
}
|
|
1986
|
-
const ccInFlight = agentFw === "claude-code" ? claudeTaskConcurrency.get(agent.code_name) ?? 0 : null;
|
|
1987
|
-
log(`[${agent.code_name}] Harvest gate: gatewayRunning=${gatewayRunning} gatewayPort=${gatewayPort} tasks=${tasks.length} fw=${agentFw}${ccInFlight !== null ? ` claude-p=${ccInFlight}/${MAX_CLAUDE_CONCURRENCY}` : ""}`);
|
|
1988
2084
|
if (agentFw === "openclaw" && gatewayRunning && gatewayPort && tasks.length > 0) {
|
|
1989
2085
|
const lastHarvest = lastHarvestAt.get(agent.code_name) ?? 0;
|
|
1990
2086
|
if (Date.now() - lastHarvest >= HARVEST_INTERVAL_MS) {
|
|
@@ -2006,7 +2102,7 @@ async function processAgent(agent, agentStates) {
|
|
|
2006
2102
|
const jobsPath = join2(homeDir, `.openclaw-${agent.code_name}`, "cron", "jobs.json");
|
|
2007
2103
|
if (existsSync2(jobsPath)) {
|
|
2008
2104
|
try {
|
|
2009
|
-
const jobsData = JSON.parse(
|
|
2105
|
+
const jobsData = JSON.parse(readFileSync3(jobsPath, "utf-8"));
|
|
2010
2106
|
const kanbanJob = (jobsData.jobs ?? []).find(
|
|
2011
2107
|
(j) => typeof j.name === "string" && j.name.includes("kanban-work")
|
|
2012
2108
|
);
|
|
@@ -2019,7 +2115,20 @@ async function processAgent(agent, agentStates) {
|
|
|
2019
2115
|
}
|
|
2020
2116
|
}
|
|
2021
2117
|
} else if (agentFw === "claude-code") {
|
|
2022
|
-
|
|
2118
|
+
if (sessionMode === "persistent") {
|
|
2119
|
+
if (isSessionHealthy(agent.code_name)) {
|
|
2120
|
+
const todayItem = boardItems.find((b) => b.status === "today");
|
|
2121
|
+
const taskHint = todayItem ? ` Top item: "${todayItem.title}" (priority ${todayItem.priority}).` : "";
|
|
2122
|
+
injectMessage(agent.code_name, "task", `New work triggered. Check your kanban board with kanban_list and pick up the next item.${taskHint}`, {
|
|
2123
|
+
task_name: "kanban-work-trigger"
|
|
2124
|
+
});
|
|
2125
|
+
log(`[persistent-session] Work trigger injected for '${agent.code_name}'`);
|
|
2126
|
+
} else {
|
|
2127
|
+
log(`[persistent-session] Work trigger skipped for '${agent.code_name}' \u2014 session not yet healthy`);
|
|
2128
|
+
}
|
|
2129
|
+
} else {
|
|
2130
|
+
fireClaudeWorkTrigger(agent.code_name, agent.agent_id, boardItems);
|
|
2131
|
+
}
|
|
2023
2132
|
}
|
|
2024
2133
|
}
|
|
2025
2134
|
}
|
|
@@ -2150,7 +2259,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2150
2259
|
const indexPath = join2(sessionsDir, "sessions.json");
|
|
2151
2260
|
if (!existsSync2(indexPath)) return;
|
|
2152
2261
|
try {
|
|
2153
|
-
const raw =
|
|
2262
|
+
const raw = readFileSync3(indexPath, "utf-8");
|
|
2154
2263
|
const index = JSON.parse(raw);
|
|
2155
2264
|
const cronRunKeys = Object.keys(index).filter((k) => k.includes(":cron:") && k.includes(":run:")).map((k) => ({
|
|
2156
2265
|
key: k,
|
|
@@ -2195,7 +2304,7 @@ function cleanupCronSessions(sessionsDir, keepCount) {
|
|
|
2195
2304
|
}
|
|
2196
2305
|
}
|
|
2197
2306
|
}
|
|
2198
|
-
|
|
2307
|
+
writeFileSync2(indexPath, JSON.stringify(index));
|
|
2199
2308
|
if (toDelete.length > 0) {
|
|
2200
2309
|
log(`Cleaned ${toDelete.length} cron session(s) and ${deletedFiles} file(s) from ${sessionsDir}`);
|
|
2201
2310
|
}
|
|
@@ -2206,7 +2315,7 @@ var STALE_RUN_TIMEOUT_MS = 5 * 6e4;
|
|
|
2206
2315
|
function clearStaleCronRunState(jobsPath) {
|
|
2207
2316
|
if (!existsSync2(jobsPath)) return;
|
|
2208
2317
|
try {
|
|
2209
|
-
const raw =
|
|
2318
|
+
const raw = readFileSync3(jobsPath, "utf-8");
|
|
2210
2319
|
const data = JSON.parse(raw);
|
|
2211
2320
|
const jobs = data.jobs ?? data;
|
|
2212
2321
|
if (!Array.isArray(jobs)) return;
|
|
@@ -2231,7 +2340,7 @@ function clearStaleCronRunState(jobsPath) {
|
|
|
2231
2340
|
}
|
|
2232
2341
|
}
|
|
2233
2342
|
if (changed) {
|
|
2234
|
-
|
|
2343
|
+
writeFileSync2(jobsPath, JSON.stringify(data, null, 2));
|
|
2235
2344
|
}
|
|
2236
2345
|
} catch {
|
|
2237
2346
|
}
|
|
@@ -2334,6 +2443,7 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
2334
2443
|
async function executeAndProcessClaudeTask(codeName, agentId, task, prompt) {
|
|
2335
2444
|
const projectDir = getProjectDir(codeName);
|
|
2336
2445
|
const mcpConfigPath = join2(projectDir, ".mcp.json");
|
|
2446
|
+
sanitizeMcpJson(mcpConfigPath, requireHost());
|
|
2337
2447
|
try {
|
|
2338
2448
|
const claudeMdPath = join2(projectDir, "CLAUDE.md");
|
|
2339
2449
|
const claudeArgs = [
|
|
@@ -2464,6 +2574,11 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2464
2574
|
if (persistentSessionAgents.has(codeName)) {
|
|
2465
2575
|
log(`[persistent-session] Session for '${codeName}' is unhealthy, will restart`);
|
|
2466
2576
|
}
|
|
2577
|
+
try {
|
|
2578
|
+
provisionStopHook(codeName);
|
|
2579
|
+
} catch (err) {
|
|
2580
|
+
log(`[persistent-session] Failed to provision Stop hook for '${codeName}': ${err.message}`);
|
|
2581
|
+
}
|
|
2467
2582
|
startPersistentSession({
|
|
2468
2583
|
codeName,
|
|
2469
2584
|
agentId: agent.agent_id,
|
|
@@ -2505,6 +2620,21 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
2505
2620
|
}
|
|
2506
2621
|
}
|
|
2507
2622
|
log(`[persistent-session] Injecting task '${task.name}' into '${codeName}'`);
|
|
2623
|
+
const projectDir2 = getProjectDir2(codeName);
|
|
2624
|
+
const markerDir = join2(projectDir2, ".claude");
|
|
2625
|
+
const markerPath = join2(markerDir, ".agt-pending-task.json");
|
|
2626
|
+
try {
|
|
2627
|
+
mkdirSync(markerDir, { recursive: true });
|
|
2628
|
+
writeFileSync2(markerPath, JSON.stringify({
|
|
2629
|
+
agent_id: agent.agent_id,
|
|
2630
|
+
task_id: task.taskId,
|
|
2631
|
+
template_id: task.templateId,
|
|
2632
|
+
task_name: task.name,
|
|
2633
|
+
injected_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2634
|
+
}), "utf-8");
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
log(`[persistent-session] Failed to write task marker for '${codeName}': ${err.message}`);
|
|
2637
|
+
}
|
|
2508
2638
|
const injected = await injectMessage(codeName, "task", prompt, {
|
|
2509
2639
|
task_id: task.taskId,
|
|
2510
2640
|
template_id: task.templateId,
|
|
@@ -3041,7 +3171,7 @@ function getBuiltInSkillContent(skillId) {
|
|
|
3041
3171
|
];
|
|
3042
3172
|
for (const candidate of candidates) {
|
|
3043
3173
|
if (existsSync2(candidate)) {
|
|
3044
|
-
const content =
|
|
3174
|
+
const content = readFileSync3(candidate, "utf-8");
|
|
3045
3175
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
3046
3176
|
builtInSkillCache.set(skillId, files);
|
|
3047
3177
|
return files;
|
|
@@ -3559,16 +3689,50 @@ function scheduleNext() {
|
|
|
3559
3689
|
void pollCycle().then(scheduleNext);
|
|
3560
3690
|
}, config.intervalMs);
|
|
3561
3691
|
}
|
|
3692
|
+
async function killAllAgtTmuxSessions() {
|
|
3693
|
+
try {
|
|
3694
|
+
const { stdout: output } = await execFilePromiseLong(
|
|
3695
|
+
"tmux",
|
|
3696
|
+
["list-sessions", "-F", "#{session_name}"],
|
|
3697
|
+
{ timeout: 2e3, stdin: "ignore" }
|
|
3698
|
+
);
|
|
3699
|
+
const sessions2 = output.trim().split("\n").filter((s) => s.startsWith("agt-"));
|
|
3700
|
+
for (const session of sessions2) {
|
|
3701
|
+
try {
|
|
3702
|
+
await execFilePromiseLong("tmux", ["kill-session", "-t", session], {
|
|
3703
|
+
timeout: 2e3,
|
|
3704
|
+
stdin: "ignore"
|
|
3705
|
+
});
|
|
3706
|
+
log(`Killed tmux session '${session}'`);
|
|
3707
|
+
} catch {
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
if (sessions2.length > 0) {
|
|
3711
|
+
log(`Cleaned up ${sessions2.length} agt-* tmux session(s)`);
|
|
3712
|
+
}
|
|
3713
|
+
} catch {
|
|
3714
|
+
}
|
|
3715
|
+
}
|
|
3562
3716
|
async function stopPolling() {
|
|
3563
3717
|
running = false;
|
|
3564
3718
|
if (pollTimer) {
|
|
3565
3719
|
clearTimeout(pollTimer);
|
|
3566
3720
|
pollTimer = null;
|
|
3567
3721
|
}
|
|
3722
|
+
const shutdownTimer = setTimeout(() => {
|
|
3723
|
+
log("Shutdown timeout exceeded (5s), forcing exit");
|
|
3724
|
+
process.exit(1);
|
|
3725
|
+
}, 5e3);
|
|
3726
|
+
shutdownTimer.unref();
|
|
3568
3727
|
stopCaffeinate();
|
|
3569
3728
|
stopRealtimeChat();
|
|
3570
3729
|
stopGatewayPool();
|
|
3730
|
+
log("Stopping persistent sessions...");
|
|
3731
|
+
await stopAllSessionsAndWait(log, { timeoutMs: 4e3 });
|
|
3732
|
+
log("Stopping gateway processes...");
|
|
3571
3733
|
await stopAllGateways();
|
|
3734
|
+
await killAllAgtTmuxSessions();
|
|
3735
|
+
clearTimeout(shutdownTimer);
|
|
3572
3736
|
}
|
|
3573
3737
|
function startManager(opts) {
|
|
3574
3738
|
config = opts;
|