@integrity-labs/agt-cli 0.28.716 → 0.28.718
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/bin/agt.js +5 -5
- package/dist/{chunk-LHYYO4NY.js → chunk-7LH3SVAD.js} +124 -1
- package/dist/chunk-7LH3SVAD.js.map +1 -0
- package/dist/{chunk-OOYUWMTR.js → chunk-EEZHSNQ2.js} +4 -4
- package/dist/{chunk-ILDRNSOB.js → chunk-FNALM4HG.js} +120 -19
- package/dist/chunk-FNALM4HG.js.map +1 -0
- package/dist/{claude-pair-runtime-JK7GQU25.js → claude-pair-runtime-PDG74SAT.js} +2 -2
- package/dist/lib/manager-worker.js +535 -347
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +123 -0
- package/dist/mcp/index.js +123 -0
- package/dist/mcp/origami.js +123 -0
- package/dist/mcp/slack-channel.js +123 -0
- package/dist/mcp/telegram-channel.js +132 -9
- package/dist/{persistent-session-DJSSEIR6.js → persistent-session-2AWYIUCX.js} +7 -3
- package/dist/{responsiveness-probe-A5335ORT.js → responsiveness-probe-JQS4WA2L.js} +3 -3
- package/dist/{session-auth-dead-KUYVUOK5.js → session-auth-dead-VBMZW4GG.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-ILDRNSOB.js.map +0 -1
- package/dist/chunk-LHYYO4NY.js.map +0 -1
- /package/dist/{chunk-OOYUWMTR.js.map → chunk-EEZHSNQ2.js.map} +0 -0
- /package/dist/{claude-pair-runtime-JK7GQU25.js.map → claude-pair-runtime-PDG74SAT.js.map} +0 -0
- /package/dist/{persistent-session-DJSSEIR6.js.map → persistent-session-2AWYIUCX.js.map} +0 -0
- /package/dist/{responsiveness-probe-A5335ORT.js.map → responsiveness-probe-JQS4WA2L.js.map} +0 -0
- /package/dist/{session-auth-dead-KUYVUOK5.js.map → session-auth-dead-VBMZW4GG.js.map} +0 -0
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
safeWriteJsonAtomic,
|
|
58
58
|
setConfigHash,
|
|
59
59
|
tripClass
|
|
60
|
-
} from "../chunk-
|
|
60
|
+
} from "../chunk-EEZHSNQ2.js";
|
|
61
61
|
import {
|
|
62
62
|
getProjectDir as getProjectDir2,
|
|
63
63
|
getReadyTasks,
|
|
@@ -115,7 +115,7 @@ import {
|
|
|
115
115
|
takeZombieDetection,
|
|
116
116
|
toOpencodeModel,
|
|
117
117
|
writeEgressAllowlist
|
|
118
|
-
} from "../chunk-
|
|
118
|
+
} from "../chunk-FNALM4HG.js";
|
|
119
119
|
import {
|
|
120
120
|
ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
|
|
121
121
|
AnchorSessionClient,
|
|
@@ -200,14 +200,14 @@ import {
|
|
|
200
200
|
subagentActivityAgeSeconds,
|
|
201
201
|
sumTranscriptUsageInWindow,
|
|
202
202
|
transcriptActivityAgeSeconds
|
|
203
|
-
} from "../chunk-
|
|
203
|
+
} from "../chunk-7LH3SVAD.js";
|
|
204
204
|
import {
|
|
205
205
|
reapOrphanChannelMcps
|
|
206
206
|
} from "../chunk-XWVM4KPK.js";
|
|
207
207
|
|
|
208
208
|
// src/lib/manager-worker.ts
|
|
209
|
-
import { createHash as
|
|
210
|
-
import { readFileSync as
|
|
209
|
+
import { createHash as createHash18 } from "crypto";
|
|
210
|
+
import { readFileSync as readFileSync30, writeFileSync as writeFileSync16, mkdirSync as mkdirSync12, existsSync as existsSync17, rmSync as rmSync6, readdirSync as readdirSync10, statSync as statSync9, unlinkSync as unlinkSync6, renameSync as renameSync9, utimesSync as utimesSync2 } from "fs";
|
|
211
211
|
|
|
212
212
|
// src/lib/atomic-file-replace.ts
|
|
213
213
|
import { copyFileSync, renameSync, unlinkSync } from "fs";
|
|
@@ -233,7 +233,7 @@ function defaultUnique() {
|
|
|
233
233
|
|
|
234
234
|
// src/lib/manager-worker.ts
|
|
235
235
|
import { execFileSync as syncExecFile } from "child_process";
|
|
236
|
-
import { join as
|
|
236
|
+
import { join as join39, dirname as dirname10, delimiter as pathDelimiter } from "path";
|
|
237
237
|
import { homedir as homedir17 } from "os";
|
|
238
238
|
import { fileURLToPath } from "url";
|
|
239
239
|
|
|
@@ -1691,12 +1691,12 @@ function isPathInsideDir(candidate, dir) {
|
|
|
1691
1691
|
return resolvedCandidate.startsWith(resolvedDir.endsWith(sep) ? resolvedDir : resolvedDir + sep);
|
|
1692
1692
|
}
|
|
1693
1693
|
function findMissingMcpBundles(mcpConfigPath, deps = {}) {
|
|
1694
|
-
const
|
|
1695
|
-
const
|
|
1694
|
+
const existsSync18 = deps.existsSync ?? nodeExistsSync;
|
|
1695
|
+
const readFileSync31 = deps.readFileSync ?? nodeReadFileSync;
|
|
1696
1696
|
const mcpDir = deps.mcpDir ?? getSharedMcpDir();
|
|
1697
1697
|
let parsed;
|
|
1698
1698
|
try {
|
|
1699
|
-
parsed = JSON.parse(
|
|
1699
|
+
parsed = JSON.parse(readFileSync31(mcpConfigPath, "utf-8"));
|
|
1700
1700
|
} catch {
|
|
1701
1701
|
return [];
|
|
1702
1702
|
}
|
|
@@ -1716,7 +1716,7 @@ function findMissingMcpBundles(mcpConfigPath, deps = {}) {
|
|
|
1716
1716
|
if (seenPaths.has(resolvedBundlePath)) continue;
|
|
1717
1717
|
let present;
|
|
1718
1718
|
try {
|
|
1719
|
-
present =
|
|
1719
|
+
present = existsSync18(bundlePath);
|
|
1720
1720
|
} catch {
|
|
1721
1721
|
continue;
|
|
1722
1722
|
}
|
|
@@ -5194,19 +5194,122 @@ function computeChannelConfigHash(input) {
|
|
|
5194
5194
|
).digest("hex");
|
|
5195
5195
|
}
|
|
5196
5196
|
|
|
5197
|
-
// src/lib/
|
|
5198
|
-
import {
|
|
5197
|
+
// src/lib/knowledge-cache.ts
|
|
5198
|
+
import { createHash as createHash9 } from "crypto";
|
|
5199
|
+
import {
|
|
5200
|
+
existsSync as existsSync6,
|
|
5201
|
+
mkdirSync as mkdirSync6,
|
|
5202
|
+
readFileSync as readFileSync17,
|
|
5203
|
+
readdirSync as readdirSync5,
|
|
5204
|
+
renameSync as renameSync5,
|
|
5205
|
+
rmSync as rmSync3,
|
|
5206
|
+
statSync as statSync5,
|
|
5207
|
+
utimesSync,
|
|
5208
|
+
writeFileSync as writeFileSync8
|
|
5209
|
+
} from "fs";
|
|
5199
5210
|
import { join as join23 } from "path";
|
|
5211
|
+
var KNOWLEDGE_FETCH_CHUNK = 100;
|
|
5212
|
+
var CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
5213
|
+
var SWEEP_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
5214
|
+
var lastSweepAt = 0;
|
|
5215
|
+
function knowledgeHash(content) {
|
|
5216
|
+
return createHash9("sha256").update(content, "utf8").digest("hex");
|
|
5217
|
+
}
|
|
5218
|
+
function cachePath(cacheDir, hash) {
|
|
5219
|
+
return join23(cacheDir, hash);
|
|
5220
|
+
}
|
|
5221
|
+
function readCachedKnowledge(cacheDir, hash) {
|
|
5222
|
+
const path = cachePath(cacheDir, hash);
|
|
5223
|
+
try {
|
|
5224
|
+
const content = readFileSync17(path, "utf-8");
|
|
5225
|
+
try {
|
|
5226
|
+
const now = /* @__PURE__ */ new Date();
|
|
5227
|
+
utimesSync(path, now, now);
|
|
5228
|
+
} catch {
|
|
5229
|
+
}
|
|
5230
|
+
return content;
|
|
5231
|
+
} catch {
|
|
5232
|
+
return null;
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
function writeCachedKnowledge(cacheDir, hash, content) {
|
|
5236
|
+
if (knowledgeHash(content) !== hash) return false;
|
|
5237
|
+
try {
|
|
5238
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
5239
|
+
const tmp = `${cachePath(cacheDir, hash)}.tmp-${process.pid}`;
|
|
5240
|
+
writeFileSync8(tmp, content, "utf-8");
|
|
5241
|
+
renameSync5(tmp, cachePath(cacheDir, hash));
|
|
5242
|
+
return true;
|
|
5243
|
+
} catch {
|
|
5244
|
+
return false;
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
function sweepKnowledgeCache(cacheDir, now = Date.now()) {
|
|
5248
|
+
if (now - lastSweepAt < SWEEP_INTERVAL_MS) return;
|
|
5249
|
+
lastSweepAt = now;
|
|
5250
|
+
try {
|
|
5251
|
+
if (!existsSync6(cacheDir)) return;
|
|
5252
|
+
for (const name of readdirSync5(cacheDir)) {
|
|
5253
|
+
const path = join23(cacheDir, name);
|
|
5254
|
+
try {
|
|
5255
|
+
if (now - statSync5(path).mtimeMs > CACHE_TTL_MS) rmSync3(path, { force: true });
|
|
5256
|
+
} catch {
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
} catch {
|
|
5260
|
+
}
|
|
5261
|
+
}
|
|
5262
|
+
async function resolveKnowledgeFromManifest(args) {
|
|
5263
|
+
const { manifest, cacheDir, delivery, fetchContents, log: log2 } = args;
|
|
5264
|
+
const meta = (e) => ({ title: e.title, slug: e.slug, scope: e.scope });
|
|
5265
|
+
if (delivery === "search") {
|
|
5266
|
+
sweepKnowledgeCache(cacheDir);
|
|
5267
|
+
return manifest.map((e) => ({ ...meta(e), content: null }));
|
|
5268
|
+
}
|
|
5269
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
5270
|
+
const missing = [];
|
|
5271
|
+
for (const entry of manifest) {
|
|
5272
|
+
if (!entry.hash || bodies.has(entry.hash)) continue;
|
|
5273
|
+
const cached = readCachedKnowledge(cacheDir, entry.hash);
|
|
5274
|
+
if (cached !== null) bodies.set(entry.hash, cached);
|
|
5275
|
+
else if (!missing.includes(entry.hash)) missing.push(entry.hash);
|
|
5276
|
+
}
|
|
5277
|
+
if (missing.length > 0) {
|
|
5278
|
+
for (let i = 0; i < missing.length; i += KNOWLEDGE_FETCH_CHUNK) {
|
|
5279
|
+
const chunk2 = missing.slice(i, i + KNOWLEDGE_FETCH_CHUNK);
|
|
5280
|
+
try {
|
|
5281
|
+
for (const { hash, content } of await fetchContents(chunk2)) {
|
|
5282
|
+
if (!writeCachedKnowledge(cacheDir, hash, content)) {
|
|
5283
|
+
log2?.(`[knowledge] hash mismatch for ${hash} \u2014 dropping this poll`);
|
|
5284
|
+
continue;
|
|
5285
|
+
}
|
|
5286
|
+
bodies.set(hash, content);
|
|
5287
|
+
}
|
|
5288
|
+
} catch (err) {
|
|
5289
|
+
log2?.(`[knowledge] fetch failed for ${chunk2.length} entries: ${err.message}`);
|
|
5290
|
+
}
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
sweepKnowledgeCache(cacheDir);
|
|
5294
|
+
return manifest.map((e) => ({
|
|
5295
|
+
...meta(e),
|
|
5296
|
+
content: (e.hash ? bodies.get(e.hash) : void 0) ?? null
|
|
5297
|
+
}));
|
|
5298
|
+
}
|
|
5299
|
+
|
|
5300
|
+
// src/lib/channel-hash-cache.ts
|
|
5301
|
+
import { existsSync as existsSync7, readFileSync as readFileSync18, writeFileSync as writeFileSync9 } from "fs";
|
|
5302
|
+
import { join as join24 } from "path";
|
|
5200
5303
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
5201
5304
|
function getChannelHashCacheFile(configDir) {
|
|
5202
|
-
return
|
|
5305
|
+
return join24(configDir, CACHE_FILENAME);
|
|
5203
5306
|
}
|
|
5204
5307
|
function loadChannelHashCache(target, configDir) {
|
|
5205
5308
|
const path = getChannelHashCacheFile(configDir);
|
|
5206
|
-
if (!
|
|
5309
|
+
if (!existsSync7(path)) return;
|
|
5207
5310
|
let parsed;
|
|
5208
5311
|
try {
|
|
5209
|
-
parsed = JSON.parse(
|
|
5312
|
+
parsed = JSON.parse(readFileSync18(path, "utf-8"));
|
|
5210
5313
|
} catch {
|
|
5211
5314
|
return;
|
|
5212
5315
|
}
|
|
@@ -5220,14 +5323,14 @@ function saveChannelHashCache(source, configDir) {
|
|
|
5220
5323
|
const obj = {};
|
|
5221
5324
|
for (const [key, value] of source) obj[key] = value;
|
|
5222
5325
|
try {
|
|
5223
|
-
|
|
5326
|
+
writeFileSync9(path, JSON.stringify(obj, null, 2));
|
|
5224
5327
|
} catch {
|
|
5225
5328
|
}
|
|
5226
5329
|
}
|
|
5227
5330
|
|
|
5228
5331
|
// src/lib/sender-policy-baseline.ts
|
|
5229
|
-
import { existsSync as
|
|
5230
|
-
import { join as
|
|
5332
|
+
import { existsSync as existsSync8, readFileSync as readFileSync19 } from "fs";
|
|
5333
|
+
import { join as join25 } from "path";
|
|
5231
5334
|
var BASELINE_FILENAME = "sender-policy-baseline.json";
|
|
5232
5335
|
var SENDER_POLICY_BASELINE_VERSION = 1;
|
|
5233
5336
|
var BASELINE_CONCERNS = ["senderPolicy", "slackBehaviour", "msteamsBehaviour"];
|
|
@@ -5239,14 +5342,14 @@ function createDeliveryBaselineMaps() {
|
|
|
5239
5342
|
};
|
|
5240
5343
|
}
|
|
5241
5344
|
function getSenderPolicyBaselineFile(configDir) {
|
|
5242
|
-
return
|
|
5345
|
+
return join25(configDir, BASELINE_FILENAME);
|
|
5243
5346
|
}
|
|
5244
5347
|
function loadSenderPolicyBaseline(target, configDir, log2) {
|
|
5245
5348
|
const path = getSenderPolicyBaselineFile(configDir);
|
|
5246
|
-
if (!
|
|
5349
|
+
if (!existsSync8(path)) return;
|
|
5247
5350
|
let parsed;
|
|
5248
5351
|
try {
|
|
5249
|
-
parsed = JSON.parse(
|
|
5352
|
+
parsed = JSON.parse(readFileSync19(path, "utf-8"));
|
|
5250
5353
|
} catch (err) {
|
|
5251
5354
|
log2?.(
|
|
5252
5355
|
`[sender-policy] discarding corrupt ${BASELINE_FILENAME} (${err.message}) - restrictive-policy agents will take one fail-closed restart`
|
|
@@ -5295,8 +5398,8 @@ function saveSenderPolicyBaseline(source, configDir, log2) {
|
|
|
5295
5398
|
}
|
|
5296
5399
|
|
|
5297
5400
|
// src/lib/stuck-streak-store.ts
|
|
5298
|
-
import { existsSync as
|
|
5299
|
-
import { join as
|
|
5401
|
+
import { existsSync as existsSync9, readFileSync as readFileSync20 } from "fs";
|
|
5402
|
+
import { join as join26 } from "path";
|
|
5300
5403
|
var STORE_FILENAME = "stuck-streaks.json";
|
|
5301
5404
|
var STUCK_STREAK_STORE_VERSION = 1;
|
|
5302
5405
|
var STUCK_STREAK_CONCERNS = ["channelSync", "realtimeRebind"];
|
|
@@ -5304,7 +5407,7 @@ function createStuckStreakMaps() {
|
|
|
5304
5407
|
return { channelSync: /* @__PURE__ */ new Map(), realtimeRebind: /* @__PURE__ */ new Map() };
|
|
5305
5408
|
}
|
|
5306
5409
|
function getStuckStreakStoreFile(configDir) {
|
|
5307
|
-
return
|
|
5410
|
+
return join26(configDir, STORE_FILENAME);
|
|
5308
5411
|
}
|
|
5309
5412
|
function isValidPersistedStreak(value) {
|
|
5310
5413
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
@@ -5343,10 +5446,10 @@ function buildStuckStreakPayload(source) {
|
|
|
5343
5446
|
}
|
|
5344
5447
|
function loadStuckStreaks(target, configDir, log2) {
|
|
5345
5448
|
const path = getStuckStreakStoreFile(configDir);
|
|
5346
|
-
if (!
|
|
5449
|
+
if (!existsSync9(path)) return;
|
|
5347
5450
|
let parsed;
|
|
5348
5451
|
try {
|
|
5349
|
-
parsed = JSON.parse(
|
|
5452
|
+
parsed = JSON.parse(readFileSync20(path, "utf-8"));
|
|
5350
5453
|
} catch (err) {
|
|
5351
5454
|
log2?.(
|
|
5352
5455
|
`[stuck-streaks] discarding corrupt ${STORE_FILENAME} (${err.message}) \u2014 stuck-streak ages restart from zero and one spurious "recovered" close may follow`
|
|
@@ -5863,7 +5966,7 @@ function planGlobalSkillSync(globalSkills, prevIds, hashOf, knownHash, options)
|
|
|
5863
5966
|
}
|
|
5864
5967
|
|
|
5865
5968
|
// src/lib/manager/integration-skill-cache.ts
|
|
5866
|
-
import { join as
|
|
5969
|
+
import { join as join27 } from "path";
|
|
5867
5970
|
function integrationSkillHashKey(agentId, skillId) {
|
|
5868
5971
|
return `plugin-skill:${agentId}:${skillId}`;
|
|
5869
5972
|
}
|
|
@@ -5879,21 +5982,21 @@ function forgetIntegrationSkill(cache2, agentId, skillId) {
|
|
|
5879
5982
|
function removeIntegrationSkillFolder(opts) {
|
|
5880
5983
|
forgetIntegrationSkill(opts.cache, opts.agentId, opts.entry);
|
|
5881
5984
|
for (const dir of opts.dirs) {
|
|
5882
|
-
opts.removeDir(
|
|
5985
|
+
opts.removeDir(join27(dir, opts.entry));
|
|
5883
5986
|
}
|
|
5884
5987
|
}
|
|
5885
5988
|
|
|
5886
5989
|
// src/lib/manager/managed-skill-manifest.ts
|
|
5887
|
-
import { existsSync as
|
|
5888
|
-
import { dirname as dirname7, join as
|
|
5990
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
|
|
5991
|
+
import { dirname as dirname7, join as join28 } from "path";
|
|
5889
5992
|
var MANIFEST_VERSION = 1;
|
|
5890
5993
|
function managedSkillManifestPath(agentRootDir) {
|
|
5891
|
-
return
|
|
5994
|
+
return join28(agentRootDir, "managed-skills.json");
|
|
5892
5995
|
}
|
|
5893
5996
|
function readManagedSkillManifest(path) {
|
|
5894
5997
|
try {
|
|
5895
|
-
if (!
|
|
5896
|
-
const parsed = JSON.parse(
|
|
5998
|
+
if (!existsSync10(path)) return /* @__PURE__ */ new Set();
|
|
5999
|
+
const parsed = JSON.parse(readFileSync21(path, "utf-8"));
|
|
5897
6000
|
const ids = Array.isArray(parsed?.globalSkillIds) ? parsed.globalSkillIds : [];
|
|
5898
6001
|
return new Set(ids.filter((id) => typeof id === "string" && id.length > 0));
|
|
5899
6002
|
} catch {
|
|
@@ -5902,12 +6005,12 @@ function readManagedSkillManifest(path) {
|
|
|
5902
6005
|
}
|
|
5903
6006
|
function writeManagedSkillManifest(path, ids) {
|
|
5904
6007
|
try {
|
|
5905
|
-
|
|
6008
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
5906
6009
|
const body = {
|
|
5907
6010
|
version: MANIFEST_VERSION,
|
|
5908
6011
|
globalSkillIds: [...ids].sort()
|
|
5909
6012
|
};
|
|
5910
|
-
|
|
6013
|
+
writeFileSync10(path, JSON.stringify(body, null, 2));
|
|
5911
6014
|
} catch {
|
|
5912
6015
|
}
|
|
5913
6016
|
}
|
|
@@ -6019,8 +6122,8 @@ function resolveModelChain(refreshData) {
|
|
|
6019
6122
|
}
|
|
6020
6123
|
|
|
6021
6124
|
// src/lib/manager/claude-auth.ts
|
|
6022
|
-
import { existsSync as
|
|
6023
|
-
import { join as
|
|
6125
|
+
import { existsSync as existsSync11, rmSync as rmSync4 } from "fs";
|
|
6126
|
+
import { join as join29 } from "path";
|
|
6024
6127
|
import { homedir as homedir13 } from "os";
|
|
6025
6128
|
async function applyClaudeAuthToEnv(childEnv, label) {
|
|
6026
6129
|
const apiKey = getApiKey();
|
|
@@ -6033,12 +6136,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
6033
6136
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
6034
6137
|
}
|
|
6035
6138
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
6036
|
-
const claudeDir =
|
|
6139
|
+
const claudeDir = join29(homedir13(), ".claude");
|
|
6037
6140
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
6038
|
-
const p =
|
|
6039
|
-
if (
|
|
6141
|
+
const p = join29(claudeDir, filename);
|
|
6142
|
+
if (existsSync11(p)) {
|
|
6040
6143
|
try {
|
|
6041
|
-
|
|
6144
|
+
rmSync4(p, { force: true });
|
|
6042
6145
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
6043
6146
|
} catch {
|
|
6044
6147
|
}
|
|
@@ -6117,8 +6220,8 @@ function heartbeatRuntimeAuthFields(probeVerdict) {
|
|
|
6117
6220
|
}
|
|
6118
6221
|
|
|
6119
6222
|
// src/lib/manager/kanban/parsers.ts
|
|
6120
|
-
import { existsSync as
|
|
6121
|
-
import { join as
|
|
6223
|
+
import { existsSync as existsSync12, readFileSync as readFileSync22 } from "fs";
|
|
6224
|
+
import { join as join30 } from "path";
|
|
6122
6225
|
var STANDUP_TEMPLATES = /* @__PURE__ */ new Set(["daily-standup", "end-of-day-summary"]);
|
|
6123
6226
|
var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-update"]);
|
|
6124
6227
|
var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
|
|
@@ -6270,12 +6373,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
6270
6373
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
6271
6374
|
try {
|
|
6272
6375
|
const candidates = [
|
|
6273
|
-
|
|
6274
|
-
|
|
6376
|
+
join30(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
6377
|
+
join30(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
6275
6378
|
];
|
|
6276
6379
|
for (const candidate of candidates) {
|
|
6277
|
-
if (
|
|
6278
|
-
const content =
|
|
6380
|
+
if (existsSync12(candidate)) {
|
|
6381
|
+
const content = readFileSync22(candidate, "utf-8");
|
|
6279
6382
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
6280
6383
|
builtInSkillCache.set(skillId, files);
|
|
6281
6384
|
return files;
|
|
@@ -6416,19 +6519,19 @@ function formatBoardForPrompt(items, template) {
|
|
|
6416
6519
|
}
|
|
6417
6520
|
|
|
6418
6521
|
// src/lib/manager/kanban/nudge-state-cache.ts
|
|
6419
|
-
import { existsSync as
|
|
6420
|
-
import { join as
|
|
6522
|
+
import { existsSync as existsSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync11 } from "fs";
|
|
6523
|
+
import { join as join31 } from "path";
|
|
6421
6524
|
var CACHE_FILENAME2 = "kanban-nudge-state.json";
|
|
6422
6525
|
var KANBAN_NUDGE_STATE_VERSION = 1;
|
|
6423
6526
|
function getKanbanNudgeStateFile(configDir) {
|
|
6424
|
-
return
|
|
6527
|
+
return join31(configDir, CACHE_FILENAME2);
|
|
6425
6528
|
}
|
|
6426
6529
|
function loadKanbanNudgeState(target, configDir) {
|
|
6427
6530
|
const path = getKanbanNudgeStateFile(configDir);
|
|
6428
|
-
if (!
|
|
6531
|
+
if (!existsSync13(path)) return;
|
|
6429
6532
|
let parsed;
|
|
6430
6533
|
try {
|
|
6431
|
-
parsed = JSON.parse(
|
|
6534
|
+
parsed = JSON.parse(readFileSync23(path, "utf-8"));
|
|
6432
6535
|
} catch {
|
|
6433
6536
|
return;
|
|
6434
6537
|
}
|
|
@@ -6460,13 +6563,13 @@ function saveKanbanNudgeState(source, configDir) {
|
|
|
6460
6563
|
const agents = {};
|
|
6461
6564
|
for (const [codeName, state9] of source) agents[codeName] = state9;
|
|
6462
6565
|
try {
|
|
6463
|
-
|
|
6566
|
+
writeFileSync11(path, JSON.stringify({ version: KANBAN_NUDGE_STATE_VERSION, agents }, null, 2));
|
|
6464
6567
|
} catch {
|
|
6465
6568
|
}
|
|
6466
6569
|
}
|
|
6467
6570
|
|
|
6468
6571
|
// src/lib/manager/kanban/notify.ts
|
|
6469
|
-
import { createHash as
|
|
6572
|
+
import { createHash as createHash10 } from "crypto";
|
|
6470
6573
|
async function enqueueKanbanNotice(opts) {
|
|
6471
6574
|
try {
|
|
6472
6575
|
const res = await api.post(
|
|
@@ -6481,7 +6584,7 @@ async function enqueueKanbanNotice(opts) {
|
|
|
6481
6584
|
return res.ok === true;
|
|
6482
6585
|
} catch (err) {
|
|
6483
6586
|
const errText = err instanceof Error ? err.message : String(err);
|
|
6484
|
-
const errId =
|
|
6587
|
+
const errId = createHash10("sha256").update(errText).digest("hex").slice(0, 12);
|
|
6485
6588
|
log(`[kanban] notice enqueue failed for agent_id=${opts.agentId} error_id=${errId}`);
|
|
6486
6589
|
return false;
|
|
6487
6590
|
}
|
|
@@ -6496,7 +6599,7 @@ async function cancelKanbanNotice(agentId) {
|
|
|
6496
6599
|
return typeof res.cancelled === "number" ? res.cancelled : 0;
|
|
6497
6600
|
} catch (err) {
|
|
6498
6601
|
const errText = err instanceof Error ? err.message : String(err);
|
|
6499
|
-
const errId =
|
|
6602
|
+
const errId = createHash10("sha256").update(errText).digest("hex").slice(0, 12);
|
|
6500
6603
|
log(`[kanban] notice cancel failed for agent_id=${agentId} error_id=${errId}`);
|
|
6501
6604
|
return null;
|
|
6502
6605
|
}
|
|
@@ -6902,7 +7005,7 @@ function deriveScheduledTaskNotify(task) {
|
|
|
6902
7005
|
}
|
|
6903
7006
|
|
|
6904
7007
|
// src/lib/manager/scheduler/runs.ts
|
|
6905
|
-
import { createHash as
|
|
7008
|
+
import { createHash as createHash11 } from "crypto";
|
|
6906
7009
|
async function startRun(opts) {
|
|
6907
7010
|
try {
|
|
6908
7011
|
const res = await api.post(
|
|
@@ -6917,7 +7020,7 @@ async function startRun(opts) {
|
|
|
6917
7020
|
};
|
|
6918
7021
|
} catch (err) {
|
|
6919
7022
|
const errText = err instanceof Error ? err.message : String(err);
|
|
6920
|
-
const errId =
|
|
7023
|
+
const errId = createHash11("sha256").update(errText).digest("hex").slice(0, 12);
|
|
6921
7024
|
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
6922
7025
|
return { run_id: null, kanban_item_id: null };
|
|
6923
7026
|
}
|
|
@@ -6956,7 +7059,7 @@ async function finishRun(runId, outcome, options = {}) {
|
|
|
6956
7059
|
return;
|
|
6957
7060
|
} catch (err) {
|
|
6958
7061
|
const errText = err instanceof Error ? err.message : String(err);
|
|
6959
|
-
const errId =
|
|
7062
|
+
const errId = createHash11("sha256").update(errText).digest("hex").slice(0, 12);
|
|
6960
7063
|
const status = err instanceof ApiError ? err.status : 0;
|
|
6961
7064
|
if (isRetryableFinishError(err) && attempt < maxRetries) {
|
|
6962
7065
|
log(
|
|
@@ -6985,7 +7088,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
|
6985
7088
|
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
6986
7089
|
} catch (err) {
|
|
6987
7090
|
const errText = err instanceof Error ? err.message : String(err);
|
|
6988
|
-
const errId =
|
|
7091
|
+
const errId = createHash11("sha256").update(errText).digest("hex").slice(0, 12);
|
|
6989
7092
|
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
6990
7093
|
return [];
|
|
6991
7094
|
}
|
|
@@ -7045,13 +7148,13 @@ function closeSessionRunForCode(codeName, outcome, reason) {
|
|
|
7045
7148
|
}
|
|
7046
7149
|
|
|
7047
7150
|
// src/lib/manager/scheduler/kanban-route.ts
|
|
7048
|
-
import { createHash as
|
|
7049
|
-
import { writeFileSync as
|
|
7151
|
+
import { createHash as createHash13 } from "crypto";
|
|
7152
|
+
import { writeFileSync as writeFileSync12, renameSync as renameSync6, mkdirSync as mkdirSync8, readFileSync as readFileSync24, unlinkSync as unlinkSync3 } from "fs";
|
|
7050
7153
|
import { homedir as homedir14 } from "os";
|
|
7051
|
-
import { join as
|
|
7154
|
+
import { join as join32, dirname as dirname8 } from "path";
|
|
7052
7155
|
|
|
7053
7156
|
// src/lib/manager/scheduler/notify.ts
|
|
7054
|
-
import { createHash as
|
|
7157
|
+
import { createHash as createHash12 } from "crypto";
|
|
7055
7158
|
async function enqueueScheduledTaskNotice(opts) {
|
|
7056
7159
|
try {
|
|
7057
7160
|
const res = await api.post(
|
|
@@ -7068,7 +7171,7 @@ async function enqueueScheduledTaskNotice(opts) {
|
|
|
7068
7171
|
return res.ok === true;
|
|
7069
7172
|
} catch (err) {
|
|
7070
7173
|
const errText = err instanceof Error ? err.message : String(err);
|
|
7071
|
-
const errId =
|
|
7174
|
+
const errId = createHash12("sha256").update(errText).digest("hex").slice(0, 12);
|
|
7072
7175
|
log(
|
|
7073
7176
|
`[scheduled-kanban] notice enqueue failed for agent_id=${opts.agentId} task_id=${opts.taskId} error_id=${errId}`
|
|
7074
7177
|
);
|
|
@@ -7407,19 +7510,19 @@ function resolveScheduledSlackTarget(task) {
|
|
|
7407
7510
|
}
|
|
7408
7511
|
function stampScheduledTurnMarker(codeName, taskId, target) {
|
|
7409
7512
|
try {
|
|
7410
|
-
const file =
|
|
7513
|
+
const file = join32(homedir14(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
|
|
7411
7514
|
const marker = { ts: Date.now(), task_id: taskId, ...target ? { target } : {} };
|
|
7412
7515
|
const tmp = `${file}.tmp`;
|
|
7413
|
-
|
|
7414
|
-
|
|
7516
|
+
writeFileSync12(tmp, JSON.stringify(marker), "utf8");
|
|
7517
|
+
renameSync6(tmp, file);
|
|
7415
7518
|
} catch (err) {
|
|
7416
7519
|
log(`[scheduled-kanban] scheduled-turn marker write failed for '${codeName}': ${err.message}`);
|
|
7417
7520
|
}
|
|
7418
7521
|
}
|
|
7419
7522
|
function clearScheduledTurnMarkerForTask(codeName, taskId) {
|
|
7420
|
-
const file =
|
|
7523
|
+
const file = join32(homedir14(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
|
|
7421
7524
|
try {
|
|
7422
|
-
const raw = JSON.parse(
|
|
7525
|
+
const raw = JSON.parse(readFileSync24(file, "utf8"));
|
|
7423
7526
|
if (typeof raw?.task_id !== "string" || raw.task_id !== taskId) return;
|
|
7424
7527
|
unlinkSync3(file);
|
|
7425
7528
|
log(`[scheduled-kanban] scheduled-turn marker cleared for '${codeName}' (task ${taskId} complete)`);
|
|
@@ -7480,8 +7583,8 @@ async function routeScheduledTaskViaKanban(codeName, agentId, task, prompt, dura
|
|
|
7480
7583
|
}
|
|
7481
7584
|
try {
|
|
7482
7585
|
const doorbell = directChatDoorbellPath(agentId, homedir14());
|
|
7483
|
-
|
|
7484
|
-
|
|
7586
|
+
mkdirSync8(dirname8(doorbell), { recursive: true });
|
|
7587
|
+
writeFileSync12(doorbell, String(Date.now()));
|
|
7485
7588
|
} catch (err) {
|
|
7486
7589
|
log(`[scheduled-kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
7487
7590
|
}
|
|
@@ -7547,24 +7650,24 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
7547
7650
|
const assertion = parseDeliverAssertion(rawOutput);
|
|
7548
7651
|
if (!assertion.deliver) {
|
|
7549
7652
|
const trimmed = (rawOutput ?? "").trim();
|
|
7550
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
7653
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash13("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
7551
7654
|
log(`[claude-scheduler] Suppressed by delivery_policy=conditional for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 ${assertion.vacuous ? "deliver marker had a vacuous reason" : "no deliver marker"}; output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
7552
7655
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
7553
7656
|
await reportDeliveryStatus(agentId, delivery.taskId, { status: "skipped", error_code: "SUPPRESSED_BY_POLICY" });
|
|
7554
7657
|
}
|
|
7555
7658
|
return { ok: true };
|
|
7556
7659
|
}
|
|
7557
|
-
const reasonHash =
|
|
7660
|
+
const reasonHash = createHash13("sha256").update(assertion.reason ?? "").digest("hex").slice(0, 12);
|
|
7558
7661
|
log(`[claude-scheduler] Delivering conditional run for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent asserted a concrete trigger (reason_len=${(assertion.reason ?? "").length} reason_hash=${reasonHash})`);
|
|
7559
7662
|
rawOutput = assertion.deliverable;
|
|
7560
7663
|
}
|
|
7561
7664
|
const classification = classifyOutput(rawOutput);
|
|
7562
7665
|
if (classification.action === "suppress") {
|
|
7563
7666
|
const trimmed = (rawOutput ?? "").trim();
|
|
7564
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
7667
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash13("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
7565
7668
|
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
7566
7669
|
if (classification.suppressedNotes) {
|
|
7567
|
-
const notesHash =
|
|
7670
|
+
const notesHash = createHash13("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
7568
7671
|
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
7569
7672
|
}
|
|
7570
7673
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
@@ -7630,13 +7733,13 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
7630
7733
|
}
|
|
7631
7734
|
|
|
7632
7735
|
// src/lib/manager/scheduler/execution.ts
|
|
7633
|
-
import { createHash as
|
|
7736
|
+
import { createHash as createHash14 } from "crypto";
|
|
7634
7737
|
import { homedir as homedir15 } from "os";
|
|
7635
|
-
import { join as
|
|
7738
|
+
import { join as join34 } from "path";
|
|
7636
7739
|
|
|
7637
7740
|
// src/lib/agent-serving-probe.ts
|
|
7638
|
-
import { readFileSync as
|
|
7639
|
-
import { join as
|
|
7741
|
+
import { readFileSync as readFileSync25, readdirSync as readdirSync6, statSync as statSync6 } from "fs";
|
|
7742
|
+
import { join as join33 } from "path";
|
|
7640
7743
|
var RATE_LIMIT_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
7641
7744
|
function probeRateLimit(args) {
|
|
7642
7745
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
@@ -7645,23 +7748,23 @@ function probeRateLimit(args) {
|
|
|
7645
7748
|
const dir = args.transcriptDir ?? sessionTranscriptDir(args.projectDir);
|
|
7646
7749
|
let entries;
|
|
7647
7750
|
try {
|
|
7648
|
-
entries =
|
|
7751
|
+
entries = readdirSync6(dir);
|
|
7649
7752
|
} catch {
|
|
7650
7753
|
return UNKNOWN_RATE_LIMIT;
|
|
7651
7754
|
}
|
|
7652
7755
|
let newest = UNKNOWN_RATE_LIMIT;
|
|
7653
7756
|
for (const name of entries) {
|
|
7654
7757
|
if (!name.endsWith(".jsonl")) continue;
|
|
7655
|
-
const path =
|
|
7758
|
+
const path = join33(dir, name);
|
|
7656
7759
|
try {
|
|
7657
|
-
const st =
|
|
7760
|
+
const st = statSync6(path);
|
|
7658
7761
|
if (!st.isFile() || st.mtimeMs < startMs) continue;
|
|
7659
7762
|
} catch {
|
|
7660
7763
|
continue;
|
|
7661
7764
|
}
|
|
7662
7765
|
let content;
|
|
7663
7766
|
try {
|
|
7664
|
-
content =
|
|
7767
|
+
content = readFileSync25(path, "utf-8");
|
|
7665
7768
|
} catch {
|
|
7666
7769
|
continue;
|
|
7667
7770
|
}
|
|
@@ -7723,7 +7826,7 @@ function shouldLogUsageCapDeferral(site, codeName, limitedUntil) {
|
|
|
7723
7826
|
|
|
7724
7827
|
// src/lib/manager/scheduler/execution.ts
|
|
7725
7828
|
function claudePidFilePath() {
|
|
7726
|
-
return
|
|
7829
|
+
return join34(homedir15(), ".augmented", "manager-claude-pids.json");
|
|
7727
7830
|
}
|
|
7728
7831
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
7729
7832
|
function registerClaudeSpawn(record) {
|
|
@@ -7743,10 +7846,10 @@ function unregisterClaudeSpawn(pid) {
|
|
|
7743
7846
|
}
|
|
7744
7847
|
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData, durableScheduledNudge = false) {
|
|
7745
7848
|
const codeName = agent.code_name;
|
|
7746
|
-
const stableTasksHash =
|
|
7747
|
-
const boardHash = boardItems.length > 0 ?
|
|
7849
|
+
const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
7850
|
+
const boardHash = boardItems.length > 0 ? createHash14("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
7748
7851
|
const resolvedModels = resolveModelChain(refreshData);
|
|
7749
|
-
const modelsHash =
|
|
7852
|
+
const modelsHash = createHash14("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
7750
7853
|
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
7751
7854
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
7752
7855
|
if (combinedHash !== prevHash) {
|
|
@@ -7793,8 +7896,8 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
7793
7896
|
}
|
|
7794
7897
|
|
|
7795
7898
|
// src/lib/occupancy-gate.ts
|
|
7796
|
-
import { closeSync as closeSync2, openSync as openSync2, readdirSync as
|
|
7797
|
-
import { join as
|
|
7899
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync7, readSync as readSync2, statSync as statSync7 } from "fs";
|
|
7900
|
+
import { join as join35 } from "path";
|
|
7798
7901
|
function rostersMeasuredZero(mode, attested, runtimeRunning) {
|
|
7799
7902
|
return mode === "enforce" && attested && runtimeRunning;
|
|
7800
7903
|
}
|
|
@@ -7808,7 +7911,7 @@ var brackets = /* @__PURE__ */ new Map();
|
|
|
7808
7911
|
function readAlignedTail(path, tailBytes, maxAlignBytes) {
|
|
7809
7912
|
let fd = null;
|
|
7810
7913
|
try {
|
|
7811
|
-
const size =
|
|
7914
|
+
const size = statSync7(path).size;
|
|
7812
7915
|
if (size <= 0) return { content: "", complete: true, truncated: false };
|
|
7813
7916
|
fd = openSync2(path, "r");
|
|
7814
7917
|
let readStart = Math.max(0, size - tailBytes);
|
|
@@ -7878,26 +7981,26 @@ function candidateTranscriptPaths(dir) {
|
|
|
7878
7981
|
const paths = [];
|
|
7879
7982
|
let top;
|
|
7880
7983
|
try {
|
|
7881
|
-
top =
|
|
7984
|
+
top = readdirSync7(dir);
|
|
7882
7985
|
} catch {
|
|
7883
7986
|
return { paths, complete: false };
|
|
7884
7987
|
}
|
|
7885
7988
|
let complete = true;
|
|
7886
7989
|
for (const name of top) {
|
|
7887
7990
|
if (name.endsWith(".jsonl")) {
|
|
7888
|
-
paths.push(
|
|
7991
|
+
paths.push(join35(dir, name));
|
|
7889
7992
|
continue;
|
|
7890
7993
|
}
|
|
7891
|
-
const subDir =
|
|
7994
|
+
const subDir = join35(dir, name, "subagents");
|
|
7892
7995
|
let subs;
|
|
7893
7996
|
try {
|
|
7894
|
-
subs =
|
|
7997
|
+
subs = readdirSync7(subDir);
|
|
7895
7998
|
} catch (err) {
|
|
7896
7999
|
if (!isAbsentDirError(err)) complete = false;
|
|
7897
8000
|
continue;
|
|
7898
8001
|
}
|
|
7899
8002
|
for (const sub of subs) {
|
|
7900
|
-
if (sub.endsWith(".jsonl")) paths.push(
|
|
8003
|
+
if (sub.endsWith(".jsonl")) paths.push(join35(subDir, sub));
|
|
7901
8004
|
}
|
|
7902
8005
|
}
|
|
7903
8006
|
return { paths, complete };
|
|
@@ -7952,7 +8055,7 @@ function collectQualifyingTurns(codeName, nowMs, opts = {}) {
|
|
|
7952
8055
|
for (const path of paths) {
|
|
7953
8056
|
let st;
|
|
7954
8057
|
try {
|
|
7955
|
-
st =
|
|
8058
|
+
st = statSync7(path);
|
|
7956
8059
|
} catch (err) {
|
|
7957
8060
|
if (!isAbsentDirError(err)) complete = false;
|
|
7958
8061
|
continue;
|
|
@@ -8058,7 +8161,7 @@ function qualifyBuckets(buckets, qualifying, mode, nowMs, neighbourhoodMs = DEFA
|
|
|
8058
8161
|
}
|
|
8059
8162
|
|
|
8060
8163
|
// src/lib/pane-occupancy-sampler.ts
|
|
8061
|
-
import { statSync as
|
|
8164
|
+
import { statSync as statSync8 } from "fs";
|
|
8062
8165
|
var SAMPLE_INTERVAL_MS = 1e4;
|
|
8063
8166
|
var IDLE_GAP_MS = 12e4;
|
|
8064
8167
|
var POST_IDLE_CREDIT_MS = 6e4;
|
|
@@ -8072,7 +8175,7 @@ function hasFreshPaneObservation(codeName, nowMs, maxAgeMs = OBSERVATION_FRESH_M
|
|
|
8072
8175
|
}
|
|
8073
8176
|
function paneMtimeMs(codeName) {
|
|
8074
8177
|
try {
|
|
8075
|
-
return
|
|
8178
|
+
return statSync8(paneLogPath(codeName)).mtimeMs;
|
|
8076
8179
|
} catch {
|
|
8077
8180
|
return null;
|
|
8078
8181
|
}
|
|
@@ -8124,7 +8227,7 @@ function stopPaneOccupancySampler() {
|
|
|
8124
8227
|
|
|
8125
8228
|
// src/lib/pid-pressure-sampler.ts
|
|
8126
8229
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
8127
|
-
import { existsSync as
|
|
8230
|
+
import { existsSync as existsSync14, readFileSync as readFileSync26 } from "fs";
|
|
8128
8231
|
var SAMPLE_INTERVAL_MS2 = 3e4;
|
|
8129
8232
|
function warnFraction() {
|
|
8130
8233
|
const raw = Number(process.env.AGT_PID_PRESSURE_WARN_FRACTION);
|
|
@@ -8137,7 +8240,7 @@ function configuredCeiling() {
|
|
|
8137
8240
|
}
|
|
8138
8241
|
function readTextReal(path) {
|
|
8139
8242
|
try {
|
|
8140
|
-
return
|
|
8243
|
+
return readFileSync26(path, "utf-8");
|
|
8141
8244
|
} catch {
|
|
8142
8245
|
return null;
|
|
8143
8246
|
}
|
|
@@ -8146,7 +8249,7 @@ var cgroupDirCache = /* @__PURE__ */ new Map();
|
|
|
8146
8249
|
var resolveRetryAfter = /* @__PURE__ */ new Map();
|
|
8147
8250
|
var RESOLVE_RETRY_BACKOFF_MS = 5 * 6e4;
|
|
8148
8251
|
function resolveCgroupDirReal(codeName, deps = {}) {
|
|
8149
|
-
const exists = deps.exists ??
|
|
8252
|
+
const exists = deps.exists ?? existsSync14;
|
|
8150
8253
|
const inspectId = deps.inspectId ?? inspectContainerId;
|
|
8151
8254
|
const now = deps.now ?? Date.now;
|
|
8152
8255
|
const cached = cgroupDirCache.get(codeName);
|
|
@@ -8264,7 +8367,7 @@ function stopPidPressureSampler() {
|
|
|
8264
8367
|
}
|
|
8265
8368
|
|
|
8266
8369
|
// src/lib/opencode-slack-ingest.ts
|
|
8267
|
-
import { createHash as
|
|
8370
|
+
import { createHash as createHash15 } from "crypto";
|
|
8268
8371
|
|
|
8269
8372
|
// ../../packages/core/dist/channels/slack-rich-text.js
|
|
8270
8373
|
var MAX_DEPTH = 12;
|
|
@@ -9271,7 +9374,7 @@ function startSlackIngest(config2) {
|
|
|
9271
9374
|
// src/lib/opencode-slack-ingest.ts
|
|
9272
9375
|
var ingests = /* @__PURE__ */ new Map();
|
|
9273
9376
|
function fingerprint(params) {
|
|
9274
|
-
return
|
|
9377
|
+
return createHash15("sha256").update(
|
|
9275
9378
|
JSON.stringify({
|
|
9276
9379
|
appToken: params.appToken,
|
|
9277
9380
|
botToken: params.botToken,
|
|
@@ -9352,7 +9455,7 @@ function stopAllOpencodeSlackIngests(log2) {
|
|
|
9352
9455
|
}
|
|
9353
9456
|
|
|
9354
9457
|
// src/lib/manager/opencode-scheduler.ts
|
|
9355
|
-
import { createHash as
|
|
9458
|
+
import { createHash as createHash16 } from "crypto";
|
|
9356
9459
|
var MAX_OPENCODE_SCHED_CONCURRENCY = 2;
|
|
9357
9460
|
var opencodeSchedulerStates = /* @__PURE__ */ new Map();
|
|
9358
9461
|
var inFlightOpencodeTasks = /* @__PURE__ */ new Set();
|
|
@@ -9366,7 +9469,7 @@ function shouldDeliver(task, reply) {
|
|
|
9366
9469
|
}
|
|
9367
9470
|
async function syncAndCheckOpencodeScheduler(agent, tasks, refreshData) {
|
|
9368
9471
|
const codeName = agent.code_name;
|
|
9369
|
-
const tasksHash =
|
|
9472
|
+
const tasksHash = createHash16("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
9370
9473
|
if (knownOpencodeTaskHashes.get(agent.agent_id) !== tasksHash) {
|
|
9371
9474
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
9372
9475
|
const state10 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
@@ -9444,10 +9547,10 @@ async function fireOpencodeScheduledTask(agent, task) {
|
|
|
9444
9547
|
}
|
|
9445
9548
|
|
|
9446
9549
|
// src/lib/opencode-telegram-ingest.ts
|
|
9447
|
-
import { createHash as
|
|
9448
|
-
import { existsSync as
|
|
9550
|
+
import { createHash as createHash17 } from "crypto";
|
|
9551
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync27, renameSync as renameSync7, unlinkSync as unlinkSync4, writeFileSync as writeFileSync13 } from "fs";
|
|
9449
9552
|
import { randomUUID } from "crypto";
|
|
9450
|
-
import { join as
|
|
9553
|
+
import { join as join36 } from "path";
|
|
9451
9554
|
|
|
9452
9555
|
// src/lib/telegram-ingest.ts
|
|
9453
9556
|
import https2 from "https";
|
|
@@ -9980,7 +10083,7 @@ function anySignal(signals) {
|
|
|
9980
10083
|
// src/lib/opencode-telegram-ingest.ts
|
|
9981
10084
|
var ingests2 = /* @__PURE__ */ new Map();
|
|
9982
10085
|
function fingerprint2(params) {
|
|
9983
|
-
return
|
|
10086
|
+
return createHash17("sha256").update(
|
|
9984
10087
|
JSON.stringify({
|
|
9985
10088
|
botToken: params.botToken,
|
|
9986
10089
|
allowedChats: [...params.allowedChats].sort(),
|
|
@@ -9995,7 +10098,7 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
9995
10098
|
let filePath;
|
|
9996
10099
|
try {
|
|
9997
10100
|
dir = getFramework("opencode").getAgentDir(codeName);
|
|
9998
|
-
filePath =
|
|
10101
|
+
filePath = join36(dir, "telegram-getupdates-offset-opencode.json");
|
|
9999
10102
|
} catch {
|
|
10000
10103
|
dir = null;
|
|
10001
10104
|
filePath = null;
|
|
@@ -10004,7 +10107,7 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
10004
10107
|
load() {
|
|
10005
10108
|
if (!filePath) return 0;
|
|
10006
10109
|
try {
|
|
10007
|
-
const parsed = JSON.parse(
|
|
10110
|
+
const parsed = JSON.parse(readFileSync27(filePath, "utf-8"));
|
|
10008
10111
|
if (currentBotId != null && typeof parsed?.bot_id === "number" && parsed.bot_id !== currentBotId) {
|
|
10009
10112
|
log2(`[telegram-ingest:${codeName}] offset cursor belongs to a different bot; ignoring (bot swap)`);
|
|
10010
10113
|
return 0;
|
|
@@ -10022,8 +10125,8 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
10022
10125
|
if (!filePath || !dir) return;
|
|
10023
10126
|
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
10024
10127
|
try {
|
|
10025
|
-
|
|
10026
|
-
|
|
10128
|
+
mkdirSync9(dir, { recursive: true, mode: 448 });
|
|
10129
|
+
writeFileSync13(
|
|
10027
10130
|
tmpPath,
|
|
10028
10131
|
JSON.stringify({
|
|
10029
10132
|
offset,
|
|
@@ -10033,11 +10136,11 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
10033
10136
|
}),
|
|
10034
10137
|
{ mode: 384 }
|
|
10035
10138
|
);
|
|
10036
|
-
|
|
10139
|
+
renameSync7(tmpPath, filePath);
|
|
10037
10140
|
} catch (err) {
|
|
10038
10141
|
log2(`[telegram-ingest:${codeName}] offset persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
10039
10142
|
try {
|
|
10040
|
-
if (
|
|
10143
|
+
if (existsSync15(tmpPath)) unlinkSync4(tmpPath);
|
|
10041
10144
|
} catch {
|
|
10042
10145
|
}
|
|
10043
10146
|
}
|
|
@@ -10274,24 +10377,24 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
10274
10377
|
}
|
|
10275
10378
|
|
|
10276
10379
|
// src/lib/restart-flags.ts
|
|
10277
|
-
import { existsSync as
|
|
10380
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readdirSync as readdirSync8, readFileSync as readFileSync28, renameSync as renameSync8, rmSync as rmSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
10278
10381
|
import { homedir as homedir16 } from "os";
|
|
10279
|
-
import { join as
|
|
10382
|
+
import { join as join37 } from "path";
|
|
10280
10383
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
10281
10384
|
function restartFlagsDir() {
|
|
10282
|
-
return
|
|
10385
|
+
return join37(homedir16(), ".augmented", "restart-flags");
|
|
10283
10386
|
}
|
|
10284
10387
|
function flagPath(codeName) {
|
|
10285
|
-
return
|
|
10388
|
+
return join37(restartFlagsDir(), `${codeName}.flag`);
|
|
10286
10389
|
}
|
|
10287
10390
|
function readRestartFlags() {
|
|
10288
10391
|
const dir = restartFlagsDir();
|
|
10289
|
-
if (!
|
|
10392
|
+
if (!existsSync16(dir)) return [];
|
|
10290
10393
|
const out = [];
|
|
10291
|
-
for (const entry of
|
|
10394
|
+
for (const entry of readdirSync8(dir)) {
|
|
10292
10395
|
if (!entry.endsWith(".flag")) continue;
|
|
10293
10396
|
try {
|
|
10294
|
-
const raw =
|
|
10397
|
+
const raw = readFileSync28(join37(dir, entry), "utf8");
|
|
10295
10398
|
const parsed = JSON.parse(raw);
|
|
10296
10399
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
10297
10400
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -10309,8 +10412,8 @@ function readRestartFlags() {
|
|
|
10309
10412
|
}
|
|
10310
10413
|
function deleteRestartFlag(codeName) {
|
|
10311
10414
|
const path = flagPath(codeName);
|
|
10312
|
-
if (
|
|
10313
|
-
|
|
10415
|
+
if (existsSync16(path)) {
|
|
10416
|
+
rmSync5(path, { force: true });
|
|
10314
10417
|
}
|
|
10315
10418
|
}
|
|
10316
10419
|
|
|
@@ -10409,8 +10512,8 @@ async function sendError(flag, opts, text) {
|
|
|
10409
10512
|
}
|
|
10410
10513
|
|
|
10411
10514
|
// src/lib/restart-context.ts
|
|
10412
|
-
import { readdirSync as
|
|
10413
|
-
import { dirname as dirname9, join as
|
|
10515
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync29, writeFileSync as writeFileSync15, mkdirSync as mkdirSync11, unlinkSync as unlinkSync5 } from "fs";
|
|
10516
|
+
import { dirname as dirname9, join as join38 } from "path";
|
|
10414
10517
|
var SLACK_PENDING_INBOUND_DIRNAME = "slack-pending-inbound";
|
|
10415
10518
|
var SLACK_RESTART_CONTEXT_DIRNAME = "slack-restart-context";
|
|
10416
10519
|
var MAX_TOPIC_CHARS = 140;
|
|
@@ -10422,10 +10525,10 @@ function augmentedAgentDir(codeName) {
|
|
|
10422
10525
|
return dirname9(getProjectDir(codeName));
|
|
10423
10526
|
}
|
|
10424
10527
|
function slackPendingInboundDir(codeName) {
|
|
10425
|
-
return
|
|
10528
|
+
return join38(augmentedAgentDir(codeName), SLACK_PENDING_INBOUND_DIRNAME);
|
|
10426
10529
|
}
|
|
10427
10530
|
function slackRestartContextDir(codeName) {
|
|
10428
|
-
return
|
|
10531
|
+
return join38(augmentedAgentDir(codeName), SLACK_RESTART_CONTEXT_DIRNAME);
|
|
10429
10532
|
}
|
|
10430
10533
|
function sanitizeTopic(raw) {
|
|
10431
10534
|
const cleaned = raw.replace(/\s+/g, " ").trim().replace(/[<>]/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -10460,14 +10563,14 @@ function computeRestartContextHints(markers, allTurns, nowMs, reconstruct = reco
|
|
|
10460
10563
|
}
|
|
10461
10564
|
function safeReaddir(dir) {
|
|
10462
10565
|
try {
|
|
10463
|
-
return
|
|
10566
|
+
return readdirSync9(dir);
|
|
10464
10567
|
} catch {
|
|
10465
10568
|
return [];
|
|
10466
10569
|
}
|
|
10467
10570
|
}
|
|
10468
10571
|
function readStrandedMarker(path) {
|
|
10469
10572
|
try {
|
|
10470
|
-
const parsed = JSON.parse(
|
|
10573
|
+
const parsed = JSON.parse(readFileSync29(path, "utf-8"));
|
|
10471
10574
|
if (typeof parsed.channel === "string" && typeof parsed.thread_ts === "string") {
|
|
10472
10575
|
return { channel: parsed.channel, thread_ts: parsed.thread_ts };
|
|
10473
10576
|
}
|
|
@@ -10476,8 +10579,8 @@ function readStrandedMarker(path) {
|
|
|
10476
10579
|
return null;
|
|
10477
10580
|
}
|
|
10478
10581
|
function writeHintFile(path, dir, hint) {
|
|
10479
|
-
|
|
10480
|
-
|
|
10582
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
10583
|
+
writeFileSync15(path, JSON.stringify(hint), { mode: 384 });
|
|
10481
10584
|
}
|
|
10482
10585
|
function pruneHintsExcept(codeName, freshFilenames) {
|
|
10483
10586
|
const ctxDir = slackRestartContextDir(codeName);
|
|
@@ -10485,7 +10588,7 @@ function pruneHintsExcept(codeName, freshFilenames) {
|
|
|
10485
10588
|
if (!filename.endsWith(".json")) continue;
|
|
10486
10589
|
if (freshFilenames.has(filename)) continue;
|
|
10487
10590
|
try {
|
|
10488
|
-
unlinkSync5(
|
|
10591
|
+
unlinkSync5(join38(ctxDir, filename));
|
|
10489
10592
|
} catch {
|
|
10490
10593
|
}
|
|
10491
10594
|
}
|
|
@@ -10506,7 +10609,7 @@ function refreshSlackRestartContextHints(codeNames, opts = {}) {
|
|
|
10506
10609
|
}
|
|
10507
10610
|
const markers = [];
|
|
10508
10611
|
for (const filename of markerFilenames.slice(0, cap)) {
|
|
10509
|
-
const parsed = readStrandedMarker(
|
|
10612
|
+
const parsed = readStrandedMarker(join38(markerDir, filename));
|
|
10510
10613
|
if (parsed) markers.push({ filename, channel: parsed.channel, thread_ts: parsed.thread_ts });
|
|
10511
10614
|
}
|
|
10512
10615
|
if (markers.length === 0) {
|
|
@@ -10520,7 +10623,7 @@ function refreshSlackRestartContextHints(codeNames, opts = {}) {
|
|
|
10520
10623
|
const freshFilenames = /* @__PURE__ */ new Set();
|
|
10521
10624
|
for (const { filename, hint } of hints) {
|
|
10522
10625
|
try {
|
|
10523
|
-
writeHintFile(
|
|
10626
|
+
writeHintFile(join38(ctxDir, filename), ctxDir, hint);
|
|
10524
10627
|
freshFilenames.add(filename);
|
|
10525
10628
|
} catch (err) {
|
|
10526
10629
|
log2(`[restart-context] ${codeName}: hint write failed for ${filename}: ${err.message}`);
|
|
@@ -10538,13 +10641,58 @@ function refreshSlackRestartContextHints(codeNames, opts = {}) {
|
|
|
10538
10641
|
|
|
10539
10642
|
// src/lib/persistent-session-stuck-tracker.ts
|
|
10540
10643
|
var DEFAULT_STUCK_WARN_THRESHOLD = 3;
|
|
10644
|
+
var DEFAULT_SPAWN_LOOP_WARN_THRESHOLD = 3;
|
|
10541
10645
|
var PersistentSessionStuckTracker = class {
|
|
10542
10646
|
counts = /* @__PURE__ */ new Map();
|
|
10543
10647
|
warned = /* @__PURE__ */ new Set();
|
|
10544
10648
|
threshold;
|
|
10545
|
-
|
|
10649
|
+
/**
|
|
10650
|
+
* ENG-9614 — the second streak, held separately because it answers a
|
|
10651
|
+
* DIFFERENT question and must not inherit the first one's reset rule.
|
|
10652
|
+
*
|
|
10653
|
+
* `counts` above resets whenever `spawnAttempted` is true, which is correct
|
|
10654
|
+
* for its own question (ENG-5116/Vigil: unhealthy and nobody even TRIED).
|
|
10655
|
+
* But it makes the tracker structurally blind to the inverse: the scratch/tmp
|
|
10656
|
+
* collision attempted a spawn on EVERY tick, so `counts` was deleted on every
|
|
10657
|
+
* tick, `shouldWarn` could never fire, and an agent looped unrecoverably for
|
|
10658
|
+
* 3m48s without this class emitting a single line.
|
|
10659
|
+
*
|
|
10660
|
+
* Folding both into one counter was considered and rejected - a shared
|
|
10661
|
+
* counter would have to pick one reset rule, and either choice makes the
|
|
10662
|
+
* other question unanswerable. Two questions, two counters.
|
|
10663
|
+
*/
|
|
10664
|
+
spawnLoopCounts = /* @__PURE__ */ new Map();
|
|
10665
|
+
spawnLoopWarned = /* @__PURE__ */ new Set();
|
|
10666
|
+
spawnLoopThreshold;
|
|
10667
|
+
constructor(threshold = DEFAULT_STUCK_WARN_THRESHOLD, spawnLoopThreshold = DEFAULT_SPAWN_LOOP_WARN_THRESHOLD) {
|
|
10546
10668
|
if (threshold < 1) throw new Error("stuck-tracker threshold must be >= 1");
|
|
10669
|
+
if (spawnLoopThreshold < 1) throw new Error("stuck-tracker spawn-loop threshold must be >= 1");
|
|
10547
10670
|
this.threshold = threshold;
|
|
10671
|
+
this.spawnLoopThreshold = spawnLoopThreshold;
|
|
10672
|
+
}
|
|
10673
|
+
/**
|
|
10674
|
+
* Advance the spawn-loop streak for one tick.
|
|
10675
|
+
*
|
|
10676
|
+
* Counts only when a spawn was attempted AND the session is still unhealthy.
|
|
10677
|
+
* Health alone resets it - an agent that came up is not looping, however it
|
|
10678
|
+
* got there. A tick with no spawn attempt neither counts nor resets: that is
|
|
10679
|
+
* the OTHER failure, already owned by `counts`, and letting it clear this
|
|
10680
|
+
* streak would let the two shapes alternate and hide each other.
|
|
10681
|
+
*/
|
|
10682
|
+
recordSpawnLoop(codeName, sessionHealthy, spawnAttempted) {
|
|
10683
|
+
if (sessionHealthy) {
|
|
10684
|
+
this.spawnLoopCounts.delete(codeName);
|
|
10685
|
+
this.spawnLoopWarned.delete(codeName);
|
|
10686
|
+
return { ticks: 0, shouldWarn: false };
|
|
10687
|
+
}
|
|
10688
|
+
if (!spawnAttempted) {
|
|
10689
|
+
return { ticks: this.spawnLoopCounts.get(codeName) ?? 0, shouldWarn: false };
|
|
10690
|
+
}
|
|
10691
|
+
const next = (this.spawnLoopCounts.get(codeName) ?? 0) + 1;
|
|
10692
|
+
this.spawnLoopCounts.set(codeName, next);
|
|
10693
|
+
const crossed = next >= this.spawnLoopThreshold && !this.spawnLoopWarned.has(codeName);
|
|
10694
|
+
if (crossed) this.spawnLoopWarned.add(codeName);
|
|
10695
|
+
return { ticks: next, shouldWarn: crossed };
|
|
10548
10696
|
}
|
|
10549
10697
|
/**
|
|
10550
10698
|
* Record one tick for one agent. Returns whether the caller should log
|
|
@@ -10555,6 +10703,7 @@ var PersistentSessionStuckTracker = class {
|
|
|
10555
10703
|
*/
|
|
10556
10704
|
record(input) {
|
|
10557
10705
|
const { codeName, sessionHealthy, spawnAttempted } = input;
|
|
10706
|
+
const spawnLoop = this.recordSpawnLoop(codeName, sessionHealthy, spawnAttempted);
|
|
10558
10707
|
const wasStuck = (this.counts.get(codeName) ?? 0) >= this.threshold;
|
|
10559
10708
|
if (sessionHealthy || spawnAttempted) {
|
|
10560
10709
|
this.counts.delete(codeName);
|
|
@@ -10562,7 +10711,9 @@ var PersistentSessionStuckTracker = class {
|
|
|
10562
10711
|
return {
|
|
10563
10712
|
consecutiveTicks: 0,
|
|
10564
10713
|
shouldWarn: false,
|
|
10565
|
-
recovered: wasStuck && wasWarned
|
|
10714
|
+
recovered: wasStuck && wasWarned,
|
|
10715
|
+
spawnLoopTicks: spawnLoop.ticks,
|
|
10716
|
+
shouldWarnSpawnLoop: spawnLoop.shouldWarn
|
|
10566
10717
|
};
|
|
10567
10718
|
}
|
|
10568
10719
|
const next = (this.counts.get(codeName) ?? 0) + 1;
|
|
@@ -10572,17 +10723,25 @@ var PersistentSessionStuckTracker = class {
|
|
|
10572
10723
|
return {
|
|
10573
10724
|
consecutiveTicks: next,
|
|
10574
10725
|
shouldWarn: crossed,
|
|
10575
|
-
recovered: false
|
|
10726
|
+
recovered: false,
|
|
10727
|
+
spawnLoopTicks: spawnLoop.ticks,
|
|
10728
|
+
shouldWarnSpawnLoop: spawnLoop.shouldWarn
|
|
10576
10729
|
};
|
|
10577
10730
|
}
|
|
10578
10731
|
/** Test/debug helper: peek at an agent's current streak. */
|
|
10579
10732
|
peek(codeName) {
|
|
10580
10733
|
return this.counts.get(codeName) ?? 0;
|
|
10581
10734
|
}
|
|
10735
|
+
/** Test/debug helper: peek at an agent's current spawn-loop streak. */
|
|
10736
|
+
peekSpawnLoop(codeName) {
|
|
10737
|
+
return this.spawnLoopCounts.get(codeName) ?? 0;
|
|
10738
|
+
}
|
|
10582
10739
|
/** Test helper: clear all state. */
|
|
10583
10740
|
reset() {
|
|
10584
10741
|
this.counts.clear();
|
|
10585
10742
|
this.warned.clear();
|
|
10743
|
+
this.spawnLoopCounts.clear();
|
|
10744
|
+
this.spawnLoopWarned.clear();
|
|
10586
10745
|
}
|
|
10587
10746
|
};
|
|
10588
10747
|
|
|
@@ -11898,7 +12057,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
|
|
|
11898
12057
|
var dayRolloverInboundHold = /* @__PURE__ */ new Map();
|
|
11899
12058
|
var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
|
|
11900
12059
|
async function channelInboundActivityAgeSecondsFor(codeName) {
|
|
11901
|
-
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-
|
|
12060
|
+
const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-JQS4WA2L.js");
|
|
11902
12061
|
const newest = newestPendingInboundActivityMtimeMs(dirname10(paneLogPath(codeName)));
|
|
11903
12062
|
if (newest === null) return null;
|
|
11904
12063
|
return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
|
|
@@ -11998,7 +12157,7 @@ function performLazyDayRolloverReset(codeName, agentTimezone) {
|
|
|
11998
12157
|
}
|
|
11999
12158
|
function paneLogAgeSecondsFor(codeName) {
|
|
12000
12159
|
try {
|
|
12001
|
-
const mtimeMs =
|
|
12160
|
+
const mtimeMs = statSync9(paneLogPath(codeName)).mtimeMs;
|
|
12002
12161
|
return Math.max(0, Math.floor((Date.now() - mtimeMs) / 1e3));
|
|
12003
12162
|
} catch (err) {
|
|
12004
12163
|
if (err?.code === "ENOENT") return null;
|
|
@@ -12148,15 +12307,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
|
12148
12307
|
var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
|
|
12149
12308
|
function projectMcpHash(_codeName, projectDir) {
|
|
12150
12309
|
try {
|
|
12151
|
-
const raw =
|
|
12152
|
-
return
|
|
12310
|
+
const raw = readFileSync30(join39(projectDir, ".mcp.json"), "utf-8");
|
|
12311
|
+
return createHash18("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
12153
12312
|
} catch {
|
|
12154
12313
|
return null;
|
|
12155
12314
|
}
|
|
12156
12315
|
}
|
|
12157
12316
|
function projectMcpKeys(_codeName, projectDir) {
|
|
12158
12317
|
try {
|
|
12159
|
-
const raw =
|
|
12318
|
+
const raw = readFileSync30(join39(projectDir, ".mcp.json"), "utf-8");
|
|
12160
12319
|
const parsed = JSON.parse(raw);
|
|
12161
12320
|
const servers = parsed.mcpServers;
|
|
12162
12321
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -12174,7 +12333,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
|
|
|
12174
12333
|
else runningMcpServerKeys.delete(codeName);
|
|
12175
12334
|
let launchStructure = null;
|
|
12176
12335
|
try {
|
|
12177
|
-
const raw =
|
|
12336
|
+
const raw = readFileSync30(join39(projectDir, ".mcp.json"), "utf-8");
|
|
12178
12337
|
launchStructure = managedMcpStructureHashFromFile(
|
|
12179
12338
|
JSON.parse(raw),
|
|
12180
12339
|
isManagedMcpServerKey
|
|
@@ -12300,7 +12459,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
|
|
|
12300
12459
|
if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
|
|
12301
12460
|
let mcpJsonForRebind = null;
|
|
12302
12461
|
try {
|
|
12303
|
-
mcpJsonForRebind = JSON.parse(
|
|
12462
|
+
mcpJsonForRebind = JSON.parse(readFileSync30(join39(projectDir, ".mcp.json"), "utf-8"));
|
|
12304
12463
|
} catch {
|
|
12305
12464
|
mcpJsonForRebind = null;
|
|
12306
12465
|
}
|
|
@@ -12448,7 +12607,7 @@ function shouldInjectChannelSecrets(agentId) {
|
|
|
12448
12607
|
function projectChannelSecretHash(projectDir) {
|
|
12449
12608
|
try {
|
|
12450
12609
|
const entries = parseEnvIntegrations(
|
|
12451
|
-
|
|
12610
|
+
readFileSync30(join39(projectDir, ".env.integrations"), "utf-8")
|
|
12452
12611
|
);
|
|
12453
12612
|
return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
|
|
12454
12613
|
} catch {
|
|
@@ -12546,7 +12705,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
12546
12705
|
var lastVersionCheckAt = 0;
|
|
12547
12706
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
12548
12707
|
var lastResponsivenessProbeAt = 0;
|
|
12549
|
-
var agtCliVersion = true ? "0.28.
|
|
12708
|
+
var agtCliVersion = true ? "0.28.718" : "dev";
|
|
12550
12709
|
function resolveBrewPath(execFileSync3) {
|
|
12551
12710
|
try {
|
|
12552
12711
|
const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -12559,7 +12718,7 @@ function resolveBrewPath(execFileSync3) {
|
|
|
12559
12718
|
"/usr/local/bin/brew"
|
|
12560
12719
|
];
|
|
12561
12720
|
for (const path of fallbacks) {
|
|
12562
|
-
if (
|
|
12721
|
+
if (existsSync17(path)) return path;
|
|
12563
12722
|
}
|
|
12564
12723
|
return null;
|
|
12565
12724
|
}
|
|
@@ -12569,7 +12728,7 @@ function claudeBinaryInstalled(execFileSync3) {
|
|
|
12569
12728
|
"/opt/homebrew/bin/claude",
|
|
12570
12729
|
"/usr/local/bin/claude"
|
|
12571
12730
|
];
|
|
12572
|
-
if (canonical.some((path) =>
|
|
12731
|
+
if (canonical.some((path) => existsSync17(path))) return true;
|
|
12573
12732
|
try {
|
|
12574
12733
|
execFileSync3("which", ["claude"], { timeout: 5e3 });
|
|
12575
12734
|
return true;
|
|
@@ -12879,8 +13038,8 @@ var MANAGED_SETTINGS_KEYS = ["channelsEnabled", "enableAllProjectMcpServers"];
|
|
|
12879
13038
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
12880
13039
|
try {
|
|
12881
13040
|
let settings = {};
|
|
12882
|
-
if (
|
|
12883
|
-
const raw =
|
|
13041
|
+
if (existsSync17(path)) {
|
|
13042
|
+
const raw = readFileSync30(path, "utf-8").trim();
|
|
12884
13043
|
if (raw) {
|
|
12885
13044
|
let parsed;
|
|
12886
13045
|
try {
|
|
@@ -12897,8 +13056,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
12897
13056
|
const missing = MANAGED_SETTINGS_KEYS.filter((key) => settings[key] !== true);
|
|
12898
13057
|
if (missing.length === 0) return "ok";
|
|
12899
13058
|
for (const key of missing) settings[key] = true;
|
|
12900
|
-
|
|
12901
|
-
|
|
13059
|
+
mkdirSync12(dirname10(path), { recursive: true });
|
|
13060
|
+
writeFileSync16(path, `${JSON.stringify(settings, null, 2)}
|
|
12902
13061
|
`);
|
|
12903
13062
|
log(`[managed-settings] set ${missing.map((k) => `${k}:true`).join(", ")} in ${path} (ENG-5786 unblocks Claude Code channels; ENG-9223 pre-approves project MCP servers so a headless session never freezes on the trust prompt)`);
|
|
12904
13063
|
return "ok";
|
|
@@ -12936,7 +13095,7 @@ async function ensureOpencodeBinary() {
|
|
|
12936
13095
|
try {
|
|
12937
13096
|
const prefix = execFileSync3("npm", ["prefix", "-g"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
12938
13097
|
if (prefix) {
|
|
12939
|
-
const npmBin =
|
|
13098
|
+
const npmBin = join39(prefix, "bin");
|
|
12940
13099
|
const current = (process.env.PATH ?? "").split(pathDelimiter);
|
|
12941
13100
|
if (!current.includes(npmBin)) {
|
|
12942
13101
|
process.env.PATH = [npmBin, ...current.filter(Boolean)].join(pathDelimiter);
|
|
@@ -12997,7 +13156,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
12997
13156
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
12998
13157
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
12999
13158
|
}
|
|
13000
|
-
if (
|
|
13159
|
+
if (existsSync17("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
13001
13160
|
log("Claude Code installed successfully");
|
|
13002
13161
|
} else {
|
|
13003
13162
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -13053,7 +13212,7 @@ ${r.stderr}`;
|
|
|
13053
13212
|
}
|
|
13054
13213
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
13055
13214
|
function selfUpdateAppliedMarkerPath() {
|
|
13056
|
-
return
|
|
13215
|
+
return join39(homedir17(), ".augmented", ".last-self-update-applied");
|
|
13057
13216
|
}
|
|
13058
13217
|
var selfUpdateUpToDateLogged = false;
|
|
13059
13218
|
var selfUpdatePinnedLogged = false;
|
|
@@ -13104,7 +13263,7 @@ async function checkAndUpdateCli(opts) {
|
|
|
13104
13263
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
13105
13264
|
if (!isBrewFormula && !isNpmGlobal) return "noop";
|
|
13106
13265
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
13107
|
-
const markerPath =
|
|
13266
|
+
const markerPath = join39(homedir17(), ".augmented", ".last-update-check");
|
|
13108
13267
|
if (!force) {
|
|
13109
13268
|
try {
|
|
13110
13269
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
@@ -13597,14 +13756,14 @@ async function checkClaudeAuth() {
|
|
|
13597
13756
|
}
|
|
13598
13757
|
var evalEmptyMcpConfigPath = null;
|
|
13599
13758
|
function ensureEvalEmptyMcpConfig() {
|
|
13600
|
-
if (evalEmptyMcpConfigPath &&
|
|
13601
|
-
const dir =
|
|
13759
|
+
if (evalEmptyMcpConfigPath && existsSync17(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
13760
|
+
const dir = join39(homedir17(), ".augmented");
|
|
13602
13761
|
try {
|
|
13603
|
-
|
|
13762
|
+
mkdirSync12(dir, { recursive: true });
|
|
13604
13763
|
} catch {
|
|
13605
13764
|
}
|
|
13606
|
-
const p =
|
|
13607
|
-
|
|
13765
|
+
const p = join39(dir, ".eval-empty-mcp.json");
|
|
13766
|
+
writeFileSync16(p, JSON.stringify({ mcpServers: {} }));
|
|
13608
13767
|
evalEmptyMcpConfigPath = p;
|
|
13609
13768
|
return p;
|
|
13610
13769
|
}
|
|
@@ -13698,10 +13857,10 @@ function resolveConversationEvalBackend() {
|
|
|
13698
13857
|
return conversationEvalBackend;
|
|
13699
13858
|
}
|
|
13700
13859
|
function getStateFile() {
|
|
13701
|
-
return
|
|
13860
|
+
return join39(config?.configDir ?? join39(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
13702
13861
|
}
|
|
13703
13862
|
function channelHashCacheDir() {
|
|
13704
|
-
return config?.configDir ??
|
|
13863
|
+
return config?.configDir ?? join39(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
13705
13864
|
}
|
|
13706
13865
|
function loadChannelHashCache2() {
|
|
13707
13866
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -13906,7 +14065,7 @@ function removeDeliveryBaselineEntries(agentId) {
|
|
|
13906
14065
|
var _channelQuarantineStore = null;
|
|
13907
14066
|
function channelQuarantineStore() {
|
|
13908
14067
|
if (!_channelQuarantineStore) {
|
|
13909
|
-
const dir = config?.configDir ??
|
|
14068
|
+
const dir = config?.configDir ?? join39(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
13910
14069
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
13911
14070
|
}
|
|
13912
14071
|
return _channelQuarantineStore;
|
|
@@ -13922,12 +14081,12 @@ function claudeMdSizeFor(codeName) {
|
|
|
13922
14081
|
}
|
|
13923
14082
|
function forwardedToolsFor(codeName) {
|
|
13924
14083
|
if (!config?.configDir) return null;
|
|
13925
|
-
return readForwardedToolsReport(
|
|
14084
|
+
return readForwardedToolsReport(join39(config.configDir, codeName));
|
|
13926
14085
|
}
|
|
13927
14086
|
var _hostFlagStore = null;
|
|
13928
14087
|
function hostFlagStore() {
|
|
13929
14088
|
if (!_hostFlagStore) {
|
|
13930
|
-
const dir = config?.configDir ??
|
|
14089
|
+
const dir = config?.configDir ?? join39(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
13931
14090
|
_hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
|
|
13932
14091
|
}
|
|
13933
14092
|
return _hostFlagStore;
|
|
@@ -14000,13 +14159,13 @@ function parseSkillFrontmatter(content) {
|
|
|
14000
14159
|
return out;
|
|
14001
14160
|
}
|
|
14002
14161
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
14003
|
-
const { readdirSync:
|
|
14004
|
-
const skillsDir =
|
|
14005
|
-
const claudeMdPath =
|
|
14162
|
+
const { readdirSync: readdirSync11, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync17 } = await import("fs");
|
|
14163
|
+
const skillsDir = join39(configDir, codeName, "project", ".claude", "skills");
|
|
14164
|
+
const claudeMdPath = join39(configDir, codeName, "project", "CLAUDE.md");
|
|
14006
14165
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
14007
14166
|
const entries = [];
|
|
14008
|
-
for (const dir of
|
|
14009
|
-
const skillFile =
|
|
14167
|
+
for (const dir of readdirSync11(skillsDir).sort()) {
|
|
14168
|
+
const skillFile = join39(skillsDir, dir, "SKILL.md");
|
|
14010
14169
|
if (!ex(skillFile)) continue;
|
|
14011
14170
|
try {
|
|
14012
14171
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -14050,7 +14209,7 @@ ${SKILLS_INDEX_END}`;
|
|
|
14050
14209
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
14051
14210
|
}
|
|
14052
14211
|
if (next !== current) {
|
|
14053
|
-
|
|
14212
|
+
writeFileSync17(claudeMdPath, next, "utf-8");
|
|
14054
14213
|
log2(
|
|
14055
14214
|
injectSkillList ? `Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)` : `Refreshed CLAUDE.md managed block for '${codeName}' (skill list suppressed by claude-md-skills-index=false; ${entries.length} skills on disk)`
|
|
14056
14215
|
);
|
|
@@ -14073,7 +14232,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
14073
14232
|
if (codeNames.length === 0) return;
|
|
14074
14233
|
void (async () => {
|
|
14075
14234
|
try {
|
|
14076
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
14235
|
+
const { collectDiagnostics } = await import("../persistent-session-2AWYIUCX.js");
|
|
14077
14236
|
await api.post("/host/heartbeat", {
|
|
14078
14237
|
host_id: hostId,
|
|
14079
14238
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -14191,7 +14350,7 @@ async function pollCycleInner() {
|
|
|
14191
14350
|
}
|
|
14192
14351
|
try {
|
|
14193
14352
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
14194
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
14353
|
+
const { collectDiagnostics } = await import("../persistent-session-2AWYIUCX.js");
|
|
14195
14354
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
14196
14355
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
|
|
14197
14356
|
let tailscaleHostname;
|
|
@@ -14221,7 +14380,7 @@ async function pollCycleInner() {
|
|
|
14221
14380
|
claudeAuth = await detectClaudeAuth();
|
|
14222
14381
|
} catch (err) {
|
|
14223
14382
|
const errText = err instanceof Error ? err.message : String(err);
|
|
14224
|
-
const errId =
|
|
14383
|
+
const errId = createHash18("sha256").update(errText).digest("hex").slice(0, 12);
|
|
14225
14384
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
14226
14385
|
}
|
|
14227
14386
|
const hostHasClaudeCode = state8.agents.some(
|
|
@@ -14360,7 +14519,7 @@ async function pollCycleInner() {
|
|
|
14360
14519
|
collectPanelessActivityProbes,
|
|
14361
14520
|
getResponsivenessIntervalMs,
|
|
14362
14521
|
occupancyQualificationClassifications
|
|
14363
|
-
} = await import("../responsiveness-probe-
|
|
14522
|
+
} = await import("../responsiveness-probe-JQS4WA2L.js");
|
|
14364
14523
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
14365
14524
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
14366
14525
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -14470,7 +14629,7 @@ async function pollCycleInner() {
|
|
|
14470
14629
|
collectResponsivenessProbes,
|
|
14471
14630
|
livePendingInboundOldestAgeSeconds,
|
|
14472
14631
|
parkPendingInbound
|
|
14473
|
-
} = await import("../responsiveness-probe-
|
|
14632
|
+
} = await import("../responsiveness-probe-JQS4WA2L.js");
|
|
14474
14633
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
14475
14634
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
14476
14635
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -14559,13 +14718,13 @@ async function pollCycleInner() {
|
|
|
14559
14718
|
);
|
|
14560
14719
|
if (hostFlagStore().getBoolean("wedge-transient-notice")) {
|
|
14561
14720
|
try {
|
|
14562
|
-
const paneTail =
|
|
14721
|
+
const paneTail = readFileSync30(paneLogPath(codeName), "utf8").slice(-65536);
|
|
14563
14722
|
const transient = detectTransientApiErrorInLog(paneTail);
|
|
14564
14723
|
if (transient) {
|
|
14565
|
-
const wedgeHome =
|
|
14566
|
-
if (
|
|
14724
|
+
const wedgeHome = join39(homedir17(), ".augmented", codeName);
|
|
14725
|
+
if (existsSync17(wedgeHome)) {
|
|
14567
14726
|
atomicWriteFileSync(
|
|
14568
|
-
|
|
14727
|
+
join39(wedgeHome, "watchdog-give-up.json"),
|
|
14569
14728
|
JSON.stringify({
|
|
14570
14729
|
gave_up_at: wedgeNow.toISOString(),
|
|
14571
14730
|
reason: "transient_overload"
|
|
@@ -14626,7 +14785,7 @@ async function pollCycleInner() {
|
|
|
14626
14785
|
}
|
|
14627
14786
|
try {
|
|
14628
14787
|
const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
|
|
14629
|
-
const { probeSessionAuth } = await import("../session-auth-dead-
|
|
14788
|
+
const { probeSessionAuth } = await import("../session-auth-dead-VBMZW4GG.js");
|
|
14630
14789
|
const observations = [];
|
|
14631
14790
|
const pendingCacheCommits = [];
|
|
14632
14791
|
const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
|
|
@@ -14881,7 +15040,7 @@ async function pollCycleInner() {
|
|
|
14881
15040
|
`[drain-flush] FAILED for '${prev.codeName}': ${err.message} \u2014 proceeding to teardown; this session was NOT shipped (ENG-9491)`
|
|
14882
15041
|
);
|
|
14883
15042
|
}
|
|
14884
|
-
const agentDir =
|
|
15043
|
+
const agentDir = join39(adapter.getAgentDir(prev.codeName), "provision");
|
|
14885
15044
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
14886
15045
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
14887
15046
|
}
|
|
@@ -14968,10 +15127,10 @@ async function pollCycleInner() {
|
|
|
14968
15127
|
// pending-inbound marker. Best-effort: a write failure is logged by
|
|
14969
15128
|
// the watchdog, never fails the poll cycle.
|
|
14970
15129
|
signalGiveUp: (codeName) => {
|
|
14971
|
-
const dir =
|
|
14972
|
-
if (!
|
|
15130
|
+
const dir = join39(homedir17(), ".augmented", codeName);
|
|
15131
|
+
if (!existsSync17(dir)) return;
|
|
14973
15132
|
atomicWriteFileSync(
|
|
14974
|
-
|
|
15133
|
+
join39(dir, "watchdog-give-up.json"),
|
|
14975
15134
|
JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
14976
15135
|
);
|
|
14977
15136
|
},
|
|
@@ -14990,9 +15149,9 @@ async function pollCycleInner() {
|
|
|
14990
15149
|
// parser elsewhere can only reconstruct an hour (ENG-8901).
|
|
14991
15150
|
signalUsageLimit: (codeName, resetsHint) => {
|
|
14992
15151
|
const dir = getFramework("claude-code").getAgentDir(codeName);
|
|
14993
|
-
if (!
|
|
15152
|
+
if (!existsSync17(dir)) return;
|
|
14994
15153
|
atomicWriteFileSync(
|
|
14995
|
-
|
|
15154
|
+
join39(dir, "watchdog-give-up.json"),
|
|
14996
15155
|
JSON.stringify({
|
|
14997
15156
|
gave_up_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14998
15157
|
reason: "usage_limit",
|
|
@@ -15254,7 +15413,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15254
15413
|
}
|
|
15255
15414
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15256
15415
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
15257
|
-
let agentDir =
|
|
15416
|
+
let agentDir = join39(adapter.getAgentDir(agent.code_name), "provision");
|
|
15258
15417
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
15259
15418
|
forgetChannelSyncState(agent.agent_id);
|
|
15260
15419
|
if (previousKnownStatus !== agent.status) {
|
|
@@ -15295,7 +15454,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15295
15454
|
const residuals = {
|
|
15296
15455
|
gatewayRunning: false,
|
|
15297
15456
|
portAllocated: false,
|
|
15298
|
-
provisionDirExists:
|
|
15457
|
+
provisionDirExists: existsSync17(agentDir)
|
|
15299
15458
|
};
|
|
15300
15459
|
if (!hasRevokedResiduals(residuals)) {
|
|
15301
15460
|
agentStates.push({
|
|
@@ -15383,8 +15542,28 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15383
15542
|
let refreshData;
|
|
15384
15543
|
try {
|
|
15385
15544
|
refreshData = await api.post("/host/refresh", {
|
|
15386
|
-
agent_id: agent.agent_id
|
|
15545
|
+
agent_id: agent.agent_id,
|
|
15546
|
+
// ENG-9585: ask for hashes instead of knowledge bodies. An API build that
|
|
15547
|
+
// predates the manifest ignores this and answers with `knowledge` as
|
|
15548
|
+
// before, which the resolution below handles without a version check.
|
|
15549
|
+
knowledge_manifest: true
|
|
15387
15550
|
});
|
|
15551
|
+
if (refreshData.knowledge_manifest) {
|
|
15552
|
+
const resolved = await resolveKnowledgeFromManifest({
|
|
15553
|
+
manifest: refreshData.knowledge_manifest,
|
|
15554
|
+
cacheDir: join39(config.configDir, "_knowledge"),
|
|
15555
|
+
delivery: refreshData.agent?.knowledge_delivery ?? "both",
|
|
15556
|
+
fetchContents: async (hashes) => {
|
|
15557
|
+
const res = await api.post(
|
|
15558
|
+
"/host/knowledge/contents",
|
|
15559
|
+
{ agent_id: agent.agent_id, hashes }
|
|
15560
|
+
);
|
|
15561
|
+
return res.contents ?? [];
|
|
15562
|
+
},
|
|
15563
|
+
log
|
|
15564
|
+
});
|
|
15565
|
+
refreshData.knowledge = resolved;
|
|
15566
|
+
}
|
|
15388
15567
|
} catch (err) {
|
|
15389
15568
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
15390
15569
|
const existing = state8.agents.find((a) => a.agentId === agent.agent_id);
|
|
@@ -15429,7 +15608,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15429
15608
|
const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
|
|
15430
15609
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
15431
15610
|
const frameworkAdapter = getFramework(frameworkId);
|
|
15432
|
-
agentDir =
|
|
15611
|
+
agentDir = join39(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
15433
15612
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
15434
15613
|
agentRestartTimezoneInputs.set(agent.code_name, {
|
|
15435
15614
|
agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
|
|
@@ -15485,9 +15664,9 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15485
15664
|
try {
|
|
15486
15665
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter, renderIntegrationsSection);
|
|
15487
15666
|
const changedFiles = [];
|
|
15488
|
-
|
|
15667
|
+
mkdirSync12(agentDir, { recursive: true });
|
|
15489
15668
|
for (const artifact of artifacts) {
|
|
15490
|
-
const filePath =
|
|
15669
|
+
const filePath = join39(agentDir, artifact.relativePath);
|
|
15491
15670
|
let existingHash;
|
|
15492
15671
|
let newHash;
|
|
15493
15672
|
let writeContent = artifact.content;
|
|
@@ -15506,8 +15685,8 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15506
15685
|
};
|
|
15507
15686
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
15508
15687
|
try {
|
|
15509
|
-
const projectClaudeMd =
|
|
15510
|
-
const existing =
|
|
15688
|
+
const projectClaudeMd = join39(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
15689
|
+
const existing = readFileSync30(projectClaudeMd, "utf-8");
|
|
15511
15690
|
existingHash = sha256(stripDynamicSections(existing));
|
|
15512
15691
|
} catch {
|
|
15513
15692
|
existingHash = null;
|
|
@@ -15525,7 +15704,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15525
15704
|
const generatorKeys = Object.keys(generatorServers);
|
|
15526
15705
|
let existingRaw = "";
|
|
15527
15706
|
try {
|
|
15528
|
-
existingRaw =
|
|
15707
|
+
existingRaw = readFileSync30(filePath, "utf-8");
|
|
15529
15708
|
} catch {
|
|
15530
15709
|
}
|
|
15531
15710
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -15541,7 +15720,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15541
15720
|
} else if (artifact.relativePath === "opencode.json") {
|
|
15542
15721
|
let existingRaw = null;
|
|
15543
15722
|
try {
|
|
15544
|
-
existingRaw =
|
|
15723
|
+
existingRaw = readFileSync30(filePath, "utf-8");
|
|
15545
15724
|
} catch {
|
|
15546
15725
|
}
|
|
15547
15726
|
const mergeResult = mergeOpencodeConfigArtifact(artifact.content, existingRaw);
|
|
@@ -15557,26 +15736,26 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15557
15736
|
}
|
|
15558
15737
|
}
|
|
15559
15738
|
if (changedFiles.length > 0) {
|
|
15560
|
-
const isFirst = !
|
|
15739
|
+
const isFirst = !existsSync17(join39(agentDir, "CHARTER.md"));
|
|
15561
15740
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
15562
15741
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
15563
15742
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
15564
15743
|
for (const file of changedFiles) {
|
|
15565
|
-
const filePath =
|
|
15566
|
-
|
|
15744
|
+
const filePath = join39(agentDir, file.relativePath);
|
|
15745
|
+
mkdirSync12(dirname10(filePath), { recursive: true });
|
|
15567
15746
|
if (file.relativePath === ".mcp.json") {
|
|
15568
15747
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
15569
15748
|
} else {
|
|
15570
|
-
|
|
15749
|
+
writeFileSync16(filePath, file.content);
|
|
15571
15750
|
}
|
|
15572
15751
|
}
|
|
15573
15752
|
try {
|
|
15574
|
-
const provSkillsDir =
|
|
15575
|
-
if (
|
|
15576
|
-
for (const folder of
|
|
15753
|
+
const provSkillsDir = join39(agentDir, ".claude", "skills");
|
|
15754
|
+
if (existsSync17(provSkillsDir)) {
|
|
15755
|
+
for (const folder of readdirSync10(provSkillsDir)) {
|
|
15577
15756
|
if (folder.startsWith("knowledge-")) {
|
|
15578
15757
|
try {
|
|
15579
|
-
|
|
15758
|
+
rmSync6(join39(provSkillsDir, folder), { recursive: true });
|
|
15580
15759
|
} catch {
|
|
15581
15760
|
}
|
|
15582
15761
|
}
|
|
@@ -15589,7 +15768,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15589
15768
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
15590
15769
|
const hashes = /* @__PURE__ */ new Map();
|
|
15591
15770
|
for (const file of trackedFiles2) {
|
|
15592
|
-
const h = hashFile(
|
|
15771
|
+
const h = hashFile(join39(agentDir, file));
|
|
15593
15772
|
if (h) hashes.set(file, h);
|
|
15594
15773
|
}
|
|
15595
15774
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -15607,14 +15786,14 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15607
15786
|
}
|
|
15608
15787
|
if (Array.isArray(refreshData.workflows)) {
|
|
15609
15788
|
try {
|
|
15610
|
-
const provWorkflowsDir =
|
|
15611
|
-
if (
|
|
15789
|
+
const provWorkflowsDir = join39(agentDir, ".claude", "workflows");
|
|
15790
|
+
if (existsSync17(provWorkflowsDir)) {
|
|
15612
15791
|
const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
|
|
15613
|
-
for (const file of
|
|
15792
|
+
for (const file of readdirSync10(provWorkflowsDir)) {
|
|
15614
15793
|
if (!file.endsWith(".js")) continue;
|
|
15615
15794
|
if (expected.has(file)) continue;
|
|
15616
15795
|
try {
|
|
15617
|
-
|
|
15796
|
+
rmSync6(join39(provWorkflowsDir, file));
|
|
15618
15797
|
} catch {
|
|
15619
15798
|
}
|
|
15620
15799
|
}
|
|
@@ -15693,10 +15872,10 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15693
15872
|
}
|
|
15694
15873
|
let lastDriftCheckAt = now;
|
|
15695
15874
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
15696
|
-
if (written &&
|
|
15875
|
+
if (written && existsSync17(agentDir)) {
|
|
15697
15876
|
const driftedFiles = [];
|
|
15698
15877
|
for (const [file, expectedHash] of written) {
|
|
15699
|
-
const localHash = hashFile(
|
|
15878
|
+
const localHash = hashFile(join39(agentDir, file));
|
|
15700
15879
|
if (localHash && localHash !== expectedHash) {
|
|
15701
15880
|
driftedFiles.push(file);
|
|
15702
15881
|
}
|
|
@@ -15707,7 +15886,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15707
15886
|
try {
|
|
15708
15887
|
const localHashes = {};
|
|
15709
15888
|
for (const file of driftedFiles) {
|
|
15710
|
-
localHashes[file] = hashFile(
|
|
15889
|
+
localHashes[file] = hashFile(join39(agentDir, file));
|
|
15711
15890
|
}
|
|
15712
15891
|
await api.post("/host/drift", {
|
|
15713
15892
|
agent_id: agent.agent_id,
|
|
@@ -15975,15 +16154,15 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
15975
16154
|
const addedChannels = [...restartDecision.added];
|
|
15976
16155
|
const writeDmNoticeMarkers = isChannelAddRestart ? () => {
|
|
15977
16156
|
try {
|
|
15978
|
-
const agentAugmentedDir =
|
|
15979
|
-
|
|
16157
|
+
const agentAugmentedDir = join39(homedir17(), ".augmented", agent.code_name);
|
|
16158
|
+
mkdirSync12(agentAugmentedDir, { recursive: true });
|
|
15980
16159
|
const markerJson = JSON.stringify({
|
|
15981
16160
|
version: 1,
|
|
15982
16161
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15983
16162
|
added: addedChannels
|
|
15984
16163
|
});
|
|
15985
16164
|
for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
|
|
15986
|
-
atomicWriteFileSync(
|
|
16165
|
+
atomicWriteFileSync(join39(agentAugmentedDir, file), markerJson);
|
|
15987
16166
|
}
|
|
15988
16167
|
} catch (err) {
|
|
15989
16168
|
log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
|
|
@@ -16059,7 +16238,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16059
16238
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
16060
16239
|
msteamsEntry?.config
|
|
16061
16240
|
);
|
|
16062
|
-
const behaviourHash =
|
|
16241
|
+
const behaviourHash = createHash18("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
16063
16242
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
16064
16243
|
const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
|
|
16065
16244
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
@@ -16115,7 +16294,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16115
16294
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
16116
16295
|
slackEntry?.config
|
|
16117
16296
|
);
|
|
16118
|
-
const slackBehaviourHash =
|
|
16297
|
+
const slackBehaviourHash = createHash18("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
16119
16298
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
16120
16299
|
const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
|
|
16121
16300
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
@@ -16172,24 +16351,24 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16172
16351
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
|
|
16173
16352
|
try {
|
|
16174
16353
|
const agentProvisionDir = agentDir;
|
|
16175
|
-
const projectDir =
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
const provisionMcpPath =
|
|
16179
|
-
const projectMcpPath =
|
|
16354
|
+
const projectDir = join39(homedir17(), ".augmented", agent.code_name, "project");
|
|
16355
|
+
mkdirSync12(agentProvisionDir, { recursive: true });
|
|
16356
|
+
mkdirSync12(projectDir, { recursive: true });
|
|
16357
|
+
const provisionMcpPath = join39(agentProvisionDir, ".mcp.json");
|
|
16358
|
+
const projectMcpPath = join39(projectDir, ".mcp.json");
|
|
16180
16359
|
let mcpConfig = { mcpServers: {} };
|
|
16181
16360
|
try {
|
|
16182
|
-
mcpConfig = JSON.parse(
|
|
16361
|
+
mcpConfig = JSON.parse(readFileSync30(provisionMcpPath, "utf-8"));
|
|
16183
16362
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
16184
16363
|
} catch {
|
|
16185
16364
|
}
|
|
16186
|
-
const localDirectChatChannel =
|
|
16365
|
+
const localDirectChatChannel = join39(homedir17(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
16187
16366
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
16188
16367
|
const directChatTz = (() => {
|
|
16189
16368
|
const tz = directChatTeamSettings?.["timezone"];
|
|
16190
16369
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
16191
16370
|
})();
|
|
16192
|
-
if (
|
|
16371
|
+
if (existsSync17(localDirectChatChannel)) {
|
|
16193
16372
|
const directChatEnv = {
|
|
16194
16373
|
AGT_HOST: requireHost(),
|
|
16195
16374
|
// ENG-5901 Track D: templated — the manager exports the real
|
|
@@ -16209,7 +16388,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16209
16388
|
// ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
|
|
16210
16389
|
// returns the agent root (NOT the /provision subdir `agentDir` points at),
|
|
16211
16390
|
// so it byte-matches the broker readers' path.
|
|
16212
|
-
AGT_TURN_INITIATOR_FILE:
|
|
16391
|
+
AGT_TURN_INITIATOR_FILE: join39(
|
|
16213
16392
|
frameworkAdapter.getAgentDir(agent.code_name),
|
|
16214
16393
|
".current-turn-initiator.json"
|
|
16215
16394
|
)
|
|
@@ -16229,10 +16408,10 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16229
16408
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
16230
16409
|
}
|
|
16231
16410
|
}
|
|
16232
|
-
const staleChannelsPath =
|
|
16233
|
-
if (
|
|
16411
|
+
const staleChannelsPath = join39(projectDir, ".mcp-channels.json");
|
|
16412
|
+
if (existsSync17(staleChannelsPath)) {
|
|
16234
16413
|
try {
|
|
16235
|
-
|
|
16414
|
+
rmSync6(staleChannelsPath, { force: true });
|
|
16236
16415
|
} catch {
|
|
16237
16416
|
}
|
|
16238
16417
|
}
|
|
@@ -16319,7 +16498,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16319
16498
|
}
|
|
16320
16499
|
if (hostFlagStore().getBoolean("connectivity-probe")) {
|
|
16321
16500
|
try {
|
|
16322
|
-
const probeProjectDir =
|
|
16501
|
+
const probeProjectDir = join39(homedir17(), ".augmented", agent.code_name, "project");
|
|
16323
16502
|
let probeSet = integrations;
|
|
16324
16503
|
try {
|
|
16325
16504
|
const quarantined = await api.post("/host/agent-integrations/quarantined", { agent_id: agent.agent_id });
|
|
@@ -16365,7 +16544,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16365
16544
|
const forceDue = attemptsLeft > 0;
|
|
16366
16545
|
let probeRan = false;
|
|
16367
16546
|
try {
|
|
16368
|
-
const probeProjectDir =
|
|
16547
|
+
const probeProjectDir = join39(homedir17(), ".augmented", agent.code_name, "project");
|
|
16369
16548
|
probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
|
|
16370
16549
|
} catch (err) {
|
|
16371
16550
|
log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -16442,11 +16621,11 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16442
16621
|
const intHash = computeIntegrationsHash(integrations);
|
|
16443
16622
|
const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
|
|
16444
16623
|
if (intHash !== prevIntHash) {
|
|
16445
|
-
const projectDir =
|
|
16446
|
-
const envIntPath =
|
|
16624
|
+
const projectDir = join39(homedir17(), ".augmented", agent.code_name, "project");
|
|
16625
|
+
const envIntPath = join39(projectDir, ".env.integrations");
|
|
16447
16626
|
let preWriteEnv;
|
|
16448
16627
|
try {
|
|
16449
|
-
preWriteEnv =
|
|
16628
|
+
preWriteEnv = readFileSync30(envIntPath, "utf-8");
|
|
16450
16629
|
} catch {
|
|
16451
16630
|
preWriteEnv = void 0;
|
|
16452
16631
|
}
|
|
@@ -16465,9 +16644,9 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16465
16644
|
}
|
|
16466
16645
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
16467
16646
|
try {
|
|
16468
|
-
const projectMcpPath =
|
|
16469
|
-
const postWriteEnv =
|
|
16470
|
-
const mcpContent =
|
|
16647
|
+
const projectMcpPath = join39(projectDir, ".mcp.json");
|
|
16648
|
+
const postWriteEnv = readFileSync30(envIntPath, "utf-8");
|
|
16649
|
+
const mcpContent = readFileSync30(projectMcpPath, "utf-8");
|
|
16471
16650
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
16472
16651
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
16473
16652
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -16557,10 +16736,10 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16557
16736
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
16558
16737
|
}
|
|
16559
16738
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
16560
|
-
const headersHash =
|
|
16739
|
+
const headersHash = createHash18("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
16561
16740
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
16562
16741
|
}).join("\n");
|
|
16563
|
-
const mcpHash =
|
|
16742
|
+
const mcpHash = createHash18("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
16564
16743
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
16565
16744
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
16566
16745
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -16575,7 +16754,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16575
16754
|
if (mcpHash !== prevMcpHash) {
|
|
16576
16755
|
for (const e of desiredEntries) {
|
|
16577
16756
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
16578
|
-
const urlHash =
|
|
16757
|
+
const urlHash = createHash18("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
16579
16758
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
16580
16759
|
}
|
|
16581
16760
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
|
|
@@ -16655,7 +16834,10 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16655
16834
|
if (missing.length > 0) {
|
|
16656
16835
|
log(`Auto-provisioned ${missing.length} default schedule(s) for '${agent.code_name}': ${missing.map((s) => s.template_id).join(", ")}`);
|
|
16657
16836
|
}
|
|
16658
|
-
const freshData = await api.post("/host/refresh", {
|
|
16837
|
+
const freshData = await api.post("/host/refresh", {
|
|
16838
|
+
agent_id: agent.agent_id,
|
|
16839
|
+
knowledge_manifest: true
|
|
16840
|
+
});
|
|
16659
16841
|
tasks = freshData.scheduled_tasks ?? tasks;
|
|
16660
16842
|
}
|
|
16661
16843
|
} catch (err) {
|
|
@@ -16678,7 +16860,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16678
16860
|
if (frameworkAdapter.installSkillFiles) {
|
|
16679
16861
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
16680
16862
|
const installedIntegrationSkills = [];
|
|
16681
|
-
const { createHash:
|
|
16863
|
+
const { createHash: createHash19 } = await import("crypto");
|
|
16682
16864
|
const refreshAny = refreshData;
|
|
16683
16865
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
16684
16866
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -16707,7 +16889,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16707
16889
|
)
|
|
16708
16890
|
}));
|
|
16709
16891
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
16710
|
-
const contentHash =
|
|
16892
|
+
const contentHash = createHash19("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
16711
16893
|
if (!shouldWriteIntegrationSkill(
|
|
16712
16894
|
agentState.knownSkillHashes,
|
|
16713
16895
|
agent.agent_id,
|
|
@@ -16730,23 +16912,23 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16730
16912
|
}
|
|
16731
16913
|
}
|
|
16732
16914
|
try {
|
|
16733
|
-
const { readdirSync:
|
|
16915
|
+
const { readdirSync: readdirSync11, rmSync: rmSync7 } = await import("fs");
|
|
16734
16916
|
const { homedir: homedir18 } = await import("os");
|
|
16735
16917
|
const frameworkId2 = frameworkAdapter.id;
|
|
16736
16918
|
const candidateSkillDirs = [
|
|
16737
16919
|
// Claude Code — framework runtime tree
|
|
16738
|
-
|
|
16920
|
+
join39(homedir18(), ".augmented", agent.code_name, "skills"),
|
|
16739
16921
|
// Claude Code — project tree
|
|
16740
|
-
|
|
16922
|
+
join39(homedir18(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
16741
16923
|
// Defensive: legacy provision-side path, not currently an
|
|
16742
16924
|
// install target but cheap to sweep.
|
|
16743
|
-
|
|
16925
|
+
join39(agentDir, ".claude", "skills")
|
|
16744
16926
|
];
|
|
16745
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
16927
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync17(d));
|
|
16746
16928
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
16747
16929
|
for (const dir of existingDirs) {
|
|
16748
16930
|
try {
|
|
16749
|
-
for (const entry of
|
|
16931
|
+
for (const entry of readdirSync11(dir)) {
|
|
16750
16932
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
16751
16933
|
discoveredEntries.add(entry);
|
|
16752
16934
|
}
|
|
@@ -16761,8 +16943,8 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16761
16943
|
entry,
|
|
16762
16944
|
dirs: existingDirs,
|
|
16763
16945
|
removeDir: (p) => {
|
|
16764
|
-
if (
|
|
16765
|
-
|
|
16946
|
+
if (existsSync17(p)) {
|
|
16947
|
+
rmSync7(p, { recursive: true, force: true });
|
|
16766
16948
|
}
|
|
16767
16949
|
}
|
|
16768
16950
|
});
|
|
@@ -16781,7 +16963,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16781
16963
|
const sharedSkillsPayload = refreshAny.shared_skills;
|
|
16782
16964
|
const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
|
|
16783
16965
|
const manifestPath = managedSkillManifestPath(
|
|
16784
|
-
|
|
16966
|
+
join39(homedir17(), ".augmented", agent.code_name)
|
|
16785
16967
|
);
|
|
16786
16968
|
const prevIds = /* @__PURE__ */ new Set([
|
|
16787
16969
|
...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
@@ -16790,7 +16972,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16790
16972
|
const plan = planGlobalSkillSync(
|
|
16791
16973
|
[...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
|
|
16792
16974
|
prevIds,
|
|
16793
|
-
(content) =>
|
|
16975
|
+
(content) => createHash19("sha256").update(content).digest("hex").slice(0, 12),
|
|
16794
16976
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
|
|
16795
16977
|
{ desiredResolved }
|
|
16796
16978
|
);
|
|
@@ -16801,16 +16983,16 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16801
16983
|
}
|
|
16802
16984
|
if (plan.removes.length) {
|
|
16803
16985
|
const globalSkillDirs = [
|
|
16804
|
-
|
|
16805
|
-
|
|
16806
|
-
|
|
16986
|
+
join39(homedir17(), ".augmented", agent.code_name, "skills"),
|
|
16987
|
+
join39(homedir17(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
16988
|
+
join39(agentDir, ".claude", "skills")
|
|
16807
16989
|
];
|
|
16808
16990
|
for (const id of plan.removes) {
|
|
16809
16991
|
let prunedAny = false;
|
|
16810
16992
|
for (const dir of globalSkillDirs) {
|
|
16811
|
-
const p =
|
|
16812
|
-
if (
|
|
16813
|
-
|
|
16993
|
+
const p = join39(dir, id);
|
|
16994
|
+
if (existsSync17(p) && existsSync17(join39(p, "SKILL.md"))) {
|
|
16995
|
+
rmSync6(p, { recursive: true, force: true });
|
|
16814
16996
|
prunedAny = true;
|
|
16815
16997
|
}
|
|
16816
16998
|
}
|
|
@@ -16841,7 +17023,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16841
17023
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
16842
17024
|
if (!slug) continue;
|
|
16843
17025
|
try {
|
|
16844
|
-
const scriptHash =
|
|
17026
|
+
const scriptHash = createHash19("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
16845
17027
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
16846
17028
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
16847
17029
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -16856,9 +17038,9 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
16856
17038
|
} else if (result.timedOut) {
|
|
16857
17039
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
16858
17040
|
} else {
|
|
16859
|
-
const stderrHash =
|
|
17041
|
+
const stderrHash = createHash19("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
16860
17042
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
16861
|
-
const missingCmdHash = missingCmd ?
|
|
17043
|
+
const missingCmdHash = missingCmd ? createHash19("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
16862
17044
|
log(
|
|
16863
17045
|
`Integration hook on_install '${slug}' exited ${result.exitCode} for '${agent.code_name}' ` + (missingCmdHash ? `[missing_command_hash=${missingCmdHash}] ` : "") + `[stderr_hash=${stderrHash} stderr_len=${result.stderr.length}]`
|
|
16864
17046
|
);
|
|
@@ -17024,6 +17206,12 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
17024
17206
|
} else if (stuck.recovered) {
|
|
17025
17207
|
log(`[persistent-session-stuck] agent=${agent.code_name} recovered from stuck state`);
|
|
17026
17208
|
}
|
|
17209
|
+
if (stuck.shouldWarnSpawnLoop) {
|
|
17210
|
+
const failure = getLastFailureContext(agent.code_name);
|
|
17211
|
+
log(
|
|
17212
|
+
`[persistent-session-spawn-loop] WARN: agent=${agent.code_name} respawned ${stuck.spawnLoopTicks} consecutive ticks and is still unhealthy (last_decision=${psResult.decision} signature=${failure.signature} restart_count=${failure.restartCount}) \u2014 the spawn is failing in a way a retry cannot fix; see the pane log tail for the cause (ENG-9614)`
|
|
17213
|
+
);
|
|
17214
|
+
}
|
|
17027
17215
|
if (agentFw === "claude-code") {
|
|
17028
17216
|
try {
|
|
17029
17217
|
checkMcpConfigDriftAndScheduleRestart(agent.code_name, getProjectDir(agent.code_name));
|
|
@@ -17045,8 +17233,8 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
17045
17233
|
const sess = getSessionState(agent.code_name);
|
|
17046
17234
|
let mcpJsonParsed = null;
|
|
17047
17235
|
try {
|
|
17048
|
-
const mcpPath =
|
|
17049
|
-
mcpJsonParsed = JSON.parse(
|
|
17236
|
+
const mcpPath = join39(getProjectDir(agent.code_name), ".mcp.json");
|
|
17237
|
+
mcpJsonParsed = JSON.parse(readFileSync30(mcpPath, "utf-8"));
|
|
17050
17238
|
} catch {
|
|
17051
17239
|
}
|
|
17052
17240
|
reapMissingMcpSessions({
|
|
@@ -17429,7 +17617,7 @@ async function processAgent(agent, agentStates, managedToolkits) {
|
|
|
17429
17617
|
freshDoneIds.add(item.id);
|
|
17430
17618
|
}
|
|
17431
17619
|
} catch (err) {
|
|
17432
|
-
const errId =
|
|
17620
|
+
const errId = createHash18("sha256").update(err instanceof Error ? err.message : String(err)).digest("hex").slice(0, 12);
|
|
17433
17621
|
log(`Reap API error for item_id=${item.id} error_id=${errId}`);
|
|
17434
17622
|
}
|
|
17435
17623
|
if (reapAction === "dead_letter") {
|
|
@@ -17477,10 +17665,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
17477
17665
|
}
|
|
17478
17666
|
}
|
|
17479
17667
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
17480
|
-
if (trackedFiles.length > 0 &&
|
|
17668
|
+
if (trackedFiles.length > 0 && existsSync17(agentDir)) {
|
|
17481
17669
|
const hashes = /* @__PURE__ */ new Map();
|
|
17482
17670
|
for (const file of trackedFiles) {
|
|
17483
|
-
const h = hashFile(
|
|
17671
|
+
const h = hashFile(join39(agentDir, file));
|
|
17484
17672
|
if (h) hashes.set(file, h);
|
|
17485
17673
|
}
|
|
17486
17674
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -17495,7 +17683,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
17495
17683
|
refreshData.agent.onboarding_state
|
|
17496
17684
|
);
|
|
17497
17685
|
const obStep = obState.step;
|
|
17498
|
-
const markerPath =
|
|
17686
|
+
const markerPath = join39(homedir17(), ".augmented", agent.code_name, "onboarding-drive.json");
|
|
17499
17687
|
const marker = readOnboardingDriveMarker(markerPath);
|
|
17500
17688
|
const obContactRaw = refreshData.agent.manager_last_contacted_at;
|
|
17501
17689
|
const obContact = typeof obContactRaw === "string" && obContactRaw ? obContactRaw : null;
|
|
@@ -17597,7 +17785,7 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
17597
17785
|
}
|
|
17598
17786
|
stopOpencodeSlackIngest(codeName, log);
|
|
17599
17787
|
stopOpencodeTelegramIngest(codeName, log);
|
|
17600
|
-
const opencodeProjectDir =
|
|
17788
|
+
const opencodeProjectDir = join39(getFramework("opencode").getAgentDir(codeName), "provision");
|
|
17601
17789
|
const serveEnv = {
|
|
17602
17790
|
AGT_HOST: requireHost(),
|
|
17603
17791
|
AGT_API_KEY: getApiKey() ?? void 0,
|
|
@@ -17652,8 +17840,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
17652
17840
|
});
|
|
17653
17841
|
}
|
|
17654
17842
|
const projectDir = getProjectDir(codeName);
|
|
17655
|
-
const mcpConfigPath =
|
|
17656
|
-
const claudeMdPath =
|
|
17843
|
+
const mcpConfigPath = join39(projectDir, ".mcp.json");
|
|
17844
|
+
const claudeMdPath = join39(projectDir, "CLAUDE.md");
|
|
17657
17845
|
if (restartBreaker.isTripped(codeName)) {
|
|
17658
17846
|
const trip = restartBreaker.getTrip(codeName);
|
|
17659
17847
|
return {
|
|
@@ -17902,7 +18090,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
17902
18090
|
const ctx = getLastFailureContext(codeName);
|
|
17903
18091
|
const recovery = prepareForRespawn(codeName);
|
|
17904
18092
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
17905
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
18093
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash18("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
17906
18094
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
17907
18095
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
17908
18096
|
log(
|
|
@@ -17916,7 +18104,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash17("sha256")
|
|
|
17916
18104
|
);
|
|
17917
18105
|
getHostId().then((hostId) => {
|
|
17918
18106
|
if (!hostId) return;
|
|
17919
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
18107
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash18("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
17920
18108
|
return api.post("/host/events", {
|
|
17921
18109
|
host_id: hostId,
|
|
17922
18110
|
agent_code_name: codeName,
|
|
@@ -18113,7 +18301,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash17("sha256")
|
|
|
18113
18301
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
18114
18302
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
18115
18303
|
}
|
|
18116
|
-
const stableTasksHash =
|
|
18304
|
+
const stableTasksHash = createHash18("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
18117
18305
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
18118
18306
|
if (stableTasksHash !== prevHash) {
|
|
18119
18307
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -18428,7 +18616,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
18428
18616
|
void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
|
|
18429
18617
|
void (async () => {
|
|
18430
18618
|
try {
|
|
18431
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
18619
|
+
const { collectDiagnostics } = await import("../persistent-session-2AWYIUCX.js");
|
|
18432
18620
|
await api.post("/host/heartbeat", {
|
|
18433
18621
|
host_id: hostId,
|
|
18434
18622
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -18479,7 +18667,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
18479
18667
|
}
|
|
18480
18668
|
try {
|
|
18481
18669
|
const hostId = await getHostId();
|
|
18482
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
18670
|
+
const { collectDiagnostics } = await import("../persistent-session-2AWYIUCX.js");
|
|
18483
18671
|
await api.post("/host/heartbeat", {
|
|
18484
18672
|
host_id: hostId,
|
|
18485
18673
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
|
|
@@ -18587,7 +18775,7 @@ function ensureRealtimeKanbanStarted(agentStates) {
|
|
|
18587
18775
|
);
|
|
18588
18776
|
}
|
|
18589
18777
|
}).catch((err) => {
|
|
18590
|
-
const errId =
|
|
18778
|
+
const errId = createHash18("sha256").update(err instanceof Error ? err.message : String(err)).digest("hex").slice(0, 12);
|
|
18591
18779
|
log(
|
|
18592
18780
|
`[realtime] Work trigger enqueue threw for '${agent.codeName}' item_id=${item.id} error_id=${errId} \u2014 hybrid nudge will retry`
|
|
18593
18781
|
);
|
|
@@ -18877,8 +19065,8 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
18877
19065
|
if (useDoorbell) {
|
|
18878
19066
|
try {
|
|
18879
19067
|
const doorbell = directChatDoorbellPath(agent.agentId, homedir17());
|
|
18880
|
-
|
|
18881
|
-
|
|
19068
|
+
mkdirSync12(dirname10(doorbell), { recursive: true });
|
|
19069
|
+
writeFileSync16(doorbell, String(Date.now()));
|
|
18882
19070
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
18883
19071
|
return;
|
|
18884
19072
|
} catch (err) {
|
|
@@ -19006,8 +19194,8 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
|
|
|
19006
19194
|
if (run_id) openInjectedRunByCode.set(codeName, run_id);
|
|
19007
19195
|
try {
|
|
19008
19196
|
const doorbell = directChatDoorbellPath(agentId, homedir17());
|
|
19009
|
-
|
|
19010
|
-
|
|
19197
|
+
mkdirSync12(dirname10(doorbell), { recursive: true });
|
|
19198
|
+
writeFileSync16(doorbell, String(Date.now()));
|
|
19011
19199
|
} catch (err) {
|
|
19012
19200
|
log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
19013
19201
|
}
|
|
@@ -19111,7 +19299,7 @@ async function processClaudePairSessions(agents) {
|
|
|
19111
19299
|
killPairSession,
|
|
19112
19300
|
pairTmuxSession,
|
|
19113
19301
|
finalizeClaudePairOnboarding
|
|
19114
|
-
} = await import("../claude-pair-runtime-
|
|
19302
|
+
} = await import("../claude-pair-runtime-PDG74SAT.js");
|
|
19115
19303
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
19116
19304
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
19117
19305
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -19336,17 +19524,17 @@ var lastDownloadHash = /* @__PURE__ */ new Map();
|
|
|
19336
19524
|
var lastLocalFileHash = /* @__PURE__ */ new Map();
|
|
19337
19525
|
var memoryManifests = /* @__PURE__ */ new Map();
|
|
19338
19526
|
function memoryRetirementDir(configDir, codeName) {
|
|
19339
|
-
return
|
|
19527
|
+
return join39(configDir, "_memory-retirement", codeName);
|
|
19340
19528
|
}
|
|
19341
19529
|
function memoryManifestPath(configDir, codeName) {
|
|
19342
|
-
return
|
|
19530
|
+
return join39(memoryRetirementDir(configDir, codeName), "manifest.json");
|
|
19343
19531
|
}
|
|
19344
19532
|
function loadMemoryManifest(agentId, configDir, codeName) {
|
|
19345
19533
|
const cached = memoryManifests.get(agentId);
|
|
19346
19534
|
if (cached) return cached;
|
|
19347
19535
|
let manifest = {};
|
|
19348
19536
|
try {
|
|
19349
|
-
const raw =
|
|
19537
|
+
const raw = readFileSync30(memoryManifestPath(configDir, codeName), "utf-8");
|
|
19350
19538
|
const parsed = JSON.parse(raw);
|
|
19351
19539
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19352
19540
|
for (const [name, entry] of Object.entries(parsed)) {
|
|
@@ -19366,15 +19554,15 @@ function saveMemoryManifest(agentId, configDir, codeName) {
|
|
|
19366
19554
|
if (!manifest) return;
|
|
19367
19555
|
try {
|
|
19368
19556
|
const path = memoryManifestPath(configDir, codeName);
|
|
19369
|
-
|
|
19557
|
+
mkdirSync12(memoryRetirementDir(configDir, codeName), { recursive: true });
|
|
19370
19558
|
const tmp = `${path}.tmp`;
|
|
19371
|
-
|
|
19372
|
-
|
|
19559
|
+
writeFileSync16(tmp, JSON.stringify(manifest, null, 2));
|
|
19560
|
+
renameSync9(tmp, path);
|
|
19373
19561
|
} catch {
|
|
19374
19562
|
}
|
|
19375
19563
|
}
|
|
19376
19564
|
function retiredMemoryDir(configDir, codeName) {
|
|
19377
|
-
return
|
|
19565
|
+
return join39(memoryRetirementDir(configDir, codeName), "retired");
|
|
19378
19566
|
}
|
|
19379
19567
|
var RETIRED_MEMORY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
19380
19568
|
var lastRetirementReport = /* @__PURE__ */ new Map();
|
|
@@ -19452,15 +19640,15 @@ function applyMemoryTombstones(opts) {
|
|
|
19452
19640
|
log2(`[memory-retire] ${agent.code_name}: KEPT '${tomb.name}' \u2014 no write record, not ours to remove`);
|
|
19453
19641
|
continue;
|
|
19454
19642
|
}
|
|
19455
|
-
const filePath =
|
|
19456
|
-
if (!
|
|
19643
|
+
const filePath = join39(memoryDir, entry.file);
|
|
19644
|
+
if (!existsSync17(filePath)) {
|
|
19457
19645
|
outcomes.already_gone++;
|
|
19458
19646
|
delete manifest[tomb.name];
|
|
19459
19647
|
manifestDirty = true;
|
|
19460
19648
|
continue;
|
|
19461
19649
|
}
|
|
19462
19650
|
try {
|
|
19463
|
-
const actual =
|
|
19651
|
+
const actual = createHash18("sha256").update(readFileSync30(filePath)).digest("hex");
|
|
19464
19652
|
if (actual !== entry.sha) {
|
|
19465
19653
|
outcomes.kept_modified++;
|
|
19466
19654
|
outcomes.unapplied.push({ name: tomb.name, reason: "kept_modified" });
|
|
@@ -19470,16 +19658,16 @@ function applyMemoryTombstones(opts) {
|
|
|
19470
19658
|
continue;
|
|
19471
19659
|
}
|
|
19472
19660
|
const destDir = retiredMemoryDir(configDir, agent.code_name);
|
|
19473
|
-
|
|
19661
|
+
mkdirSync12(destDir, { recursive: true });
|
|
19474
19662
|
const stamp = Date.now();
|
|
19475
|
-
let dest =
|
|
19476
|
-
for (let n = 1;
|
|
19477
|
-
dest =
|
|
19663
|
+
let dest = join39(destDir, `${entry.file}.${stamp}.retired`);
|
|
19664
|
+
for (let n = 1; existsSync17(dest); n++) {
|
|
19665
|
+
dest = join39(destDir, `${entry.file}.${stamp}-${n}.retired`);
|
|
19478
19666
|
}
|
|
19479
|
-
|
|
19667
|
+
renameSync9(filePath, dest);
|
|
19480
19668
|
try {
|
|
19481
19669
|
const now = Date.now() / 1e3;
|
|
19482
|
-
|
|
19670
|
+
utimesSync2(dest, now, now);
|
|
19483
19671
|
} catch {
|
|
19484
19672
|
}
|
|
19485
19673
|
delete manifest[tomb.name];
|
|
@@ -19507,15 +19695,15 @@ function applyMemoryTombstones(opts) {
|
|
|
19507
19695
|
}
|
|
19508
19696
|
function reapRetiredMemories(configDir, codeName, log2) {
|
|
19509
19697
|
const dir = retiredMemoryDir(configDir, codeName);
|
|
19510
|
-
if (!
|
|
19698
|
+
if (!existsSync17(dir)) return;
|
|
19511
19699
|
const cutoff = Date.now() - RETIRED_MEMORY_TTL_MS;
|
|
19512
19700
|
let reaped = 0;
|
|
19513
19701
|
try {
|
|
19514
|
-
for (const file of
|
|
19702
|
+
for (const file of readdirSync10(dir)) {
|
|
19515
19703
|
if (!file.endsWith(".retired")) continue;
|
|
19516
|
-
const path =
|
|
19704
|
+
const path = join39(dir, file);
|
|
19517
19705
|
try {
|
|
19518
|
-
if (
|
|
19706
|
+
if (statSync9(path).mtimeMs < cutoff) {
|
|
19519
19707
|
unlinkSync6(path);
|
|
19520
19708
|
reaped++;
|
|
19521
19709
|
}
|
|
@@ -19528,8 +19716,8 @@ function reapRetiredMemories(configDir, codeName, log2) {
|
|
|
19528
19716
|
if (reaped > 0) log2(`[memory-retire] ${codeName}: reaped ${reaped} retired memory file(s) past TTL`);
|
|
19529
19717
|
}
|
|
19530
19718
|
async function syncMemories(agent, configDir, log2) {
|
|
19531
|
-
const projectDir =
|
|
19532
|
-
const memoryDir =
|
|
19719
|
+
const projectDir = join39(configDir, agent.code_name, "project");
|
|
19720
|
+
const memoryDir = join39(projectDir, "memory");
|
|
19533
19721
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
19534
19722
|
if (isFreshSync) {
|
|
19535
19723
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -19553,15 +19741,15 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
19553
19741
|
}
|
|
19554
19742
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
19555
19743
|
}
|
|
19556
|
-
if (
|
|
19744
|
+
if (existsSync17(memoryDir)) {
|
|
19557
19745
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
19558
19746
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
19559
19747
|
const changedMemories = [];
|
|
19560
|
-
for (const file of
|
|
19748
|
+
for (const file of readdirSync10(memoryDir)) {
|
|
19561
19749
|
if (!file.endsWith(".md")) continue;
|
|
19562
19750
|
try {
|
|
19563
|
-
const raw =
|
|
19564
|
-
const fileHash =
|
|
19751
|
+
const raw = readFileSync30(join39(memoryDir, file), "utf-8");
|
|
19752
|
+
const fileHash = createHash18("sha256").update(raw).digest("hex").slice(0, 16);
|
|
19565
19753
|
currentHashes.set(file, fileHash);
|
|
19566
19754
|
if (prevHashes.get(file) === fileHash) continue;
|
|
19567
19755
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -19585,7 +19773,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
19585
19773
|
} catch (err) {
|
|
19586
19774
|
for (const mem of changedMemories) {
|
|
19587
19775
|
for (const [file] of currentHashes) {
|
|
19588
|
-
const parsed = parseMemoryFile(
|
|
19776
|
+
const parsed = parseMemoryFile(readFileSync30(join39(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
19589
19777
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
19590
19778
|
}
|
|
19591
19779
|
}
|
|
@@ -19611,8 +19799,8 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
19611
19799
|
}
|
|
19612
19800
|
}
|
|
19613
19801
|
async function downloadMemories(agent, memoryDir, log2, { force, configDir }) {
|
|
19614
|
-
const localFiles =
|
|
19615
|
-
const localListHash =
|
|
19802
|
+
const localFiles = existsSync17(memoryDir) ? readdirSync10(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
19803
|
+
const localListHash = createHash18("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
19616
19804
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
19617
19805
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
19618
19806
|
try {
|
|
@@ -19620,14 +19808,14 @@ async function downloadMemories(agent, memoryDir, log2, { force, configDir }) {
|
|
|
19620
19808
|
agent_id: agent.agent_id
|
|
19621
19809
|
});
|
|
19622
19810
|
const tombstones = Array.isArray(dbMemories.tombstones) ? dbMemories.tombstones : null;
|
|
19623
|
-
const responseHash =
|
|
19811
|
+
const responseHash = createHash18("sha256").update(JSON.stringify({ m: dbMemories.memories ?? [], t: tombstones })).digest("hex").slice(0, 16);
|
|
19624
19812
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
19625
19813
|
return { status: "skipped", tombstones, truncated: dbMemories.tombstones_truncated === true };
|
|
19626
19814
|
}
|
|
19627
19815
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
19628
19816
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
19629
19817
|
if (dbMemories.memories?.length) {
|
|
19630
|
-
|
|
19818
|
+
mkdirSync12(memoryDir, { recursive: true });
|
|
19631
19819
|
const manifest = loadMemoryManifest(agent.agent_id, configDir, agent.code_name);
|
|
19632
19820
|
let written = 0;
|
|
19633
19821
|
let overwritten = 0;
|
|
@@ -19636,7 +19824,7 @@ async function downloadMemories(agent, memoryDir, log2, { force, configDir }) {
|
|
|
19636
19824
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
19637
19825
|
const slug = rawSlug || `memory-${i}`;
|
|
19638
19826
|
const fileName = `${slug}.md`;
|
|
19639
|
-
const filePath =
|
|
19827
|
+
const filePath = join39(memoryDir, fileName);
|
|
19640
19828
|
const desired = `---
|
|
19641
19829
|
name: ${JSON.stringify(mem.name)}
|
|
19642
19830
|
type: ${mem.type}
|
|
@@ -19646,17 +19834,17 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
19646
19834
|
${mem.content}
|
|
19647
19835
|
`;
|
|
19648
19836
|
const record = () => {
|
|
19649
|
-
const sha =
|
|
19837
|
+
const sha = createHash18("sha256").update(desired).digest("hex");
|
|
19650
19838
|
const prev = manifest[mem.name];
|
|
19651
19839
|
if (prev?.file === fileName && prev.sha === sha) return false;
|
|
19652
19840
|
manifest[mem.name] = { file: fileName, sha };
|
|
19653
19841
|
return true;
|
|
19654
19842
|
};
|
|
19655
19843
|
let manifestDirty = false;
|
|
19656
|
-
if (
|
|
19844
|
+
if (existsSync17(filePath)) {
|
|
19657
19845
|
let existing = "";
|
|
19658
19846
|
try {
|
|
19659
|
-
existing =
|
|
19847
|
+
existing = readFileSync30(filePath, "utf-8");
|
|
19660
19848
|
} catch {
|
|
19661
19849
|
}
|
|
19662
19850
|
if (existing === desired) {
|
|
@@ -19664,18 +19852,18 @@ ${mem.content}
|
|
|
19664
19852
|
if (manifestDirty) saveMemoryManifest(agent.agent_id, configDir, agent.code_name);
|
|
19665
19853
|
continue;
|
|
19666
19854
|
}
|
|
19667
|
-
|
|
19855
|
+
writeFileSync16(filePath, desired);
|
|
19668
19856
|
overwritten++;
|
|
19669
19857
|
} else {
|
|
19670
|
-
|
|
19858
|
+
writeFileSync16(filePath, desired);
|
|
19671
19859
|
written++;
|
|
19672
19860
|
}
|
|
19673
19861
|
if (record()) manifestDirty = true;
|
|
19674
19862
|
if (manifestDirty) saveMemoryManifest(agent.agent_id, configDir, agent.code_name);
|
|
19675
19863
|
}
|
|
19676
19864
|
if (written > 0 || overwritten > 0) {
|
|
19677
|
-
const updatedFiles =
|
|
19678
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
19865
|
+
const updatedFiles = readdirSync10(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
19866
|
+
lastLocalFileHash.set(agent.agent_id, createHash18("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
19679
19867
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
19680
19868
|
}
|
|
19681
19869
|
}
|
|
@@ -19686,9 +19874,9 @@ ${mem.content}
|
|
|
19686
19874
|
}
|
|
19687
19875
|
}
|
|
19688
19876
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
19689
|
-
if (
|
|
19877
|
+
if (existsSync17(agentDir)) {
|
|
19690
19878
|
try {
|
|
19691
|
-
|
|
19879
|
+
rmSync6(agentDir, { recursive: true, force: true });
|
|
19692
19880
|
log(`Removed provision directory for '${codeName}'`);
|
|
19693
19881
|
} catch (err) {
|
|
19694
19882
|
log(`Failed to remove provision dir for '${codeName}': ${err.message}`);
|
|
@@ -19956,8 +20144,8 @@ function startManager(opts) {
|
|
|
19956
20144
|
config = opts;
|
|
19957
20145
|
try {
|
|
19958
20146
|
const stateFile = getStateFile();
|
|
19959
|
-
if (
|
|
19960
|
-
const raw =
|
|
20147
|
+
if (existsSync17(stateFile)) {
|
|
20148
|
+
const raw = readFileSync30(stateFile, "utf-8");
|
|
19961
20149
|
const parsed = JSON.parse(raw);
|
|
19962
20150
|
if (Array.isArray(parsed.agents)) {
|
|
19963
20151
|
state8.agents = parsed.agents;
|
|
@@ -19984,7 +20172,7 @@ function startManager(opts) {
|
|
|
19984
20172
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
19985
20173
|
}
|
|
19986
20174
|
log(
|
|
19987
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
20175
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join39(homedir17(), ".augmented", "manager.log")}`
|
|
19988
20176
|
);
|
|
19989
20177
|
deployMcpAssets();
|
|
19990
20178
|
reapOrphanChannelMcps({ log });
|
|
@@ -20013,7 +20201,7 @@ async function reapOrphanedClaudePids() {
|
|
|
20013
20201
|
const looksLikeClaude = (pid) => {
|
|
20014
20202
|
if (process.platform !== "linux") return true;
|
|
20015
20203
|
try {
|
|
20016
|
-
const comm =
|
|
20204
|
+
const comm = readFileSync30(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
20017
20205
|
return comm.includes("claude");
|
|
20018
20206
|
} catch {
|
|
20019
20207
|
return false;
|
|
@@ -20110,14 +20298,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
20110
20298
|
}
|
|
20111
20299
|
}
|
|
20112
20300
|
function deployMcpAssets() {
|
|
20113
|
-
const targetDir =
|
|
20114
|
-
|
|
20301
|
+
const targetDir = join39(homedir17(), ".augmented", "_mcp");
|
|
20302
|
+
mkdirSync12(targetDir, { recursive: true });
|
|
20115
20303
|
const moduleDir = dirname10(fileURLToPath(import.meta.url));
|
|
20116
20304
|
let mcpSourceDir = "";
|
|
20117
20305
|
let dir = moduleDir;
|
|
20118
20306
|
for (let i = 0; i < 6; i++) {
|
|
20119
|
-
const candidate =
|
|
20120
|
-
if (
|
|
20307
|
+
const candidate = join39(dir, "dist", "mcp");
|
|
20308
|
+
if (existsSync17(join39(candidate, "index.js"))) {
|
|
20121
20309
|
mcpSourceDir = candidate;
|
|
20122
20310
|
break;
|
|
20123
20311
|
}
|
|
@@ -20134,8 +20322,8 @@ function deployMcpAssets() {
|
|
|
20134
20322
|
const failedFiles = [];
|
|
20135
20323
|
const fileHash = (p) => {
|
|
20136
20324
|
try {
|
|
20137
|
-
if (!
|
|
20138
|
-
return
|
|
20325
|
+
if (!existsSync17(p)) return null;
|
|
20326
|
+
return createHash18("sha256").update(readFileSync30(p)).digest("hex");
|
|
20139
20327
|
} catch {
|
|
20140
20328
|
return null;
|
|
20141
20329
|
}
|
|
@@ -20206,9 +20394,9 @@ function deployMcpAssets() {
|
|
|
20206
20394
|
// needs restarting to pick up a token rotation.
|
|
20207
20395
|
"xero.js"
|
|
20208
20396
|
]) {
|
|
20209
|
-
const src =
|
|
20210
|
-
const dst =
|
|
20211
|
-
if (!
|
|
20397
|
+
const src = join39(mcpSourceDir, file);
|
|
20398
|
+
const dst = join39(targetDir, file);
|
|
20399
|
+
if (!existsSync17(src)) continue;
|
|
20212
20400
|
attemptedFiles.push(file);
|
|
20213
20401
|
const before = fileHash(dst);
|
|
20214
20402
|
try {
|
|
@@ -20233,23 +20421,23 @@ function deployMcpAssets() {
|
|
|
20233
20421
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
20234
20422
|
restartRunningChannelMcps(changedBasenames);
|
|
20235
20423
|
}
|
|
20236
|
-
const localMcpPath =
|
|
20424
|
+
const localMcpPath = join39(targetDir, "index.js");
|
|
20237
20425
|
try {
|
|
20238
|
-
const agentsDir =
|
|
20239
|
-
if (
|
|
20240
|
-
for (const entry of
|
|
20426
|
+
const agentsDir = join39(homedir17(), ".augmented", "agents");
|
|
20427
|
+
if (existsSync17(agentsDir)) {
|
|
20428
|
+
for (const entry of readdirSync10(agentsDir, { withFileTypes: true })) {
|
|
20241
20429
|
if (!entry.isDirectory()) continue;
|
|
20242
20430
|
for (const subdir of ["provision", "project"]) {
|
|
20243
|
-
const mcpJsonPath =
|
|
20431
|
+
const mcpJsonPath = join39(agentsDir, entry.name, subdir, ".mcp.json");
|
|
20244
20432
|
try {
|
|
20245
|
-
const raw =
|
|
20433
|
+
const raw = readFileSync30(mcpJsonPath, "utf-8");
|
|
20246
20434
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
20247
20435
|
const mcpConfig = JSON.parse(raw);
|
|
20248
20436
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
20249
20437
|
if (!augServer) continue;
|
|
20250
20438
|
augServer.command = "node";
|
|
20251
20439
|
augServer.args = [localMcpPath];
|
|
20252
|
-
|
|
20440
|
+
writeFileSync16(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
20253
20441
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
20254
20442
|
} catch {
|
|
20255
20443
|
}
|