@integrity-labs/agt-cli 0.28.343 → 0.28.345
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 +4 -4
- package/dist/{chunk-GAYSFNTX.js → chunk-GJ5TGARW.js} +15 -3
- package/dist/{chunk-GAYSFNTX.js.map → chunk-GJ5TGARW.js.map} +1 -1
- package/dist/{chunk-7OBFGSQS.js → chunk-O5IINJDB.js} +62 -15
- package/dist/chunk-O5IINJDB.js.map +1 -0
- package/dist/{claude-pair-runtime-NUZWSILK.js → claude-pair-runtime-2ZQZVFKF.js} +2 -2
- package/dist/lib/manager-worker.js +980 -257
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +25032 -5137
- package/dist/mcp/origami.js +22 -5
- package/dist/mcp/slack-channel.js +22629 -2702
- package/dist/mcp/telegram-channel.js +25344 -5426
- package/dist/{persistent-session-XWJSAZB4.js → persistent-session-6ADHOTEG.js} +2 -2
- package/dist/{responsiveness-probe-XLS2FXIX.js → responsiveness-probe-UYVXWW5D.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-7OBFGSQS.js.map +0 -1
- /package/dist/{claude-pair-runtime-NUZWSILK.js.map → claude-pair-runtime-2ZQZVFKF.js.map} +0 -0
- /package/dist/{persistent-session-XWJSAZB4.js.map → persistent-session-6ADHOTEG.js.map} +0 -0
- /package/dist/{responsiveness-probe-XLS2FXIX.js.map → responsiveness-probe-UYVXWW5D.js.map} +0 -0
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
resolveEffectivePinRaw,
|
|
47
47
|
safeWriteJsonAtomic,
|
|
48
48
|
setConfigHash
|
|
49
|
-
} from "../chunk-
|
|
49
|
+
} from "../chunk-GJ5TGARW.js";
|
|
50
50
|
import {
|
|
51
51
|
getProjectDir as getProjectDir2,
|
|
52
52
|
getReadyTasks,
|
|
@@ -66,6 +66,7 @@ import {
|
|
|
66
66
|
SCHEDULED_TURN_MARKER_FILENAME,
|
|
67
67
|
SUPPRESS_SENTINEL,
|
|
68
68
|
StreamEncoder,
|
|
69
|
+
USAGE_LIMIT_MARKER_FILENAME,
|
|
69
70
|
appendDmFooter,
|
|
70
71
|
attributeTranscriptUsageByRun,
|
|
71
72
|
buildEgressAllowlist,
|
|
@@ -125,6 +126,7 @@ import {
|
|
|
125
126
|
restartEgressSidecar,
|
|
126
127
|
rotateSessionForWedge,
|
|
127
128
|
sendToAgent,
|
|
129
|
+
serializeUsageLimitMarker,
|
|
128
130
|
sessionTranscriptDir,
|
|
129
131
|
startPersistentSession,
|
|
130
132
|
stopAllSessionsAndWait,
|
|
@@ -135,17 +137,17 @@ import {
|
|
|
135
137
|
takeZombieDetection,
|
|
136
138
|
transcriptActivityAgeSeconds,
|
|
137
139
|
writeEgressAllowlist
|
|
138
|
-
} from "../chunk-
|
|
140
|
+
} from "../chunk-O5IINJDB.js";
|
|
139
141
|
import {
|
|
140
142
|
reapOrphanChannelMcps
|
|
141
143
|
} from "../chunk-XWVM4KPK.js";
|
|
142
144
|
|
|
143
145
|
// src/lib/manager-worker.ts
|
|
144
|
-
import { createHash as
|
|
145
|
-
import { readFileSync as
|
|
146
|
+
import { createHash as createHash14 } from "crypto";
|
|
147
|
+
import { readFileSync as readFileSync19, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, rmSync as rmSync5, readdirSync as readdirSync5, statSync as statSync4, copyFileSync } from "fs";
|
|
146
148
|
import { execFileSync as syncExecFile } from "child_process";
|
|
147
|
-
import { join as
|
|
148
|
-
import { homedir as
|
|
149
|
+
import { join as join23, dirname as dirname6 } from "path";
|
|
150
|
+
import { homedir as homedir12 } from "os";
|
|
149
151
|
import { fileURLToPath } from "url";
|
|
150
152
|
|
|
151
153
|
// src/lib/claude-code-upgrade-throttle.ts
|
|
@@ -1860,6 +1862,22 @@ var nodeArtifactFs = {
|
|
|
1860
1862
|
};
|
|
1861
1863
|
|
|
1862
1864
|
// src/lib/usage-banner-monitor.ts
|
|
1865
|
+
import { mkdirSync as mkdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
1866
|
+
import { homedir as homedir3 } from "os";
|
|
1867
|
+
import { join as join5 } from "path";
|
|
1868
|
+
function syncUsageLimitMarker(args) {
|
|
1869
|
+
const path = join5(homedir3(), ".augmented", args.codeName, USAGE_LIMIT_MARKER_FILENAME);
|
|
1870
|
+
try {
|
|
1871
|
+
if (args.pct >= 100) {
|
|
1872
|
+
mkdirSync4(join5(homedir3(), ".augmented", args.codeName), { recursive: true });
|
|
1873
|
+
writeFileSync4(path, serializeUsageLimitMarker(args.weekResetsAt), "utf-8");
|
|
1874
|
+
} else {
|
|
1875
|
+
rmSync2(path, { force: true });
|
|
1876
|
+
}
|
|
1877
|
+
} catch (err) {
|
|
1878
|
+
args.log(`[usage-banner] usage-limit marker sync failed for '${args.codeName}': ${err.message}`);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1863
1881
|
var MIN_CHECK_INTERVAL_MS = 6e4;
|
|
1864
1882
|
var PANE_TAIL_LINES_FOR_BANNER = 5e3;
|
|
1865
1883
|
var state = /* @__PURE__ */ new Map();
|
|
@@ -1892,6 +1910,12 @@ async function maybeReportUsageBanner(args) {
|
|
|
1892
1910
|
state.set(codeName, next);
|
|
1893
1911
|
return;
|
|
1894
1912
|
}
|
|
1913
|
+
syncUsageLimitMarker({
|
|
1914
|
+
codeName,
|
|
1915
|
+
pct: observation.pct,
|
|
1916
|
+
weekResetsAt: observation.weekResetsAt,
|
|
1917
|
+
log: log2
|
|
1918
|
+
});
|
|
1895
1919
|
const observedAtIso = next.lastWeekResetsAt;
|
|
1896
1920
|
const weekResetsAtIso = observation.weekResetsAt.toISOString();
|
|
1897
1921
|
if (existing && existing.lastPct === observation.pct && observedAtIso === weekResetsAtIso) {
|
|
@@ -1916,7 +1940,7 @@ async function maybeReportUsageBanner(args) {
|
|
|
1916
1940
|
|
|
1917
1941
|
// src/lib/token-usage-monitor.ts
|
|
1918
1942
|
import { readdirSync, readFileSync as readFileSync6, statSync } from "fs";
|
|
1919
|
-
import { join as
|
|
1943
|
+
import { join as join6 } from "path";
|
|
1920
1944
|
var MIN_CHECK_INTERVAL_MS2 = 6e4;
|
|
1921
1945
|
var TRANSCRIPT_MTIME_WINDOW_MS = 2 * 24 * 60 * 60 * 1e3;
|
|
1922
1946
|
var MAX_ENTRIES_PER_POST = 200;
|
|
@@ -1945,7 +1969,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
1945
1969
|
if (!name.endsWith(".jsonl")) continue;
|
|
1946
1970
|
const sessionId = name.slice(0, -".jsonl".length);
|
|
1947
1971
|
if (!sessionId) continue;
|
|
1948
|
-
const path =
|
|
1972
|
+
const path = join6(dir, name);
|
|
1949
1973
|
let st;
|
|
1950
1974
|
try {
|
|
1951
1975
|
st = statSync(path);
|
|
@@ -2043,7 +2067,7 @@ async function maybeReportTokenUsage(args) {
|
|
|
2043
2067
|
|
|
2044
2068
|
// src/lib/workflow-run-reconciler.ts
|
|
2045
2069
|
import { readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync2 } from "fs";
|
|
2046
|
-
import { join as
|
|
2070
|
+
import { join as join7 } from "path";
|
|
2047
2071
|
var MIN_CHECK_INTERVAL_MS3 = 5 * 6e4;
|
|
2048
2072
|
var SETTLE_MS = 3e4;
|
|
2049
2073
|
var TRANSCRIPT_MTIME_WINDOW_MS2 = 2 * 24 * 60 * 60 * 1e3;
|
|
@@ -2062,7 +2086,7 @@ function collectJsonlRecursive(dir, minMtimeMs, out, depth) {
|
|
|
2062
2086
|
return;
|
|
2063
2087
|
}
|
|
2064
2088
|
for (const name of entries) {
|
|
2065
|
-
const p =
|
|
2089
|
+
const p = join7(dir, name);
|
|
2066
2090
|
let st;
|
|
2067
2091
|
try {
|
|
2068
2092
|
st = statSync2(p);
|
|
@@ -2085,7 +2109,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
|
|
|
2085
2109
|
return out;
|
|
2086
2110
|
}
|
|
2087
2111
|
for (const name of entries) {
|
|
2088
|
-
const path =
|
|
2112
|
+
const path = join7(transcriptDir, name);
|
|
2089
2113
|
let st;
|
|
2090
2114
|
try {
|
|
2091
2115
|
st = statSync2(path);
|
|
@@ -2097,7 +2121,7 @@ function enumerateTranscriptFiles(transcriptDir, nowMs, minMtimeMs = nowMs - TRA
|
|
|
2097
2121
|
continue;
|
|
2098
2122
|
}
|
|
2099
2123
|
if (st.isDirectory()) {
|
|
2100
|
-
collectJsonlRecursive(
|
|
2124
|
+
collectJsonlRecursive(join7(path, "subagents"), minMtimeMs, out, 0);
|
|
2101
2125
|
}
|
|
2102
2126
|
}
|
|
2103
2127
|
return out;
|
|
@@ -2188,7 +2212,7 @@ async function maybeReconcileWorkflowRunTokens(args) {
|
|
|
2188
2212
|
|
|
2189
2213
|
// src/lib/conversation-evaluator.ts
|
|
2190
2214
|
import { readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
2191
|
-
import { join as
|
|
2215
|
+
import { join as join8 } from "path";
|
|
2192
2216
|
var MIN_CHECK_INTERVAL_MS4 = 5 * 6e4;
|
|
2193
2217
|
var TRANSCRIPT_MTIME_WINDOW_MS3 = 7 * 24 * 60 * 60 * 1e3;
|
|
2194
2218
|
var WINDOW_PAD_MS = 5 * 6e4;
|
|
@@ -2624,7 +2648,7 @@ function readRecentTurns(dir, nowMs) {
|
|
|
2624
2648
|
return;
|
|
2625
2649
|
}
|
|
2626
2650
|
for (const ent of entries) {
|
|
2627
|
-
const full =
|
|
2651
|
+
const full = join8(d, ent.name);
|
|
2628
2652
|
if (ent.isDirectory()) {
|
|
2629
2653
|
visit(full);
|
|
2630
2654
|
continue;
|
|
@@ -2930,10 +2954,10 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
|
|
|
2930
2954
|
|
|
2931
2955
|
// src/lib/activity-cache-monitor.ts
|
|
2932
2956
|
import { existsSync as existsSync2, readFileSync as readFileSync9 } from "fs";
|
|
2933
|
-
import { homedir as
|
|
2934
|
-
import { join as
|
|
2957
|
+
import { homedir as homedir4 } from "os";
|
|
2958
|
+
import { join as join9 } from "path";
|
|
2935
2959
|
var MIN_CHECK_INTERVAL_MS6 = 6e4;
|
|
2936
|
-
var STATS_CACHE_PATH =
|
|
2960
|
+
var STATS_CACHE_PATH = join9(homedir4(), ".claude", "stats-cache.json");
|
|
2937
2961
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
2938
2962
|
var state5 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
2939
2963
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
@@ -3167,8 +3191,8 @@ function bundleFingerprint(files) {
|
|
|
3167
3191
|
|
|
3168
3192
|
// src/lib/claude-auth-detect.ts
|
|
3169
3193
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
3170
|
-
import { homedir as
|
|
3171
|
-
import { join as
|
|
3194
|
+
import { homedir as homedir5, platform } from "os";
|
|
3195
|
+
import { join as join10 } from "path";
|
|
3172
3196
|
import { execFile } from "child_process";
|
|
3173
3197
|
import { promisify } from "util";
|
|
3174
3198
|
var execFileAsync = promisify(execFile);
|
|
@@ -3183,8 +3207,8 @@ async function detectClaudeAuth() {
|
|
|
3183
3207
|
}
|
|
3184
3208
|
async function findClaudeCredentialsPaths() {
|
|
3185
3209
|
const candidates = [
|
|
3186
|
-
|
|
3187
|
-
|
|
3210
|
+
join10(homedir5(), ".claude", ".credentials.json"),
|
|
3211
|
+
join10(homedir5(), ".claude", "credentials.json")
|
|
3188
3212
|
];
|
|
3189
3213
|
const isLinuxRoot = platform() === "linux" && typeof process.getuid === "function" && process.getuid() === 0;
|
|
3190
3214
|
if (isLinuxRoot) {
|
|
@@ -3192,8 +3216,8 @@ async function findClaudeCredentialsPaths() {
|
|
|
3192
3216
|
const entries = await readdir2("/home", { withFileTypes: true });
|
|
3193
3217
|
for (const entry of entries) {
|
|
3194
3218
|
if (!entry.isDirectory()) continue;
|
|
3195
|
-
candidates.push(
|
|
3196
|
-
candidates.push(
|
|
3219
|
+
candidates.push(join10("/home", entry.name, ".claude", ".credentials.json"));
|
|
3220
|
+
candidates.push(join10("/home", entry.name, ".claude", "credentials.json"));
|
|
3197
3221
|
}
|
|
3198
3222
|
} catch {
|
|
3199
3223
|
}
|
|
@@ -3289,11 +3313,11 @@ function computeChannelConfigHash(input) {
|
|
|
3289
3313
|
}
|
|
3290
3314
|
|
|
3291
3315
|
// src/lib/channel-hash-cache.ts
|
|
3292
|
-
import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as
|
|
3293
|
-
import { join as
|
|
3316
|
+
import { existsSync as existsSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
3317
|
+
import { join as join11 } from "path";
|
|
3294
3318
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
3295
3319
|
function getChannelHashCacheFile(configDir) {
|
|
3296
|
-
return
|
|
3320
|
+
return join11(configDir, CACHE_FILENAME);
|
|
3297
3321
|
}
|
|
3298
3322
|
function loadChannelHashCache(target, configDir) {
|
|
3299
3323
|
const path = getChannelHashCacheFile(configDir);
|
|
@@ -3314,14 +3338,14 @@ function saveChannelHashCache(source, configDir) {
|
|
|
3314
3338
|
const obj = {};
|
|
3315
3339
|
for (const [key, value] of source) obj[key] = value;
|
|
3316
3340
|
try {
|
|
3317
|
-
|
|
3341
|
+
writeFileSync5(path, JSON.stringify(obj, null, 2));
|
|
3318
3342
|
} catch {
|
|
3319
3343
|
}
|
|
3320
3344
|
}
|
|
3321
3345
|
|
|
3322
3346
|
// src/lib/sender-policy-baseline.ts
|
|
3323
3347
|
import { existsSync as existsSync4, readFileSync as readFileSync11 } from "fs";
|
|
3324
|
-
import { join as
|
|
3348
|
+
import { join as join12 } from "path";
|
|
3325
3349
|
var BASELINE_FILENAME = "sender-policy-baseline.json";
|
|
3326
3350
|
var SENDER_POLICY_BASELINE_VERSION = 1;
|
|
3327
3351
|
var BASELINE_CONCERNS = ["senderPolicy", "slackBehaviour", "msteamsBehaviour"];
|
|
@@ -3333,7 +3357,7 @@ function createDeliveryBaselineMaps() {
|
|
|
3333
3357
|
};
|
|
3334
3358
|
}
|
|
3335
3359
|
function getSenderPolicyBaselineFile(configDir) {
|
|
3336
|
-
return
|
|
3360
|
+
return join12(configDir, BASELINE_FILENAME);
|
|
3337
3361
|
}
|
|
3338
3362
|
function loadSenderPolicyBaseline(target, configDir, log2) {
|
|
3339
3363
|
const path = getSenderPolicyBaselineFile(configDir);
|
|
@@ -3827,11 +3851,11 @@ function planGlobalSkillSync(globalSkills, prevIds, hashOf, knownHash, options)
|
|
|
3827
3851
|
}
|
|
3828
3852
|
|
|
3829
3853
|
// src/lib/manager/managed-skill-manifest.ts
|
|
3830
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
3831
|
-
import { dirname as dirname4, join as
|
|
3854
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
3855
|
+
import { dirname as dirname4, join as join13 } from "path";
|
|
3832
3856
|
var MANIFEST_VERSION = 1;
|
|
3833
3857
|
function managedSkillManifestPath(agentRootDir) {
|
|
3834
|
-
return
|
|
3858
|
+
return join13(agentRootDir, "managed-skills.json");
|
|
3835
3859
|
}
|
|
3836
3860
|
function readManagedSkillManifest(path) {
|
|
3837
3861
|
try {
|
|
@@ -3845,21 +3869,21 @@ function readManagedSkillManifest(path) {
|
|
|
3845
3869
|
}
|
|
3846
3870
|
function writeManagedSkillManifest(path, ids) {
|
|
3847
3871
|
try {
|
|
3848
|
-
|
|
3872
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
3849
3873
|
const body = {
|
|
3850
3874
|
version: MANIFEST_VERSION,
|
|
3851
3875
|
globalSkillIds: [...ids].sort()
|
|
3852
3876
|
};
|
|
3853
|
-
|
|
3877
|
+
writeFileSync6(path, JSON.stringify(body, null, 2));
|
|
3854
3878
|
} catch {
|
|
3855
3879
|
}
|
|
3856
3880
|
}
|
|
3857
3881
|
|
|
3858
3882
|
// src/lib/manager/runtime.ts
|
|
3859
3883
|
import { createHash as createHash6 } from "crypto";
|
|
3860
|
-
import { readFileSync as readFileSync13, appendFileSync, mkdirSync as
|
|
3861
|
-
import { join as
|
|
3862
|
-
import { homedir as
|
|
3884
|
+
import { readFileSync as readFileSync13, appendFileSync, mkdirSync as mkdirSync6, chmodSync, existsSync as existsSync6 } from "fs";
|
|
3885
|
+
import { join as join14, dirname as dirname5 } from "path";
|
|
3886
|
+
import { homedir as homedir6 } from "os";
|
|
3863
3887
|
function redactForDiskLog(value) {
|
|
3864
3888
|
try {
|
|
3865
3889
|
return value.replace(/\b(Bearer\s+)[A-Za-z0-9._-]+\b/gi, "$1[REDACTED]").replace(/\bxox[baprs]-[A-Za-z0-9-]+\b/g, "[REDACTED-SLACK]").replace(/\btlk_[A-Za-z0-9._-]+\b/g, "[REDACTED-HOST]").replace(/\bsk-ant-[A-Za-z0-9_-]+\b/g, "[REDACTED-ANTHROPIC]").replace(/\b\d{8,12}:[A-Za-z0-9_-]{30,}\b/g, "[REDACTED-TELEGRAM]").replace(
|
|
@@ -3879,8 +3903,8 @@ function log(msg) {
|
|
|
3879
3903
|
`;
|
|
3880
3904
|
if (!managerLogPath) {
|
|
3881
3905
|
try {
|
|
3882
|
-
managerLogPath =
|
|
3883
|
-
|
|
3906
|
+
managerLogPath = join14(homedir6(), ".augmented", "manager.log");
|
|
3907
|
+
mkdirSync6(dirname5(managerLogPath), { recursive: true });
|
|
3884
3908
|
if (existsSync6(managerLogPath)) {
|
|
3885
3909
|
chmodSync(managerLogPath, 384);
|
|
3886
3910
|
}
|
|
@@ -4067,9 +4091,9 @@ function resolveModelChain(refreshData) {
|
|
|
4067
4091
|
}
|
|
4068
4092
|
|
|
4069
4093
|
// src/lib/manager/claude-auth.ts
|
|
4070
|
-
import { existsSync as existsSync7, rmSync as
|
|
4071
|
-
import { join as
|
|
4072
|
-
import { homedir as
|
|
4094
|
+
import { existsSync as existsSync7, rmSync as rmSync3 } from "fs";
|
|
4095
|
+
import { join as join15 } from "path";
|
|
4096
|
+
import { homedir as homedir7 } from "os";
|
|
4073
4097
|
async function applyClaudeAuthToEnv(childEnv, label) {
|
|
4074
4098
|
const apiKey = getApiKey();
|
|
4075
4099
|
if (!apiKey) {
|
|
@@ -4081,12 +4105,12 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4081
4105
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
4082
4106
|
}
|
|
4083
4107
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
4084
|
-
const claudeDir =
|
|
4108
|
+
const claudeDir = join15(homedir7(), ".claude");
|
|
4085
4109
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
4086
|
-
const p =
|
|
4110
|
+
const p = join15(claudeDir, filename);
|
|
4087
4111
|
if (existsSync7(p)) {
|
|
4088
4112
|
try {
|
|
4089
|
-
|
|
4113
|
+
rmSync3(p, { force: true });
|
|
4090
4114
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
4091
4115
|
} catch {
|
|
4092
4116
|
}
|
|
@@ -4099,7 +4123,7 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4099
4123
|
|
|
4100
4124
|
// src/lib/manager/kanban/parsers.ts
|
|
4101
4125
|
import { existsSync as existsSync8, readFileSync as readFileSync14 } from "fs";
|
|
4102
|
-
import { join as
|
|
4126
|
+
import { join as join16 } from "path";
|
|
4103
4127
|
var STANDUP_TEMPLATES = /* @__PURE__ */ new Set(["daily-standup", "end-of-day-summary"]);
|
|
4104
4128
|
var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-update"]);
|
|
4105
4129
|
var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
|
|
@@ -4235,8 +4259,8 @@ function getBuiltInSkillContent(skillId) {
|
|
|
4235
4259
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
4236
4260
|
try {
|
|
4237
4261
|
const candidates = [
|
|
4238
|
-
|
|
4239
|
-
|
|
4262
|
+
join16(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
4263
|
+
join16(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
4240
4264
|
];
|
|
4241
4265
|
for (const candidate of candidates) {
|
|
4242
4266
|
if (existsSync8(candidate)) {
|
|
@@ -4381,12 +4405,12 @@ function formatBoardForPrompt(items, template) {
|
|
|
4381
4405
|
}
|
|
4382
4406
|
|
|
4383
4407
|
// src/lib/manager/kanban/nudge-state-cache.ts
|
|
4384
|
-
import { existsSync as existsSync9, readFileSync as readFileSync15, writeFileSync as
|
|
4385
|
-
import { join as
|
|
4408
|
+
import { existsSync as existsSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync7 } from "fs";
|
|
4409
|
+
import { join as join17 } from "path";
|
|
4386
4410
|
var CACHE_FILENAME2 = "kanban-nudge-state.json";
|
|
4387
4411
|
var KANBAN_NUDGE_STATE_VERSION = 1;
|
|
4388
4412
|
function getKanbanNudgeStateFile(configDir) {
|
|
4389
|
-
return
|
|
4413
|
+
return join17(configDir, CACHE_FILENAME2);
|
|
4390
4414
|
}
|
|
4391
4415
|
function loadKanbanNudgeState(target, configDir) {
|
|
4392
4416
|
const path = getKanbanNudgeStateFile(configDir);
|
|
@@ -4418,7 +4442,7 @@ function saveKanbanNudgeState(source, configDir) {
|
|
|
4418
4442
|
const agents = {};
|
|
4419
4443
|
for (const [codeName, state7] of source) agents[codeName] = state7;
|
|
4420
4444
|
try {
|
|
4421
|
-
|
|
4445
|
+
writeFileSync7(path, JSON.stringify({ version: KANBAN_NUDGE_STATE_VERSION, agents }, null, 2));
|
|
4422
4446
|
} catch {
|
|
4423
4447
|
}
|
|
4424
4448
|
}
|
|
@@ -4959,9 +4983,9 @@ function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
|
4959
4983
|
|
|
4960
4984
|
// src/lib/manager/scheduler/kanban-route.ts
|
|
4961
4985
|
import { createHash as createHash9 } from "crypto";
|
|
4962
|
-
import { writeFileSync as
|
|
4963
|
-
import { homedir as
|
|
4964
|
-
import { join as
|
|
4986
|
+
import { writeFileSync as writeFileSync8, renameSync } from "fs";
|
|
4987
|
+
import { homedir as homedir8 } from "os";
|
|
4988
|
+
import { join as join18 } from "path";
|
|
4965
4989
|
|
|
4966
4990
|
// src/lib/manager/delivery/scheduled-output.ts
|
|
4967
4991
|
async function deliverScheduledTaskOutput(agentCodeName, agentId, rawTarget, body, taskId) {
|
|
@@ -5265,10 +5289,10 @@ function resolveScheduledSlackTarget(task) {
|
|
|
5265
5289
|
}
|
|
5266
5290
|
function stampScheduledTurnMarker(codeName, taskId, target) {
|
|
5267
5291
|
try {
|
|
5268
|
-
const file =
|
|
5292
|
+
const file = join18(homedir8(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
|
|
5269
5293
|
const marker = { ts: Date.now(), task_id: taskId, ...target ? { target } : {} };
|
|
5270
5294
|
const tmp = `${file}.tmp`;
|
|
5271
|
-
|
|
5295
|
+
writeFileSync8(tmp, JSON.stringify(marker), "utf8");
|
|
5272
5296
|
renameSync(tmp, file);
|
|
5273
5297
|
} catch (err) {
|
|
5274
5298
|
log(`[scheduled-kanban] scheduled-turn marker write failed for '${codeName}': ${err.message}`);
|
|
@@ -5451,10 +5475,10 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
5451
5475
|
|
|
5452
5476
|
// src/lib/manager/scheduler/execution.ts
|
|
5453
5477
|
import { createHash as createHash10 } from "crypto";
|
|
5454
|
-
import { homedir as
|
|
5455
|
-
import { join as
|
|
5478
|
+
import { homedir as homedir9 } from "os";
|
|
5479
|
+
import { join as join19 } from "path";
|
|
5456
5480
|
function claudePidFilePath() {
|
|
5457
|
-
return
|
|
5481
|
+
return join19(homedir9(), ".augmented", "manager-claude-pids.json");
|
|
5458
5482
|
}
|
|
5459
5483
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
5460
5484
|
function registerClaudeSpawn(record) {
|
|
@@ -5518,9 +5542,9 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
5518
5542
|
import { spawn, execSync } from "child_process";
|
|
5519
5543
|
import { createServer } from "net";
|
|
5520
5544
|
import { randomBytes } from "crypto";
|
|
5521
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
5522
|
-
import { homedir as
|
|
5523
|
-
import { join as
|
|
5545
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "fs";
|
|
5546
|
+
import { homedir as homedir10, userInfo } from "os";
|
|
5547
|
+
import { join as join20 } from "path";
|
|
5524
5548
|
var OPENCODE_BIN = process.env["AGT_OPENCODE_BIN"]?.trim() || "opencode";
|
|
5525
5549
|
var sessions = /* @__PURE__ */ new Map();
|
|
5526
5550
|
var bridges = /* @__PURE__ */ new Map();
|
|
@@ -5528,7 +5552,7 @@ function opencodeTmuxSession(codeName) {
|
|
|
5528
5552
|
return `agt-oc-${codeName}`;
|
|
5529
5553
|
}
|
|
5530
5554
|
function opencodePaneLogPath(codeName) {
|
|
5531
|
-
return
|
|
5555
|
+
return join20(homedir10(), ".augmented", codeName, "opencode-serve.log");
|
|
5532
5556
|
}
|
|
5533
5557
|
function baseUrlFor(port) {
|
|
5534
5558
|
return `http://127.0.0.1:${port}`;
|
|
@@ -5595,7 +5619,7 @@ async function startOpencodeSession(config2) {
|
|
|
5595
5619
|
}
|
|
5596
5620
|
async function spawnServe(config2, session) {
|
|
5597
5621
|
const { codeName, projectDir, log: log2 } = config2;
|
|
5598
|
-
if (!existsSync10(
|
|
5622
|
+
if (!existsSync10(join20(projectDir, "opencode.json"))) {
|
|
5599
5623
|
log2(`[opencode-session] warning: no opencode.json in ${projectDir} for '${codeName}' (provisioning may not have run)`);
|
|
5600
5624
|
}
|
|
5601
5625
|
const tmuxSession = opencodeTmuxSession(codeName);
|
|
@@ -5605,12 +5629,13 @@ async function spawnServe(config2, session) {
|
|
|
5605
5629
|
execSync(`tmux kill-session -t ${tmuxSession} 2>/dev/null`, { stdio: "ignore" });
|
|
5606
5630
|
} catch {
|
|
5607
5631
|
}
|
|
5608
|
-
|
|
5632
|
+
mkdirSync7(join20(homedir10(), ".augmented", codeName), { recursive: true });
|
|
5609
5633
|
const serveEnv = {
|
|
5610
5634
|
...process.env,
|
|
5635
|
+
...readAgentProviderEnv(codeName),
|
|
5611
5636
|
...stripUndefined(config2.serveEnv ?? {}),
|
|
5612
5637
|
OPENCODE_SERVER_PASSWORD: password,
|
|
5613
|
-
HOME: process.env.HOME?.trim() ||
|
|
5638
|
+
HOME: process.env.HOME?.trim() || homedir10(),
|
|
5614
5639
|
USER: process.env.USER?.trim() || userInfo().username
|
|
5615
5640
|
};
|
|
5616
5641
|
if (config2.runId) serveEnv["AGT_RUN_ID"] = config2.runId;
|
|
@@ -5694,6 +5719,29 @@ function stripUndefined(env) {
|
|
|
5694
5719
|
for (const [k, v] of Object.entries(env)) if (v !== void 0) out[k] = v;
|
|
5695
5720
|
return out;
|
|
5696
5721
|
}
|
|
5722
|
+
var PROVIDER_KEY_RE = /^[A-Z][A-Z0-9_]*_API_KEY$/;
|
|
5723
|
+
function readAgentProviderEnv(codeName, dir) {
|
|
5724
|
+
const file = join20(dir ?? join20(homedir10(), ".augmented", codeName), ".env");
|
|
5725
|
+
const out = {};
|
|
5726
|
+
try {
|
|
5727
|
+
if (!existsSync10(file)) return out;
|
|
5728
|
+
for (const raw of readFileSync16(file, "utf-8").split("\n")) {
|
|
5729
|
+
const line = raw.trim();
|
|
5730
|
+
if (!line || line.startsWith("#")) continue;
|
|
5731
|
+
const eq = line.indexOf("=");
|
|
5732
|
+
if (eq <= 0) continue;
|
|
5733
|
+
const key = line.slice(0, eq).trim();
|
|
5734
|
+
if (!PROVIDER_KEY_RE.test(key)) continue;
|
|
5735
|
+
let val = line.slice(eq + 1).trim();
|
|
5736
|
+
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
|
|
5737
|
+
val = val.slice(1, -1);
|
|
5738
|
+
}
|
|
5739
|
+
out[key] = val;
|
|
5740
|
+
}
|
|
5741
|
+
} catch {
|
|
5742
|
+
}
|
|
5743
|
+
return out;
|
|
5744
|
+
}
|
|
5697
5745
|
|
|
5698
5746
|
// src/lib/opencode-slack-ingest.ts
|
|
5699
5747
|
import { createHash as createHash11 } from "crypto";
|
|
@@ -6804,6 +6852,677 @@ async function fireOpencodeScheduledTask(agent, task) {
|
|
|
6804
6852
|
}
|
|
6805
6853
|
}
|
|
6806
6854
|
|
|
6855
|
+
// src/lib/opencode-telegram-ingest.ts
|
|
6856
|
+
import { createHash as createHash13 } from "crypto";
|
|
6857
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync17, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync9 } from "fs";
|
|
6858
|
+
import { randomUUID } from "crypto";
|
|
6859
|
+
import { join as join21 } from "path";
|
|
6860
|
+
|
|
6861
|
+
// src/lib/telegram-ingest.ts
|
|
6862
|
+
import https2 from "https";
|
|
6863
|
+
|
|
6864
|
+
// ../../packages/core/dist/channels/governance/telegram-peer-classifier.js
|
|
6865
|
+
function classifyPeerMessage2(msg, cfg, self) {
|
|
6866
|
+
if (!msg.from?.is_bot)
|
|
6867
|
+
return { kind: "human" };
|
|
6868
|
+
if (self.bot_id !== null && msg.from.id === self.bot_id) {
|
|
6869
|
+
return { kind: "self" };
|
|
6870
|
+
}
|
|
6871
|
+
if (cfg.peer_disabled_mode === "all") {
|
|
6872
|
+
return { kind: "drop", reason: "peer_disabled_all" };
|
|
6873
|
+
}
|
|
6874
|
+
if (cfg.peer_agent_mode === "off") {
|
|
6875
|
+
return { kind: "drop", reason: "mode_off" };
|
|
6876
|
+
}
|
|
6877
|
+
const chatId = String(msg.chat.id);
|
|
6878
|
+
if (cfg.peer_group_ids.length === 0) {
|
|
6879
|
+
if (msg.chat.type !== "private") {
|
|
6880
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
6881
|
+
}
|
|
6882
|
+
} else if (!cfg.peer_group_ids.includes(chatId)) {
|
|
6883
|
+
return { kind: "drop", reason: "chat_not_allowed" };
|
|
6884
|
+
}
|
|
6885
|
+
const peer = cfg.peers.find((p) => p.bot_id === msg.from.id);
|
|
6886
|
+
if (!peer) {
|
|
6887
|
+
return { kind: "drop", reason: "unknown_peer" };
|
|
6888
|
+
}
|
|
6889
|
+
if (peer.gate_path === null) {
|
|
6890
|
+
return { kind: "drop", reason: "cross_team_grant_missing" };
|
|
6891
|
+
}
|
|
6892
|
+
if (cfg.peer_disabled_mode === "cross_team_only" && peer.gate_path !== void 0 && peer.gate_path !== "same_team") {
|
|
6893
|
+
return { kind: "drop", reason: "peer_disabled_cross_team" };
|
|
6894
|
+
}
|
|
6895
|
+
if (self.bot_id === null || self.bot_username === null) {
|
|
6896
|
+
return { kind: "drop", reason: "self_resolution_pending" };
|
|
6897
|
+
}
|
|
6898
|
+
const addressing = classifyAddressing2(msg, self.bot_id, self.bot_username);
|
|
6899
|
+
if (!addressing.addressed) {
|
|
6900
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
6901
|
+
}
|
|
6902
|
+
if (addressing.viaReplyOnly) {
|
|
6903
|
+
const inAllowedGroup = cfg.peer_group_ids.length > 0 && cfg.peer_group_ids.includes(chatId);
|
|
6904
|
+
const respondMode = cfg.peer_agent_mode === "respond";
|
|
6905
|
+
if (!(inAllowedGroup && respondMode)) {
|
|
6906
|
+
return { kind: "drop", reason: "not_addressed" };
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6909
|
+
return { kind: "peer-ingress", peer };
|
|
6910
|
+
}
|
|
6911
|
+
function classifyAddressing2(msg, ourBotId, ourBotUsername) {
|
|
6912
|
+
let viaMentionOrCommand = false;
|
|
6913
|
+
const text = msg.text ?? msg.caption ?? "";
|
|
6914
|
+
const entities = msg.entities ?? msg.caption_entities ?? [];
|
|
6915
|
+
const usAt = "@" + ourBotUsername;
|
|
6916
|
+
for (const entity of entities) {
|
|
6917
|
+
const slice = text.slice(entity.offset, entity.offset + entity.length).toLowerCase();
|
|
6918
|
+
if (entity.type === "bot_command") {
|
|
6919
|
+
const at = slice.indexOf("@");
|
|
6920
|
+
if (at < 0 || slice.slice(at + 1) === ourBotUsername) {
|
|
6921
|
+
viaMentionOrCommand = true;
|
|
6922
|
+
break;
|
|
6923
|
+
}
|
|
6924
|
+
} else if (entity.type === "mention" && slice === usAt) {
|
|
6925
|
+
viaMentionOrCommand = true;
|
|
6926
|
+
break;
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6929
|
+
const viaReply = msg.reply_to_message?.from?.id === ourBotId;
|
|
6930
|
+
if (viaMentionOrCommand)
|
|
6931
|
+
return { addressed: true, viaReplyOnly: false };
|
|
6932
|
+
if (viaReply)
|
|
6933
|
+
return { addressed: true, viaReplyOnly: true };
|
|
6934
|
+
return { addressed: false, viaReplyOnly: false };
|
|
6935
|
+
}
|
|
6936
|
+
var CODE_NAME_RE2 = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
6937
|
+
function parsePeersEnv2(raw, gateRaw) {
|
|
6938
|
+
if (!raw || raw.trim().length === 0)
|
|
6939
|
+
return [];
|
|
6940
|
+
let parsed;
|
|
6941
|
+
try {
|
|
6942
|
+
parsed = JSON.parse(raw);
|
|
6943
|
+
} catch {
|
|
6944
|
+
return [];
|
|
6945
|
+
}
|
|
6946
|
+
if (!Array.isArray(parsed))
|
|
6947
|
+
return [];
|
|
6948
|
+
const gateMap = /* @__PURE__ */ new Map();
|
|
6949
|
+
let gateConfigInvalid = false;
|
|
6950
|
+
const gatePresent = !!(gateRaw && gateRaw.trim().length > 0);
|
|
6951
|
+
if (gatePresent) {
|
|
6952
|
+
try {
|
|
6953
|
+
const gateParsed = JSON.parse(gateRaw);
|
|
6954
|
+
if (!gateParsed || typeof gateParsed !== "object" || Array.isArray(gateParsed)) {
|
|
6955
|
+
gateConfigInvalid = true;
|
|
6956
|
+
} else {
|
|
6957
|
+
for (const [k, v] of Object.entries(gateParsed)) {
|
|
6958
|
+
if (v === null) {
|
|
6959
|
+
gateMap.set(k, null);
|
|
6960
|
+
} else if (typeof v === "string") {
|
|
6961
|
+
const isGrantPath = v.startsWith("grant:") && v.slice("grant:".length).trim().length > 0;
|
|
6962
|
+
if (v === "same_team" || v === "intra_org_unrestricted" || isGrantPath) {
|
|
6963
|
+
gateMap.set(k, v);
|
|
6964
|
+
} else {
|
|
6965
|
+
gateConfigInvalid = true;
|
|
6966
|
+
break;
|
|
6967
|
+
}
|
|
6968
|
+
} else {
|
|
6969
|
+
gateConfigInvalid = true;
|
|
6970
|
+
break;
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
}
|
|
6974
|
+
} catch {
|
|
6975
|
+
gateConfigInvalid = true;
|
|
6976
|
+
}
|
|
6977
|
+
if (gateConfigInvalid) {
|
|
6978
|
+
console.error("[telegram-peer-classifier] TELEGRAM_PEERS_GATE is present but malformed; failing closed (every peer drops as cross_team_grant_missing)");
|
|
6979
|
+
}
|
|
6980
|
+
}
|
|
6981
|
+
const out = [];
|
|
6982
|
+
for (const entry of parsed) {
|
|
6983
|
+
if (entry && typeof entry === "object" && typeof entry.code_name === "string" && CODE_NAME_RE2.test(entry.code_name) && typeof entry.agent_id === "string" && typeof entry.bot_id === "number" && Number.isInteger(entry.bot_id) && entry.bot_id > 0) {
|
|
6984
|
+
const e = entry;
|
|
6985
|
+
const peer = { code_name: e.code_name, bot_id: e.bot_id, agent_id: e.agent_id };
|
|
6986
|
+
if (gateConfigInvalid) {
|
|
6987
|
+
peer.gate_path = null;
|
|
6988
|
+
} else {
|
|
6989
|
+
const key = String(e.bot_id);
|
|
6990
|
+
if (gateMap.has(key)) {
|
|
6991
|
+
peer.gate_path = gateMap.get(key) ?? null;
|
|
6992
|
+
} else if (gatePresent) {
|
|
6993
|
+
peer.gate_path = null;
|
|
6994
|
+
}
|
|
6995
|
+
}
|
|
6996
|
+
out.push(peer);
|
|
6997
|
+
}
|
|
6998
|
+
}
|
|
6999
|
+
return out;
|
|
7000
|
+
}
|
|
7001
|
+
function parsePeerGroupIdsEnv2(raw) {
|
|
7002
|
+
if (!raw)
|
|
7003
|
+
return [];
|
|
7004
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
7005
|
+
}
|
|
7006
|
+
function parsePeerAgentModeEnv2(raw) {
|
|
7007
|
+
if (raw === "listen" || raw === "respond")
|
|
7008
|
+
return raw;
|
|
7009
|
+
return "off";
|
|
7010
|
+
}
|
|
7011
|
+
|
|
7012
|
+
// ../../packages/core/dist/channels/governance/telegram-sender-gate.js
|
|
7013
|
+
function evaluateTelegramInboundGate(evt, deps) {
|
|
7014
|
+
const isBot = Boolean(evt.from?.is_bot);
|
|
7015
|
+
const text = (evt.text ?? evt.caption ?? "").trim();
|
|
7016
|
+
const chatId = String(evt.chat.id);
|
|
7017
|
+
const content = text.length > 0 ? { forward: true } : { forward: false, reason: "empty" };
|
|
7018
|
+
let peerLeaf;
|
|
7019
|
+
let isSelf = false;
|
|
7020
|
+
if (isBot) {
|
|
7021
|
+
const classifierMsg = {
|
|
7022
|
+
message_id: evt.message_id,
|
|
7023
|
+
text: evt.text,
|
|
7024
|
+
caption: evt.caption,
|
|
7025
|
+
from: evt.from ? { id: evt.from.id, username: evt.from.username, is_bot: evt.from.is_bot } : void 0,
|
|
7026
|
+
chat: { id: evt.chat.id, type: evt.chat.type },
|
|
7027
|
+
reply_to_message: evt.reply_to_message ? {
|
|
7028
|
+
message_id: evt.reply_to_message.message_id,
|
|
7029
|
+
from: evt.reply_to_message.from ? { id: evt.reply_to_message.from.id, is_bot: evt.reply_to_message.from.is_bot } : void 0
|
|
7030
|
+
} : void 0,
|
|
7031
|
+
entities: evt.entities,
|
|
7032
|
+
caption_entities: evt.caption_entities
|
|
7033
|
+
};
|
|
7034
|
+
const c = classifyPeerMessage2(classifierMsg, deps.peerConfig, deps.self);
|
|
7035
|
+
isSelf = c.kind === "self";
|
|
7036
|
+
peerLeaf = { kind: c.kind, reason: c.kind === "drop" ? c.reason : void 0 };
|
|
7037
|
+
}
|
|
7038
|
+
const decision = decideInboundAccess({
|
|
7039
|
+
content,
|
|
7040
|
+
// Telegram's only self signal is the peer classifier (Slack catches self at
|
|
7041
|
+
// evt.user===botUserId; Teams inside its activity filter).
|
|
7042
|
+
isSelf,
|
|
7043
|
+
isBot,
|
|
7044
|
+
peer: peerLeaf,
|
|
7045
|
+
// TELEGRAM_ALLOWED_CHATS is Telegram's org-boundary axis. An unlisted chat is
|
|
7046
|
+
// external ⇒ silent drop.
|
|
7047
|
+
orgBoundary: deps.allowedChats.size > 0 && !deps.allowedChats.has(chatId) ? { forward: false } : { forward: true },
|
|
7048
|
+
// No sender-policy mode system and no command dispatch on the injection gate.
|
|
7049
|
+
sameOrg: false
|
|
7050
|
+
});
|
|
7051
|
+
if (decision.kind === "drop")
|
|
7052
|
+
return { admit: false, reason: decision.reason };
|
|
7053
|
+
return { admit: true };
|
|
7054
|
+
}
|
|
7055
|
+
|
|
7056
|
+
// ../../packages/core/dist/channels/governance/telegram-inbound-config.js
|
|
7057
|
+
function parseTelegramAllowedChatsEnv(env) {
|
|
7058
|
+
return new Set((env["TELEGRAM_ALLOWED_CHATS"] ?? "").split(",").map((s) => s.trim()).filter(Boolean));
|
|
7059
|
+
}
|
|
7060
|
+
function parseTelegramPeerDisabledModeEnv(env, onWarn) {
|
|
7061
|
+
const next = (env["PEER_DISABLED"] ?? "").trim().toLowerCase();
|
|
7062
|
+
if (next === "off" || next === "cross_team_only" || next === "all")
|
|
7063
|
+
return next;
|
|
7064
|
+
if (next.length > 0) {
|
|
7065
|
+
onWarn?.(`invalid PEER_DISABLED=${JSON.stringify(env["PEER_DISABLED"])} - defaulting to 'all' (fail-closed)`);
|
|
7066
|
+
return "all";
|
|
7067
|
+
}
|
|
7068
|
+
const legacy = (env["TELEGRAM_PEER_DISABLED"] ?? "").trim().toLowerCase();
|
|
7069
|
+
return legacy === "1" || legacy === "true" || legacy === "yes" ? "all" : "off";
|
|
7070
|
+
}
|
|
7071
|
+
function buildTelegramPeerClassifierConfigFromEnv(env, opts) {
|
|
7072
|
+
return {
|
|
7073
|
+
peer_agent_mode: parsePeerAgentModeEnv2(env["TELEGRAM_PEER_AGENT_MODE"]),
|
|
7074
|
+
peer_group_ids: parsePeerGroupIdsEnv2(env["TELEGRAM_PEER_GROUP_IDS"]),
|
|
7075
|
+
peers: parsePeersEnv2(env["TELEGRAM_PEERS"], env["TELEGRAM_PEERS_GATE"]),
|
|
7076
|
+
peer_disabled_mode: parseTelegramPeerDisabledModeEnv(env, opts?.onWarn)
|
|
7077
|
+
};
|
|
7078
|
+
}
|
|
7079
|
+
|
|
7080
|
+
// src/lib/telegram-ingest.ts
|
|
7081
|
+
var nodeHttpsTelegramFetch = (url, init) => new Promise((resolve, reject) => {
|
|
7082
|
+
const u = new URL(url);
|
|
7083
|
+
const body = init?.body;
|
|
7084
|
+
const headers = { ...init?.headers ?? {} };
|
|
7085
|
+
if (body !== void 0 && headers["Content-Length"] === void 0) {
|
|
7086
|
+
headers["Content-Length"] = String(Buffer.byteLength(body));
|
|
7087
|
+
}
|
|
7088
|
+
const req = https2.request(
|
|
7089
|
+
{
|
|
7090
|
+
hostname: u.hostname,
|
|
7091
|
+
port: 443,
|
|
7092
|
+
path: u.pathname + u.search,
|
|
7093
|
+
method: init?.method ?? "GET",
|
|
7094
|
+
family: 4,
|
|
7095
|
+
// Backstop above the long-poll hold (getUpdates parks up to
|
|
7096
|
+
// LONG_POLL_SECONDS); short calls carry their own AbortSignal.timeout.
|
|
7097
|
+
timeout: POLL_HTTP_TIMEOUT_MS + 5e3,
|
|
7098
|
+
headers
|
|
7099
|
+
},
|
|
7100
|
+
(res) => {
|
|
7101
|
+
let data = "";
|
|
7102
|
+
res.on("data", (d) => {
|
|
7103
|
+
data += d;
|
|
7104
|
+
});
|
|
7105
|
+
res.on("end", () => {
|
|
7106
|
+
const status = res.statusCode ?? 0;
|
|
7107
|
+
resolve({
|
|
7108
|
+
ok: status >= 200 && status < 300,
|
|
7109
|
+
status,
|
|
7110
|
+
json: async () => JSON.parse(data.length > 0 ? data : "{}")
|
|
7111
|
+
});
|
|
7112
|
+
});
|
|
7113
|
+
}
|
|
7114
|
+
);
|
|
7115
|
+
req.on("error", reject);
|
|
7116
|
+
req.on("timeout", () => {
|
|
7117
|
+
req.destroy(new Error("Telegram API socket timeout"));
|
|
7118
|
+
});
|
|
7119
|
+
const signal = init?.signal;
|
|
7120
|
+
if (signal) {
|
|
7121
|
+
if (signal.aborted) req.destroy(new Error("aborted"));
|
|
7122
|
+
else signal.addEventListener("abort", () => req.destroy(new Error("aborted")), { once: true });
|
|
7123
|
+
}
|
|
7124
|
+
if (body !== void 0) req.write(body);
|
|
7125
|
+
req.end();
|
|
7126
|
+
});
|
|
7127
|
+
var TELEGRAM_API = "https://api.telegram.org";
|
|
7128
|
+
var LONG_POLL_SECONDS = 50;
|
|
7129
|
+
var POLL_HTTP_TIMEOUT_MS = (LONG_POLL_SECONDS + 10) * 1e3;
|
|
7130
|
+
var POLL_ERROR_BACKOFF_MS = 5e3;
|
|
7131
|
+
function memoryOffsetStore(initial = 0) {
|
|
7132
|
+
let value = initial;
|
|
7133
|
+
return { load: () => value, save: (o) => {
|
|
7134
|
+
value = o;
|
|
7135
|
+
} };
|
|
7136
|
+
}
|
|
7137
|
+
function resolveTelegramIngestParamsFromEnv(env) {
|
|
7138
|
+
const botToken = env["TELEGRAM_BOT_TOKEN"];
|
|
7139
|
+
if (!botToken) return null;
|
|
7140
|
+
const ackReaction = (env["TELEGRAM_ACK_REACTION"] ?? "").trim() || "\u{1F440}";
|
|
7141
|
+
const skipReaction = (env["TELEGRAM_SKIP_REACTION"] ?? "").trim() || void 0;
|
|
7142
|
+
return {
|
|
7143
|
+
botToken,
|
|
7144
|
+
allowedChats: parseTelegramAllowedChatsEnv(env),
|
|
7145
|
+
peerConfig: buildTelegramPeerClassifierConfigFromEnv(env),
|
|
7146
|
+
ackReaction,
|
|
7147
|
+
...skipReaction ? { skipReaction } : {}
|
|
7148
|
+
};
|
|
7149
|
+
}
|
|
7150
|
+
function isTelegramAuthFailure(status, description) {
|
|
7151
|
+
return status === 401 || (description ?? "").toLowerCase().includes("unauthorized");
|
|
7152
|
+
}
|
|
7153
|
+
async function resolveTelegramBotIdentity(botToken, fetchImpl) {
|
|
7154
|
+
const doFetch = fetchImpl ?? nodeHttpsTelegramFetch;
|
|
7155
|
+
const unresolved = { bot_id: null, bot_username: null };
|
|
7156
|
+
try {
|
|
7157
|
+
const res = await doFetch(`${TELEGRAM_API}/bot${botToken}/getMe`, {
|
|
7158
|
+
method: "POST",
|
|
7159
|
+
signal: AbortSignal.timeout(1e4)
|
|
7160
|
+
});
|
|
7161
|
+
const data = await res.json();
|
|
7162
|
+
if (data.ok && data.result) {
|
|
7163
|
+
return {
|
|
7164
|
+
self: {
|
|
7165
|
+
bot_id: data.result.id,
|
|
7166
|
+
// Lowercased to match resolveBotUsername()'s cache convention.
|
|
7167
|
+
bot_username: data.result.username ? data.result.username.toLowerCase() : null
|
|
7168
|
+
},
|
|
7169
|
+
authFailed: false
|
|
7170
|
+
};
|
|
7171
|
+
}
|
|
7172
|
+
return { self: unresolved, authFailed: isTelegramAuthFailure(res.status, data.description) };
|
|
7173
|
+
} catch {
|
|
7174
|
+
return { self: unresolved, authFailed: false };
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
var BoundedSet2 = class {
|
|
7178
|
+
max;
|
|
7179
|
+
set = /* @__PURE__ */ new Set();
|
|
7180
|
+
constructor(max = 500) {
|
|
7181
|
+
this.max = max;
|
|
7182
|
+
}
|
|
7183
|
+
has(k) {
|
|
7184
|
+
return this.set.has(k);
|
|
7185
|
+
}
|
|
7186
|
+
add(k) {
|
|
7187
|
+
this.set.add(k);
|
|
7188
|
+
if (this.set.size > this.max) {
|
|
7189
|
+
const oldest = this.set.values().next().value;
|
|
7190
|
+
if (oldest !== void 0) this.set.delete(oldest);
|
|
7191
|
+
}
|
|
7192
|
+
}
|
|
7193
|
+
};
|
|
7194
|
+
async function dispatchTelegramInbound(update, ctx) {
|
|
7195
|
+
const msg = update.message;
|
|
7196
|
+
const chatId = String(msg.chat.id);
|
|
7197
|
+
const dedupKey = String(update.update_id);
|
|
7198
|
+
if (ctx.seen.has(dedupKey)) return "dropped_duplicate";
|
|
7199
|
+
ctx.seen.add(dedupKey);
|
|
7200
|
+
const decision = evaluateTelegramInboundGate(msg, {
|
|
7201
|
+
allowedChats: ctx.allowedChats,
|
|
7202
|
+
peerConfig: ctx.peerConfig,
|
|
7203
|
+
self: ctx.self
|
|
7204
|
+
});
|
|
7205
|
+
if (!decision.admit) {
|
|
7206
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] dropped chat=${redactId(chatId)} reason=${decision.reason}`);
|
|
7207
|
+
return "dropped_gate";
|
|
7208
|
+
}
|
|
7209
|
+
if (ctx.ackReaction) {
|
|
7210
|
+
void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.ackReaction });
|
|
7211
|
+
}
|
|
7212
|
+
const conversationKey = msg.message_thread_id ? `${chatId}:${msg.message_thread_id}` : chatId;
|
|
7213
|
+
const meta = {
|
|
7214
|
+
source: "telegram",
|
|
7215
|
+
lane: "conversational",
|
|
7216
|
+
requires_reply: "true",
|
|
7217
|
+
chat_id: chatId
|
|
7218
|
+
};
|
|
7219
|
+
if (msg.message_thread_id) meta["message_thread_id"] = String(msg.message_thread_id);
|
|
7220
|
+
const result = await ctx.inject({
|
|
7221
|
+
channelId: "telegram",
|
|
7222
|
+
conversationKey,
|
|
7223
|
+
senderId: msg.from ? String(msg.from.id) : "unknown",
|
|
7224
|
+
text: msg.text ?? msg.caption ?? "",
|
|
7225
|
+
meta
|
|
7226
|
+
});
|
|
7227
|
+
if (result.status === "declined") {
|
|
7228
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] inject declined reason=${result.reason}`);
|
|
7229
|
+
return "declined";
|
|
7230
|
+
}
|
|
7231
|
+
if (result.status === "replied" && result.reply) {
|
|
7232
|
+
await ctx.sendReply({
|
|
7233
|
+
chatId,
|
|
7234
|
+
text: result.reply,
|
|
7235
|
+
...msg.message_thread_id ? { messageThreadId: msg.message_thread_id } : {}
|
|
7236
|
+
});
|
|
7237
|
+
ctx.log(`[telegram-ingest:${ctx.codeName}] outcome=replied chat=${redactId(chatId)} len=${result.reply.length}`);
|
|
7238
|
+
return "admitted_replied";
|
|
7239
|
+
}
|
|
7240
|
+
ctx.log(
|
|
7241
|
+
`[telegram-ingest:${ctx.codeName}] outcome=no_reply chat=${redactId(chatId)} inject_status=${result.status}`
|
|
7242
|
+
);
|
|
7243
|
+
if (ctx.skipReaction) {
|
|
7244
|
+
void ctx.addReaction({ chatId, messageId: msg.message_id, emoji: ctx.skipReaction });
|
|
7245
|
+
}
|
|
7246
|
+
return "admitted_no_reply";
|
|
7247
|
+
}
|
|
7248
|
+
function redactId(id) {
|
|
7249
|
+
if (!id) return "?";
|
|
7250
|
+
return `${id.slice(0, 2)}***`;
|
|
7251
|
+
}
|
|
7252
|
+
function defaultSendReply2(botToken, fetchImpl, log2) {
|
|
7253
|
+
return async ({ chatId, text, messageThreadId }) => {
|
|
7254
|
+
try {
|
|
7255
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/sendMessage`, {
|
|
7256
|
+
method: "POST",
|
|
7257
|
+
headers: { "Content-Type": "application/json" },
|
|
7258
|
+
body: JSON.stringify({
|
|
7259
|
+
chat_id: chatId,
|
|
7260
|
+
text,
|
|
7261
|
+
...messageThreadId ? { message_thread_id: messageThreadId } : {}
|
|
7262
|
+
}),
|
|
7263
|
+
// Dedicated short timeout so an outbound reply can't inherit the ~50s
|
|
7264
|
+
// long-poll getUpdates timeout and hang a turn on a slow Telegram edge.
|
|
7265
|
+
signal: AbortSignal.timeout(1e4)
|
|
7266
|
+
});
|
|
7267
|
+
const data = await res.json();
|
|
7268
|
+
if (!data.ok) log2(`[telegram-ingest] sendMessage failed: ${data.description}`);
|
|
7269
|
+
} catch (err) {
|
|
7270
|
+
log2(`[telegram-ingest] sendMessage threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7271
|
+
}
|
|
7272
|
+
};
|
|
7273
|
+
}
|
|
7274
|
+
function defaultAddReaction2(botToken, fetchImpl, log2) {
|
|
7275
|
+
return async ({ chatId, messageId, emoji }) => {
|
|
7276
|
+
try {
|
|
7277
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/setMessageReaction`, {
|
|
7278
|
+
method: "POST",
|
|
7279
|
+
headers: { "Content-Type": "application/json" },
|
|
7280
|
+
body: JSON.stringify({
|
|
7281
|
+
chat_id: chatId,
|
|
7282
|
+
message_id: messageId,
|
|
7283
|
+
reaction: [{ type: "emoji", emoji }]
|
|
7284
|
+
}),
|
|
7285
|
+
// Dedicated short timeout — the ack must not inherit the long-poll window.
|
|
7286
|
+
signal: AbortSignal.timeout(1e4)
|
|
7287
|
+
});
|
|
7288
|
+
const data = await res.json();
|
|
7289
|
+
if (!data.ok) log2(`[telegram-ingest] setMessageReaction failed: ${data.description}`);
|
|
7290
|
+
} catch (err) {
|
|
7291
|
+
log2(`[telegram-ingest] setMessageReaction threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7292
|
+
}
|
|
7293
|
+
};
|
|
7294
|
+
}
|
|
7295
|
+
function sleep(ms) {
|
|
7296
|
+
return new Promise((resolve) => {
|
|
7297
|
+
setTimeout(resolve, ms).unref?.();
|
|
7298
|
+
});
|
|
7299
|
+
}
|
|
7300
|
+
function startTelegramIngest(config2) {
|
|
7301
|
+
const fetchImpl = config2.fetchImpl ?? nodeHttpsTelegramFetch;
|
|
7302
|
+
const sendReply = config2.sendReply ?? defaultSendReply2(config2.botToken, fetchImpl, config2.log);
|
|
7303
|
+
const addReaction = config2.addReaction ?? defaultAddReaction2(config2.botToken, fetchImpl, config2.log);
|
|
7304
|
+
const offsetStore = config2.offsetStore ?? memoryOffsetStore();
|
|
7305
|
+
const seen = new BoundedSet2();
|
|
7306
|
+
const ctx = {
|
|
7307
|
+
codeName: config2.codeName,
|
|
7308
|
+
allowedChats: config2.allowedChats,
|
|
7309
|
+
peerConfig: config2.peerConfig,
|
|
7310
|
+
self: config2.self,
|
|
7311
|
+
inject: config2.inject,
|
|
7312
|
+
sendReply,
|
|
7313
|
+
addReaction,
|
|
7314
|
+
...config2.ackReaction ? { ackReaction: config2.ackReaction } : {},
|
|
7315
|
+
...config2.skipReaction ? { skipReaction: config2.skipReaction } : {},
|
|
7316
|
+
log: config2.log,
|
|
7317
|
+
seen
|
|
7318
|
+
};
|
|
7319
|
+
let stopped = false;
|
|
7320
|
+
let controller = null;
|
|
7321
|
+
let nextOffset = offsetStore.load();
|
|
7322
|
+
const pollLoop = async () => {
|
|
7323
|
+
while (!stopped) {
|
|
7324
|
+
try {
|
|
7325
|
+
controller = new AbortController();
|
|
7326
|
+
const timeout = AbortSignal.timeout(POLL_HTTP_TIMEOUT_MS);
|
|
7327
|
+
const signal = anySignal([controller.signal, timeout]);
|
|
7328
|
+
const res = await fetchImpl(`${TELEGRAM_API}/bot${config2.botToken}/getUpdates`, {
|
|
7329
|
+
method: "POST",
|
|
7330
|
+
headers: { "Content-Type": "application/json" },
|
|
7331
|
+
body: JSON.stringify({
|
|
7332
|
+
offset: nextOffset,
|
|
7333
|
+
timeout: LONG_POLL_SECONDS,
|
|
7334
|
+
allowed_updates: ["message"]
|
|
7335
|
+
}),
|
|
7336
|
+
signal
|
|
7337
|
+
});
|
|
7338
|
+
if (stopped) return;
|
|
7339
|
+
const data = await res.json();
|
|
7340
|
+
if (!data.ok) {
|
|
7341
|
+
config2.log(`[telegram-ingest:${config2.codeName}] getUpdates failed: ${data.description ?? "unknown"}`);
|
|
7342
|
+
await sleep(POLL_ERROR_BACKOFF_MS);
|
|
7343
|
+
continue;
|
|
7344
|
+
}
|
|
7345
|
+
const updates = data.result ?? [];
|
|
7346
|
+
for (const update of updates) {
|
|
7347
|
+
nextOffset = Math.max(nextOffset, update.update_id + 1);
|
|
7348
|
+
const msg = update.message;
|
|
7349
|
+
if (!msg) continue;
|
|
7350
|
+
try {
|
|
7351
|
+
await dispatchTelegramInbound({ update_id: update.update_id, message: msg }, ctx);
|
|
7352
|
+
} catch (err) {
|
|
7353
|
+
config2.log(
|
|
7354
|
+
`[telegram-ingest:${config2.codeName}] dispatch threw: ${err instanceof Error ? err.message : String(err)}`
|
|
7355
|
+
);
|
|
7356
|
+
}
|
|
7357
|
+
}
|
|
7358
|
+
if (updates.length > 0) offsetStore.save(nextOffset);
|
|
7359
|
+
} catch (err) {
|
|
7360
|
+
if (stopped) return;
|
|
7361
|
+
config2.log(`[telegram-ingest:${config2.codeName}] poll threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
7362
|
+
await sleep(POLL_ERROR_BACKOFF_MS);
|
|
7363
|
+
}
|
|
7364
|
+
}
|
|
7365
|
+
};
|
|
7366
|
+
void pollLoop();
|
|
7367
|
+
return {
|
|
7368
|
+
stop() {
|
|
7369
|
+
stopped = true;
|
|
7370
|
+
try {
|
|
7371
|
+
controller?.abort();
|
|
7372
|
+
} catch {
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
};
|
|
7376
|
+
}
|
|
7377
|
+
function anySignal(signals) {
|
|
7378
|
+
const controller = new AbortController();
|
|
7379
|
+
for (const s of signals) {
|
|
7380
|
+
if (s.aborted) {
|
|
7381
|
+
controller.abort();
|
|
7382
|
+
break;
|
|
7383
|
+
}
|
|
7384
|
+
s.addEventListener("abort", () => controller.abort(), { once: true });
|
|
7385
|
+
}
|
|
7386
|
+
return controller.signal;
|
|
7387
|
+
}
|
|
7388
|
+
|
|
7389
|
+
// src/lib/opencode-telegram-ingest.ts
|
|
7390
|
+
var ingests2 = /* @__PURE__ */ new Map();
|
|
7391
|
+
function fingerprint2(params) {
|
|
7392
|
+
return createHash13("sha256").update(
|
|
7393
|
+
JSON.stringify({
|
|
7394
|
+
botToken: params.botToken,
|
|
7395
|
+
allowedChats: [...params.allowedChats].sort(),
|
|
7396
|
+
peerConfig: params.peerConfig,
|
|
7397
|
+
ackReaction: params.ackReaction ?? null,
|
|
7398
|
+
skipReaction: params.skipReaction ?? null
|
|
7399
|
+
})
|
|
7400
|
+
).digest("hex");
|
|
7401
|
+
}
|
|
7402
|
+
function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
7403
|
+
let dir;
|
|
7404
|
+
let filePath;
|
|
7405
|
+
try {
|
|
7406
|
+
dir = getFramework("opencode").getAgentDir(codeName);
|
|
7407
|
+
filePath = join21(dir, "telegram-getupdates-offset-opencode.json");
|
|
7408
|
+
} catch {
|
|
7409
|
+
dir = null;
|
|
7410
|
+
filePath = null;
|
|
7411
|
+
}
|
|
7412
|
+
return {
|
|
7413
|
+
load() {
|
|
7414
|
+
if (!filePath) return 0;
|
|
7415
|
+
try {
|
|
7416
|
+
const parsed = JSON.parse(readFileSync17(filePath, "utf-8"));
|
|
7417
|
+
if (currentBotId != null && typeof parsed?.bot_id === "number" && parsed.bot_id !== currentBotId) {
|
|
7418
|
+
log2(`[telegram-ingest:${codeName}] offset cursor belongs to a different bot; ignoring (bot swap)`);
|
|
7419
|
+
return 0;
|
|
7420
|
+
}
|
|
7421
|
+
const offset = parsed?.offset;
|
|
7422
|
+
if (typeof offset === "number" && Number.isFinite(offset) && offset > 0) {
|
|
7423
|
+
const normalized = Math.floor(offset);
|
|
7424
|
+
if (Number.isSafeInteger(normalized) && normalized > 0) return normalized;
|
|
7425
|
+
}
|
|
7426
|
+
} catch {
|
|
7427
|
+
}
|
|
7428
|
+
return 0;
|
|
7429
|
+
},
|
|
7430
|
+
save(offset) {
|
|
7431
|
+
if (!filePath || !dir) return;
|
|
7432
|
+
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
7433
|
+
try {
|
|
7434
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
7435
|
+
writeFileSync9(
|
|
7436
|
+
tmpPath,
|
|
7437
|
+
JSON.stringify({
|
|
7438
|
+
offset,
|
|
7439
|
+
// Stamp the owning bot so a later token swap invalidates this cursor.
|
|
7440
|
+
...currentBotId != null ? { bot_id: currentBotId } : {},
|
|
7441
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
7442
|
+
}),
|
|
7443
|
+
{ mode: 384 }
|
|
7444
|
+
);
|
|
7445
|
+
renameSync2(tmpPath, filePath);
|
|
7446
|
+
} catch (err) {
|
|
7447
|
+
log2(`[telegram-ingest:${codeName}] offset persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7448
|
+
try {
|
|
7449
|
+
if (existsSync11(tmpPath)) unlinkSync(tmpPath);
|
|
7450
|
+
} catch {
|
|
7451
|
+
}
|
|
7452
|
+
}
|
|
7453
|
+
}
|
|
7454
|
+
};
|
|
7455
|
+
}
|
|
7456
|
+
async function ensureOpencodeTelegramIngest(opts) {
|
|
7457
|
+
const { codeName, agentId, log: log2 } = opts;
|
|
7458
|
+
const readEnv = opts.readEnv ?? readChannelServerEnv;
|
|
7459
|
+
let params;
|
|
7460
|
+
try {
|
|
7461
|
+
const env = readEnv(codeName, "telegram");
|
|
7462
|
+
if (!env) {
|
|
7463
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7464
|
+
return;
|
|
7465
|
+
}
|
|
7466
|
+
params = resolveTelegramIngestParamsFromEnv(env);
|
|
7467
|
+
} catch (err) {
|
|
7468
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7469
|
+
log2(`[telegram-ingest:${codeName}] not started: ${err instanceof Error ? err.message : String(err)}`);
|
|
7470
|
+
return;
|
|
7471
|
+
}
|
|
7472
|
+
if (!params) {
|
|
7473
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7474
|
+
return;
|
|
7475
|
+
}
|
|
7476
|
+
const key = fingerprint2(params);
|
|
7477
|
+
const existing = ingests2.get(codeName);
|
|
7478
|
+
if (existing?.key === key && !existing.selfUnresolved) return;
|
|
7479
|
+
const { self, authFailed } = await resolveTelegramBotIdentity(params.botToken, opts.fetchImpl);
|
|
7480
|
+
if (authFailed) {
|
|
7481
|
+
stopOpencodeTelegramIngest(codeName, log2);
|
|
7482
|
+
log2(`[telegram-ingest:${codeName}] not started: Telegram bot token rejected (needs reconnect)`);
|
|
7483
|
+
return;
|
|
7484
|
+
}
|
|
7485
|
+
if (existing?.key === key && self.bot_id === null) return;
|
|
7486
|
+
if (self.bot_id === null) {
|
|
7487
|
+
log2(`[telegram-ingest:${codeName}] bot identity unresolved; peer classification degraded`);
|
|
7488
|
+
}
|
|
7489
|
+
if (existing) {
|
|
7490
|
+
existing.handle.stop();
|
|
7491
|
+
ingests2.delete(codeName);
|
|
7492
|
+
log2(
|
|
7493
|
+
existing.key === key ? `[telegram-ingest:${codeName}] bot identity resolved; restarting ingest` : `[telegram-ingest:${codeName}] config changed; restarting ingest`
|
|
7494
|
+
);
|
|
7495
|
+
}
|
|
7496
|
+
const handle = startTelegramIngest({
|
|
7497
|
+
codeName,
|
|
7498
|
+
agentId,
|
|
7499
|
+
botToken: params.botToken,
|
|
7500
|
+
allowedChats: params.allowedChats,
|
|
7501
|
+
peerConfig: params.peerConfig,
|
|
7502
|
+
self,
|
|
7503
|
+
inject: (msg) => injectOpencodeMessage(codeName, msg, { awaitReply: true }),
|
|
7504
|
+
log: log2,
|
|
7505
|
+
offsetStore: opts.offsetStore ?? createFileOffsetStore(codeName, log2, self.bot_id),
|
|
7506
|
+
...params.ackReaction ? { ackReaction: params.ackReaction } : {},
|
|
7507
|
+
...params.skipReaction ? { skipReaction: params.skipReaction } : {},
|
|
7508
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
7509
|
+
});
|
|
7510
|
+
ingests2.set(codeName, { key, handle, selfUnresolved: self.bot_id === null });
|
|
7511
|
+
log2(
|
|
7512
|
+
`[telegram-ingest:${codeName}] ingest started (peer_mode=${params.peerConfig.peer_agent_mode}, allowlist=${params.allowedChats.size})`
|
|
7513
|
+
);
|
|
7514
|
+
}
|
|
7515
|
+
function stopOpencodeTelegramIngest(codeName, log2) {
|
|
7516
|
+
const entry = ingests2.get(codeName);
|
|
7517
|
+
if (!entry) return;
|
|
7518
|
+
entry.handle.stop();
|
|
7519
|
+
ingests2.delete(codeName);
|
|
7520
|
+
log2?.(`[telegram-ingest:${codeName}] ingest stopped`);
|
|
7521
|
+
}
|
|
7522
|
+
function stopAllOpencodeTelegramIngests(log2) {
|
|
7523
|
+
for (const codeName of [...ingests2.keys()]) stopOpencodeTelegramIngest(codeName, log2);
|
|
7524
|
+
}
|
|
7525
|
+
|
|
6807
7526
|
// src/lib/wedge-detection.ts
|
|
6808
7527
|
var DEFAULTS = {
|
|
6809
7528
|
inboundWaitSeconds: 120,
|
|
@@ -6944,24 +7663,24 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
6944
7663
|
}
|
|
6945
7664
|
|
|
6946
7665
|
// src/lib/restart-flags.ts
|
|
6947
|
-
import { existsSync as
|
|
6948
|
-
import { homedir as
|
|
6949
|
-
import { join as
|
|
6950
|
-
import { randomUUID } from "crypto";
|
|
7666
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readdirSync as readdirSync4, readFileSync as readFileSync18, renameSync as renameSync3, rmSync as rmSync4, writeFileSync as writeFileSync10 } from "fs";
|
|
7667
|
+
import { homedir as homedir11 } from "os";
|
|
7668
|
+
import { join as join22 } from "path";
|
|
7669
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
6951
7670
|
function restartFlagsDir() {
|
|
6952
|
-
return
|
|
7671
|
+
return join22(homedir11(), ".augmented", "restart-flags");
|
|
6953
7672
|
}
|
|
6954
7673
|
function flagPath(codeName) {
|
|
6955
|
-
return
|
|
7674
|
+
return join22(restartFlagsDir(), `${codeName}.flag`);
|
|
6956
7675
|
}
|
|
6957
7676
|
function readRestartFlags() {
|
|
6958
7677
|
const dir = restartFlagsDir();
|
|
6959
|
-
if (!
|
|
7678
|
+
if (!existsSync12(dir)) return [];
|
|
6960
7679
|
const out = [];
|
|
6961
7680
|
for (const entry of readdirSync4(dir)) {
|
|
6962
7681
|
if (!entry.endsWith(".flag")) continue;
|
|
6963
7682
|
try {
|
|
6964
|
-
const raw =
|
|
7683
|
+
const raw = readFileSync18(join22(dir, entry), "utf8");
|
|
6965
7684
|
const parsed = JSON.parse(raw);
|
|
6966
7685
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
6967
7686
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -6979,8 +7698,8 @@ function readRestartFlags() {
|
|
|
6979
7698
|
}
|
|
6980
7699
|
function deleteRestartFlag(codeName) {
|
|
6981
7700
|
const path = flagPath(codeName);
|
|
6982
|
-
if (
|
|
6983
|
-
|
|
7701
|
+
if (existsSync12(path)) {
|
|
7702
|
+
rmSync4(path, { force: true });
|
|
6984
7703
|
}
|
|
6985
7704
|
}
|
|
6986
7705
|
|
|
@@ -7325,10 +8044,10 @@ function startRealtimeConfig(config2) {
|
|
|
7325
8044
|
filter: filterStr
|
|
7326
8045
|
}, (payload) => {
|
|
7327
8046
|
const agent = payload.new;
|
|
7328
|
-
const
|
|
8047
|
+
const fingerprint3 = `${agent.status}|${agent.framework}|${agent.session_mode}|${agent.primary_model}`;
|
|
7329
8048
|
const prev = lastKnown.get(agent.agent_id);
|
|
7330
|
-
if (prev ===
|
|
7331
|
-
lastKnown.set(agent.agent_id,
|
|
8049
|
+
if (prev === fingerprint3) return;
|
|
8050
|
+
lastKnown.set(agent.agent_id, fingerprint3);
|
|
7332
8051
|
log2(`[realtime] Agent config changed: ${agent.code_name} (status=${agent.status})`);
|
|
7333
8052
|
onConfigChange(agent);
|
|
7334
8053
|
}).subscribe((status) => {
|
|
@@ -8213,15 +8932,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
|
8213
8932
|
var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
|
|
8214
8933
|
function projectMcpHash(_codeName, projectDir) {
|
|
8215
8934
|
try {
|
|
8216
|
-
const raw =
|
|
8217
|
-
return
|
|
8935
|
+
const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
|
|
8936
|
+
return createHash14("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
8218
8937
|
} catch {
|
|
8219
8938
|
return null;
|
|
8220
8939
|
}
|
|
8221
8940
|
}
|
|
8222
8941
|
function projectMcpKeys(_codeName, projectDir) {
|
|
8223
8942
|
try {
|
|
8224
|
-
const raw =
|
|
8943
|
+
const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
|
|
8225
8944
|
const parsed = JSON.parse(raw);
|
|
8226
8945
|
const servers = parsed.mcpServers;
|
|
8227
8946
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -8239,7 +8958,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
|
|
|
8239
8958
|
else runningMcpServerKeys.delete(codeName);
|
|
8240
8959
|
let launchStructure = null;
|
|
8241
8960
|
try {
|
|
8242
|
-
const raw =
|
|
8961
|
+
const raw = readFileSync19(join23(projectDir, ".mcp.json"), "utf-8");
|
|
8243
8962
|
launchStructure = managedMcpStructureHashFromFile(
|
|
8244
8963
|
JSON.parse(raw),
|
|
8245
8964
|
isManagedMcpServerKey
|
|
@@ -8339,7 +9058,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
|
|
|
8339
9058
|
if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
|
|
8340
9059
|
let mcpJsonForRebind = null;
|
|
8341
9060
|
try {
|
|
8342
|
-
mcpJsonForRebind = JSON.parse(
|
|
9061
|
+
mcpJsonForRebind = JSON.parse(readFileSync19(join23(projectDir, ".mcp.json"), "utf-8"));
|
|
8343
9062
|
} catch {
|
|
8344
9063
|
mcpJsonForRebind = null;
|
|
8345
9064
|
}
|
|
@@ -8468,7 +9187,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
8468
9187
|
function projectChannelSecretHash(projectDir) {
|
|
8469
9188
|
try {
|
|
8470
9189
|
const entries = parseEnvIntegrations(
|
|
8471
|
-
|
|
9190
|
+
readFileSync19(join23(projectDir, ".env.integrations"), "utf-8")
|
|
8472
9191
|
);
|
|
8473
9192
|
return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
|
|
8474
9193
|
} catch {
|
|
@@ -8531,6 +9250,7 @@ function clearAgentCaches(agentId, codeName) {
|
|
|
8531
9250
|
const channelCacheMutated = clearAgentState(agentId, codeName);
|
|
8532
9251
|
agentChannelTokens.delete(codeName);
|
|
8533
9252
|
stopOpencodeSlackIngest(codeName, log);
|
|
9253
|
+
stopOpencodeTelegramIngest(codeName, log);
|
|
8534
9254
|
agentFrameworkCache.delete(codeName);
|
|
8535
9255
|
kanbanBoardCache.delete(codeName);
|
|
8536
9256
|
clearKanbanNudgeState(codeName);
|
|
@@ -8560,7 +9280,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
8560
9280
|
var lastVersionCheckAt = 0;
|
|
8561
9281
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8562
9282
|
var lastResponsivenessProbeAt = 0;
|
|
8563
|
-
var agtCliVersion = true ? "0.28.
|
|
9283
|
+
var agtCliVersion = true ? "0.28.345" : "dev";
|
|
8564
9284
|
function resolveBrewPath(execFileSync2) {
|
|
8565
9285
|
try {
|
|
8566
9286
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -8573,7 +9293,7 @@ function resolveBrewPath(execFileSync2) {
|
|
|
8573
9293
|
"/usr/local/bin/brew"
|
|
8574
9294
|
];
|
|
8575
9295
|
for (const path of fallbacks) {
|
|
8576
|
-
if (
|
|
9296
|
+
if (existsSync13(path)) return path;
|
|
8577
9297
|
}
|
|
8578
9298
|
return null;
|
|
8579
9299
|
}
|
|
@@ -8583,7 +9303,7 @@ function claudeBinaryInstalled(execFileSync2) {
|
|
|
8583
9303
|
"/opt/homebrew/bin/claude",
|
|
8584
9304
|
"/usr/local/bin/claude"
|
|
8585
9305
|
];
|
|
8586
|
-
if (canonical.some((path) =>
|
|
9306
|
+
if (canonical.some((path) => existsSync13(path))) return true;
|
|
8587
9307
|
try {
|
|
8588
9308
|
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
8589
9309
|
return true;
|
|
@@ -8738,8 +9458,8 @@ function claudeManagedSettingsPath() {
|
|
|
8738
9458
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
8739
9459
|
try {
|
|
8740
9460
|
let settings = {};
|
|
8741
|
-
if (
|
|
8742
|
-
const raw =
|
|
9461
|
+
if (existsSync13(path)) {
|
|
9462
|
+
const raw = readFileSync19(path, "utf-8").trim();
|
|
8743
9463
|
if (raw) {
|
|
8744
9464
|
let parsed;
|
|
8745
9465
|
try {
|
|
@@ -8755,8 +9475,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
8755
9475
|
}
|
|
8756
9476
|
if (settings.channelsEnabled === true) return "ok";
|
|
8757
9477
|
settings.channelsEnabled = true;
|
|
8758
|
-
|
|
8759
|
-
|
|
9478
|
+
mkdirSync10(dirname6(path), { recursive: true });
|
|
9479
|
+
writeFileSync11(path, `${JSON.stringify(settings, null, 2)}
|
|
8760
9480
|
`);
|
|
8761
9481
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
8762
9482
|
return "ok";
|
|
@@ -8801,7 +9521,7 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
8801
9521
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
8802
9522
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
8803
9523
|
}
|
|
8804
|
-
if (
|
|
9524
|
+
if (existsSync13("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
8805
9525
|
log("Claude Code installed successfully");
|
|
8806
9526
|
} else {
|
|
8807
9527
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -8857,7 +9577,7 @@ ${r.stderr}`;
|
|
|
8857
9577
|
}
|
|
8858
9578
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
8859
9579
|
function selfUpdateAppliedMarkerPath() {
|
|
8860
|
-
return
|
|
9580
|
+
return join23(homedir12(), ".augmented", ".last-self-update-applied");
|
|
8861
9581
|
}
|
|
8862
9582
|
var selfUpdateUpToDateLogged = false;
|
|
8863
9583
|
var selfUpdatePinnedLogged = false;
|
|
@@ -8883,7 +9603,7 @@ async function checkAndUpdateCli(opts) {
|
|
|
8883
9603
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
8884
9604
|
if (!isBrewFormula && !isNpmGlobal) return "noop";
|
|
8885
9605
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
8886
|
-
const markerPath =
|
|
9606
|
+
const markerPath = join23(homedir12(), ".augmented", ".last-update-check");
|
|
8887
9607
|
if (!force) {
|
|
8888
9608
|
try {
|
|
8889
9609
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
@@ -9228,14 +9948,14 @@ async function checkClaudeAuth() {
|
|
|
9228
9948
|
}
|
|
9229
9949
|
var evalEmptyMcpConfigPath = null;
|
|
9230
9950
|
function ensureEvalEmptyMcpConfig() {
|
|
9231
|
-
if (evalEmptyMcpConfigPath &&
|
|
9232
|
-
const dir =
|
|
9951
|
+
if (evalEmptyMcpConfigPath && existsSync13(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
9952
|
+
const dir = join23(homedir12(), ".augmented");
|
|
9233
9953
|
try {
|
|
9234
|
-
|
|
9954
|
+
mkdirSync10(dir, { recursive: true });
|
|
9235
9955
|
} catch {
|
|
9236
9956
|
}
|
|
9237
|
-
const p =
|
|
9238
|
-
|
|
9957
|
+
const p = join23(dir, ".eval-empty-mcp.json");
|
|
9958
|
+
writeFileSync11(p, JSON.stringify({ mcpServers: {} }));
|
|
9239
9959
|
evalEmptyMcpConfigPath = p;
|
|
9240
9960
|
return p;
|
|
9241
9961
|
}
|
|
@@ -9260,7 +9980,7 @@ async function runEvalClaude(prompt, model) {
|
|
|
9260
9980
|
""
|
|
9261
9981
|
];
|
|
9262
9982
|
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
9263
|
-
cwd:
|
|
9983
|
+
cwd: homedir12(),
|
|
9264
9984
|
timeout: 12e4,
|
|
9265
9985
|
stdin: "ignore",
|
|
9266
9986
|
env: childEnv,
|
|
@@ -9326,10 +10046,10 @@ function resolveConversationEvalBackend() {
|
|
|
9326
10046
|
return conversationEvalBackend;
|
|
9327
10047
|
}
|
|
9328
10048
|
function getStateFile() {
|
|
9329
|
-
return
|
|
10049
|
+
return join23(config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
9330
10050
|
}
|
|
9331
10051
|
function channelHashCacheDir() {
|
|
9332
|
-
return config?.configDir ??
|
|
10052
|
+
return config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9333
10053
|
}
|
|
9334
10054
|
function loadChannelHashCache2() {
|
|
9335
10055
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -9381,7 +10101,7 @@ function removeDeliveryBaselineEntries(agentId) {
|
|
|
9381
10101
|
var _channelQuarantineStore = null;
|
|
9382
10102
|
function channelQuarantineStore() {
|
|
9383
10103
|
if (!_channelQuarantineStore) {
|
|
9384
|
-
const dir = config?.configDir ??
|
|
10104
|
+
const dir = config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9385
10105
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
9386
10106
|
}
|
|
9387
10107
|
return _channelQuarantineStore;
|
|
@@ -9389,7 +10109,7 @@ function channelQuarantineStore() {
|
|
|
9389
10109
|
var _hostFlagStore = null;
|
|
9390
10110
|
function hostFlagStore() {
|
|
9391
10111
|
if (!_hostFlagStore) {
|
|
9392
|
-
const dir = config?.configDir ??
|
|
10112
|
+
const dir = config?.configDir ?? join23(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
9393
10113
|
_hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
|
|
9394
10114
|
}
|
|
9395
10115
|
return _hostFlagStore;
|
|
@@ -9456,13 +10176,13 @@ function parseSkillFrontmatter(content) {
|
|
|
9456
10176
|
return out;
|
|
9457
10177
|
}
|
|
9458
10178
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
9459
|
-
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync:
|
|
9460
|
-
const skillsDir =
|
|
9461
|
-
const claudeMdPath =
|
|
10179
|
+
const { readdirSync: readdirSync6, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync12 } = await import("fs");
|
|
10180
|
+
const skillsDir = join23(configDir, codeName, "project", ".claude", "skills");
|
|
10181
|
+
const claudeMdPath = join23(configDir, codeName, "project", "CLAUDE.md");
|
|
9462
10182
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
9463
10183
|
const entries = [];
|
|
9464
10184
|
for (const dir of readdirSync6(skillsDir).sort()) {
|
|
9465
|
-
const skillFile =
|
|
10185
|
+
const skillFile = join23(skillsDir, dir, "SKILL.md");
|
|
9466
10186
|
if (!ex(skillFile)) continue;
|
|
9467
10187
|
try {
|
|
9468
10188
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -9504,7 +10224,7 @@ ${SKILLS_INDEX_END}`;
|
|
|
9504
10224
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
9505
10225
|
}
|
|
9506
10226
|
if (next !== current) {
|
|
9507
|
-
|
|
10227
|
+
writeFileSync12(claudeMdPath, next, "utf-8");
|
|
9508
10228
|
log2(`Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)`);
|
|
9509
10229
|
}
|
|
9510
10230
|
}
|
|
@@ -9525,7 +10245,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
9525
10245
|
if (codeNames.length === 0) return;
|
|
9526
10246
|
void (async () => {
|
|
9527
10247
|
try {
|
|
9528
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10248
|
+
const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
|
|
9529
10249
|
await api.post("/host/heartbeat", {
|
|
9530
10250
|
host_id: hostId,
|
|
9531
10251
|
agent_diagnostics: collectDiagnostics(codeNames)
|
|
@@ -9624,7 +10344,7 @@ async function pollCycle() {
|
|
|
9624
10344
|
}
|
|
9625
10345
|
try {
|
|
9626
10346
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
9627
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
10347
|
+
const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
|
|
9628
10348
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
9629
10349
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames) : void 0;
|
|
9630
10350
|
let tailscaleHostname;
|
|
@@ -9654,7 +10374,7 @@ async function pollCycle() {
|
|
|
9654
10374
|
claudeAuth = await detectClaudeAuth();
|
|
9655
10375
|
} catch (err) {
|
|
9656
10376
|
const errText = err instanceof Error ? err.message : String(err);
|
|
9657
|
-
const errId =
|
|
10377
|
+
const errId = createHash14("sha256").update(errText).digest("hex").slice(0, 12);
|
|
9658
10378
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
9659
10379
|
}
|
|
9660
10380
|
const hostHasClaudeCode = state6.agents.some(
|
|
@@ -9779,7 +10499,7 @@ async function pollCycle() {
|
|
|
9779
10499
|
const {
|
|
9780
10500
|
collectResponsivenessProbes,
|
|
9781
10501
|
getResponsivenessIntervalMs
|
|
9782
|
-
} = await import("../responsiveness-probe-
|
|
10502
|
+
} = await import("../responsiveness-probe-UYVXWW5D.js");
|
|
9783
10503
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
9784
10504
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
9785
10505
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -9811,7 +10531,7 @@ async function pollCycle() {
|
|
|
9811
10531
|
collectResponsivenessProbes,
|
|
9812
10532
|
livePendingInboundOldestAgeSeconds,
|
|
9813
10533
|
parkPendingInbound
|
|
9814
|
-
} = await import("../responsiveness-probe-
|
|
10534
|
+
} = await import("../responsiveness-probe-UYVXWW5D.js");
|
|
9815
10535
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-HYOOQV4A.js");
|
|
9816
10536
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
9817
10537
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -9900,13 +10620,13 @@ async function pollCycle() {
|
|
|
9900
10620
|
);
|
|
9901
10621
|
if (hostFlagStore().getBoolean("wedge-transient-notice")) {
|
|
9902
10622
|
try {
|
|
9903
|
-
const paneTail =
|
|
10623
|
+
const paneTail = readFileSync19(paneLogPath(codeName), "utf8").slice(-65536);
|
|
9904
10624
|
const transient = detectTransientApiErrorInLog(paneTail);
|
|
9905
10625
|
if (transient) {
|
|
9906
|
-
const wedgeHome =
|
|
9907
|
-
if (
|
|
10626
|
+
const wedgeHome = join23(homedir12(), ".augmented", codeName);
|
|
10627
|
+
if (existsSync13(wedgeHome)) {
|
|
9908
10628
|
atomicWriteFileSync(
|
|
9909
|
-
|
|
10629
|
+
join23(wedgeHome, "watchdog-give-up.json"),
|
|
9910
10630
|
JSON.stringify({
|
|
9911
10631
|
gave_up_at: wedgeNow.toISOString(),
|
|
9912
10632
|
reason: "transient_overload"
|
|
@@ -10155,7 +10875,7 @@ async function pollCycle() {
|
|
|
10155
10875
|
} catch {
|
|
10156
10876
|
}
|
|
10157
10877
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
10158
|
-
const agentDir =
|
|
10878
|
+
const agentDir = join23(adapter.getAgentDir(prev.codeName), "provision");
|
|
10159
10879
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
10160
10880
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
10161
10881
|
}
|
|
@@ -10242,10 +10962,10 @@ async function pollCycle() {
|
|
|
10242
10962
|
// pending-inbound marker. Best-effort: a write failure is logged by
|
|
10243
10963
|
// the watchdog, never fails the poll cycle.
|
|
10244
10964
|
signalGiveUp: (codeName) => {
|
|
10245
|
-
const dir =
|
|
10246
|
-
if (!
|
|
10965
|
+
const dir = join23(homedir12(), ".augmented", codeName);
|
|
10966
|
+
if (!existsSync13(dir)) return;
|
|
10247
10967
|
atomicWriteFileSync(
|
|
10248
|
-
|
|
10968
|
+
join23(dir, "watchdog-give-up.json"),
|
|
10249
10969
|
JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
10250
10970
|
);
|
|
10251
10971
|
}
|
|
@@ -10384,7 +11104,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10384
11104
|
}
|
|
10385
11105
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10386
11106
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
10387
|
-
let agentDir =
|
|
11107
|
+
let agentDir = join23(adapter.getAgentDir(agent.code_name), "provision");
|
|
10388
11108
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
10389
11109
|
if (previousKnownStatus !== agent.status) {
|
|
10390
11110
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -10430,7 +11150,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10430
11150
|
const residuals = {
|
|
10431
11151
|
gatewayRunning: false,
|
|
10432
11152
|
portAllocated: false,
|
|
10433
|
-
provisionDirExists:
|
|
11153
|
+
provisionDirExists: existsSync13(agentDir)
|
|
10434
11154
|
};
|
|
10435
11155
|
if (!hasRevokedResiduals(residuals)) {
|
|
10436
11156
|
agentStates.push({
|
|
@@ -10568,7 +11288,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10568
11288
|
const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
|
|
10569
11289
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
10570
11290
|
const frameworkAdapter = getFramework(frameworkId);
|
|
10571
|
-
agentDir =
|
|
11291
|
+
agentDir = join23(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
10572
11292
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
10573
11293
|
agentRestartTimezoneInputs.set(agent.code_name, {
|
|
10574
11294
|
agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
|
|
@@ -10613,9 +11333,9 @@ async function processAgent(agent, agentStates) {
|
|
|
10613
11333
|
try {
|
|
10614
11334
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter);
|
|
10615
11335
|
const changedFiles = [];
|
|
10616
|
-
|
|
11336
|
+
mkdirSync10(agentDir, { recursive: true });
|
|
10617
11337
|
for (const artifact of artifacts) {
|
|
10618
|
-
const filePath =
|
|
11338
|
+
const filePath = join23(agentDir, artifact.relativePath);
|
|
10619
11339
|
let existingHash;
|
|
10620
11340
|
let newHash;
|
|
10621
11341
|
let writeContent = artifact.content;
|
|
@@ -10634,8 +11354,8 @@ async function processAgent(agent, agentStates) {
|
|
|
10634
11354
|
};
|
|
10635
11355
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
10636
11356
|
try {
|
|
10637
|
-
const projectClaudeMd =
|
|
10638
|
-
const existing =
|
|
11357
|
+
const projectClaudeMd = join23(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
11358
|
+
const existing = readFileSync19(projectClaudeMd, "utf-8");
|
|
10639
11359
|
existingHash = sha256(stripDynamicSections(existing));
|
|
10640
11360
|
} catch {
|
|
10641
11361
|
existingHash = null;
|
|
@@ -10653,7 +11373,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10653
11373
|
const generatorKeys = Object.keys(generatorServers);
|
|
10654
11374
|
let existingRaw = "";
|
|
10655
11375
|
try {
|
|
10656
|
-
existingRaw =
|
|
11376
|
+
existingRaw = readFileSync19(filePath, "utf-8");
|
|
10657
11377
|
} catch {
|
|
10658
11378
|
}
|
|
10659
11379
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -10675,26 +11395,26 @@ async function processAgent(agent, agentStates) {
|
|
|
10675
11395
|
}
|
|
10676
11396
|
}
|
|
10677
11397
|
if (changedFiles.length > 0) {
|
|
10678
|
-
const isFirst = !
|
|
11398
|
+
const isFirst = !existsSync13(join23(agentDir, "CHARTER.md"));
|
|
10679
11399
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
10680
11400
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
10681
11401
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
10682
11402
|
for (const file of changedFiles) {
|
|
10683
|
-
const filePath =
|
|
10684
|
-
|
|
11403
|
+
const filePath = join23(agentDir, file.relativePath);
|
|
11404
|
+
mkdirSync10(dirname6(filePath), { recursive: true });
|
|
10685
11405
|
if (file.relativePath === ".mcp.json") {
|
|
10686
11406
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
10687
11407
|
} else {
|
|
10688
|
-
|
|
11408
|
+
writeFileSync11(filePath, file.content);
|
|
10689
11409
|
}
|
|
10690
11410
|
}
|
|
10691
11411
|
try {
|
|
10692
|
-
const provSkillsDir =
|
|
10693
|
-
if (
|
|
11412
|
+
const provSkillsDir = join23(agentDir, ".claude", "skills");
|
|
11413
|
+
if (existsSync13(provSkillsDir)) {
|
|
10694
11414
|
for (const folder of readdirSync5(provSkillsDir)) {
|
|
10695
11415
|
if (folder.startsWith("knowledge-")) {
|
|
10696
11416
|
try {
|
|
10697
|
-
|
|
11417
|
+
rmSync5(join23(provSkillsDir, folder), { recursive: true });
|
|
10698
11418
|
} catch {
|
|
10699
11419
|
}
|
|
10700
11420
|
}
|
|
@@ -10707,7 +11427,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10707
11427
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
10708
11428
|
const hashes = /* @__PURE__ */ new Map();
|
|
10709
11429
|
for (const file of trackedFiles2) {
|
|
10710
|
-
const h = hashFile(
|
|
11430
|
+
const h = hashFile(join23(agentDir, file));
|
|
10711
11431
|
if (h) hashes.set(file, h);
|
|
10712
11432
|
}
|
|
10713
11433
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -10725,14 +11445,14 @@ async function processAgent(agent, agentStates) {
|
|
|
10725
11445
|
}
|
|
10726
11446
|
if (Array.isArray(refreshData.workflows)) {
|
|
10727
11447
|
try {
|
|
10728
|
-
const provWorkflowsDir =
|
|
10729
|
-
if (
|
|
11448
|
+
const provWorkflowsDir = join23(agentDir, ".claude", "workflows");
|
|
11449
|
+
if (existsSync13(provWorkflowsDir)) {
|
|
10730
11450
|
const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
|
|
10731
11451
|
for (const file of readdirSync5(provWorkflowsDir)) {
|
|
10732
11452
|
if (!file.endsWith(".js")) continue;
|
|
10733
11453
|
if (expected.has(file)) continue;
|
|
10734
11454
|
try {
|
|
10735
|
-
|
|
11455
|
+
rmSync5(join23(provWorkflowsDir, file));
|
|
10736
11456
|
} catch {
|
|
10737
11457
|
}
|
|
10738
11458
|
}
|
|
@@ -10789,10 +11509,10 @@ async function processAgent(agent, agentStates) {
|
|
|
10789
11509
|
}
|
|
10790
11510
|
let lastDriftCheckAt = now;
|
|
10791
11511
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
10792
|
-
if (written &&
|
|
11512
|
+
if (written && existsSync13(agentDir)) {
|
|
10793
11513
|
const driftedFiles = [];
|
|
10794
11514
|
for (const [file, expectedHash] of written) {
|
|
10795
|
-
const localHash = hashFile(
|
|
11515
|
+
const localHash = hashFile(join23(agentDir, file));
|
|
10796
11516
|
if (localHash && localHash !== expectedHash) {
|
|
10797
11517
|
driftedFiles.push(file);
|
|
10798
11518
|
}
|
|
@@ -10803,7 +11523,7 @@ async function processAgent(agent, agentStates) {
|
|
|
10803
11523
|
try {
|
|
10804
11524
|
const localHashes = {};
|
|
10805
11525
|
for (const file of driftedFiles) {
|
|
10806
|
-
localHashes[file] = hashFile(
|
|
11526
|
+
localHashes[file] = hashFile(join23(agentDir, file));
|
|
10807
11527
|
}
|
|
10808
11528
|
await api.post("/host/drift", {
|
|
10809
11529
|
agent_id: agent.agent_id,
|
|
@@ -10990,15 +11710,15 @@ async function processAgent(agent, agentStates) {
|
|
|
10990
11710
|
const addedChannels = [...restartDecision.added];
|
|
10991
11711
|
const writeDmNoticeMarkers = isChannelAddRestart ? () => {
|
|
10992
11712
|
try {
|
|
10993
|
-
const agentAugmentedDir =
|
|
10994
|
-
|
|
11713
|
+
const agentAugmentedDir = join23(homedir12(), ".augmented", agent.code_name);
|
|
11714
|
+
mkdirSync10(agentAugmentedDir, { recursive: true });
|
|
10995
11715
|
const markerJson = JSON.stringify({
|
|
10996
11716
|
version: 1,
|
|
10997
11717
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10998
11718
|
added: addedChannels
|
|
10999
11719
|
});
|
|
11000
11720
|
for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
|
|
11001
|
-
atomicWriteFileSync(
|
|
11721
|
+
atomicWriteFileSync(join23(agentAugmentedDir, file), markerJson);
|
|
11002
11722
|
}
|
|
11003
11723
|
} catch (err) {
|
|
11004
11724
|
log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
|
|
@@ -11074,7 +11794,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11074
11794
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
11075
11795
|
msteamsEntry?.config
|
|
11076
11796
|
);
|
|
11077
|
-
const behaviourHash =
|
|
11797
|
+
const behaviourHash = createHash14("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
11078
11798
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
11079
11799
|
const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
|
|
11080
11800
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11130,7 +11850,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11130
11850
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
11131
11851
|
slackEntry?.config
|
|
11132
11852
|
);
|
|
11133
|
-
const slackBehaviourHash =
|
|
11853
|
+
const slackBehaviourHash = createHash14("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
11134
11854
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
11135
11855
|
const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
|
|
11136
11856
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
@@ -11187,24 +11907,24 @@ async function processAgent(agent, agentStates) {
|
|
|
11187
11907
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
|
|
11188
11908
|
try {
|
|
11189
11909
|
const agentProvisionDir = agentDir;
|
|
11190
|
-
const projectDir =
|
|
11191
|
-
|
|
11192
|
-
|
|
11193
|
-
const provisionMcpPath =
|
|
11194
|
-
const projectMcpPath =
|
|
11910
|
+
const projectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
|
|
11911
|
+
mkdirSync10(agentProvisionDir, { recursive: true });
|
|
11912
|
+
mkdirSync10(projectDir, { recursive: true });
|
|
11913
|
+
const provisionMcpPath = join23(agentProvisionDir, ".mcp.json");
|
|
11914
|
+
const projectMcpPath = join23(projectDir, ".mcp.json");
|
|
11195
11915
|
let mcpConfig = { mcpServers: {} };
|
|
11196
11916
|
try {
|
|
11197
|
-
mcpConfig = JSON.parse(
|
|
11917
|
+
mcpConfig = JSON.parse(readFileSync19(provisionMcpPath, "utf-8"));
|
|
11198
11918
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
11199
11919
|
} catch {
|
|
11200
11920
|
}
|
|
11201
|
-
const localDirectChatChannel =
|
|
11921
|
+
const localDirectChatChannel = join23(homedir12(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
11202
11922
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
11203
11923
|
const directChatTz = (() => {
|
|
11204
11924
|
const tz = directChatTeamSettings?.["timezone"];
|
|
11205
11925
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
11206
11926
|
})();
|
|
11207
|
-
if (
|
|
11927
|
+
if (existsSync13(localDirectChatChannel)) {
|
|
11208
11928
|
const directChatEnv = {
|
|
11209
11929
|
AGT_HOST: requireHost(),
|
|
11210
11930
|
// ENG-5901 Track D: templated — the manager exports the real
|
|
@@ -11224,7 +11944,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11224
11944
|
// ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
|
|
11225
11945
|
// returns the agent root (NOT the /provision subdir `agentDir` points at),
|
|
11226
11946
|
// so it byte-matches the broker readers' path.
|
|
11227
|
-
AGT_TURN_INITIATOR_FILE:
|
|
11947
|
+
AGT_TURN_INITIATOR_FILE: join23(
|
|
11228
11948
|
frameworkAdapter.getAgentDir(agent.code_name),
|
|
11229
11949
|
".current-turn-initiator.json"
|
|
11230
11950
|
)
|
|
@@ -11244,10 +11964,10 @@ async function processAgent(agent, agentStates) {
|
|
|
11244
11964
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
11245
11965
|
}
|
|
11246
11966
|
}
|
|
11247
|
-
const staleChannelsPath =
|
|
11248
|
-
if (
|
|
11967
|
+
const staleChannelsPath = join23(projectDir, ".mcp-channels.json");
|
|
11968
|
+
if (existsSync13(staleChannelsPath)) {
|
|
11249
11969
|
try {
|
|
11250
|
-
|
|
11970
|
+
rmSync5(staleChannelsPath, { force: true });
|
|
11251
11971
|
} catch {
|
|
11252
11972
|
}
|
|
11253
11973
|
}
|
|
@@ -11334,7 +12054,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11334
12054
|
}
|
|
11335
12055
|
if (hostFlagStore().getBoolean("connectivity-probe")) {
|
|
11336
12056
|
try {
|
|
11337
|
-
const probeProjectDir =
|
|
12057
|
+
const probeProjectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
|
|
11338
12058
|
await runAgentConnectivityProbes(agent, integrations, probeProjectDir);
|
|
11339
12059
|
} catch (err) {
|
|
11340
12060
|
log(`Connectivity probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -11345,7 +12065,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11345
12065
|
const forceDue = attemptsLeft > 0;
|
|
11346
12066
|
let probeRan = false;
|
|
11347
12067
|
try {
|
|
11348
|
-
const probeProjectDir =
|
|
12068
|
+
const probeProjectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
|
|
11349
12069
|
probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
|
|
11350
12070
|
} catch (err) {
|
|
11351
12071
|
log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -11358,9 +12078,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11358
12078
|
}
|
|
11359
12079
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
11360
12080
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
11361
|
-
if (mcpPath &&
|
|
12081
|
+
if (mcpPath && existsSync13(mcpPath)) {
|
|
11362
12082
|
try {
|
|
11363
|
-
const cfg = JSON.parse(
|
|
12083
|
+
const cfg = JSON.parse(readFileSync19(mcpPath, "utf-8"));
|
|
11364
12084
|
const expectedRemoteKeys = new Set(
|
|
11365
12085
|
integrations.map((i) => i.definition_id)
|
|
11366
12086
|
);
|
|
@@ -11385,11 +12105,11 @@ async function processAgent(agent, agentStates) {
|
|
|
11385
12105
|
recordConfigChurnEvent(agent.agent_id, agent.code_name, FLAP_CHANNEL_INTEGRATIONS, intMembership);
|
|
11386
12106
|
}
|
|
11387
12107
|
if (intHash !== prevIntHash) {
|
|
11388
|
-
const projectDir =
|
|
11389
|
-
const envIntPath =
|
|
12108
|
+
const projectDir = join23(homedir12(), ".augmented", agent.code_name, "project");
|
|
12109
|
+
const envIntPath = join23(projectDir, ".env.integrations");
|
|
11390
12110
|
let preWriteEnv;
|
|
11391
12111
|
try {
|
|
11392
|
-
preWriteEnv =
|
|
12112
|
+
preWriteEnv = readFileSync19(envIntPath, "utf-8");
|
|
11393
12113
|
} catch {
|
|
11394
12114
|
preWriteEnv = void 0;
|
|
11395
12115
|
}
|
|
@@ -11401,9 +12121,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11401
12121
|
let rotationHandled = true;
|
|
11402
12122
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
11403
12123
|
try {
|
|
11404
|
-
const projectMcpPath =
|
|
11405
|
-
const postWriteEnv =
|
|
11406
|
-
const mcpContent =
|
|
12124
|
+
const projectMcpPath = join23(projectDir, ".mcp.json");
|
|
12125
|
+
const postWriteEnv = readFileSync19(envIntPath, "utf-8");
|
|
12126
|
+
const mcpContent = readFileSync19(projectMcpPath, "utf-8");
|
|
11407
12127
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
11408
12128
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
11409
12129
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -11480,10 +12200,10 @@ async function processAgent(agent, agentStates) {
|
|
|
11480
12200
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
11481
12201
|
}
|
|
11482
12202
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
11483
|
-
const headersHash =
|
|
12203
|
+
const headersHash = createHash14("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
11484
12204
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
11485
12205
|
}).join("\n");
|
|
11486
|
-
const mcpHash =
|
|
12206
|
+
const mcpHash = createHash14("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
11487
12207
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
11488
12208
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
11489
12209
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -11498,15 +12218,15 @@ async function processAgent(agent, agentStates) {
|
|
|
11498
12218
|
if (mcpHash !== prevMcpHash) {
|
|
11499
12219
|
for (const e of desiredEntries) {
|
|
11500
12220
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
11501
|
-
const urlHash =
|
|
12221
|
+
const urlHash = createHash14("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
11502
12222
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
11503
12223
|
}
|
|
11504
12224
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.getMcpPath) {
|
|
11505
12225
|
const mcpPath = frameworkAdapter.getMcpPath(agent.code_name);
|
|
11506
12226
|
if (mcpPath) {
|
|
11507
12227
|
try {
|
|
11508
|
-
const { readFileSync:
|
|
11509
|
-
const mcpConfig = JSON.parse(
|
|
12228
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
12229
|
+
const mcpConfig = JSON.parse(readFileSync20(mcpPath, "utf-8"));
|
|
11510
12230
|
if (mcpConfig.mcpServers) {
|
|
11511
12231
|
for (const key of Object.keys(mcpConfig.mcpServers)) {
|
|
11512
12232
|
if (isManagedMcpServerKey(key) && !expectedServerIds.has(key)) {
|
|
@@ -11607,7 +12327,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11607
12327
|
if (frameworkAdapter.installSkillFiles) {
|
|
11608
12328
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
11609
12329
|
const installedIntegrationSkills = [];
|
|
11610
|
-
const { createHash:
|
|
12330
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
11611
12331
|
const refreshAny = refreshData;
|
|
11612
12332
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
11613
12333
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -11636,7 +12356,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11636
12356
|
)
|
|
11637
12357
|
}));
|
|
11638
12358
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
11639
|
-
const contentHash =
|
|
12359
|
+
const contentHash = createHash15("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
11640
12360
|
const hashKey = `plugin-skill:${agent.agent_id}:${integrationSkillId}`;
|
|
11641
12361
|
if (agentState.knownSkillHashes.get(hashKey) === contentHash) continue;
|
|
11642
12362
|
frameworkAdapter.installSkillFiles(agent.code_name, integrationSkillId, bundle.files);
|
|
@@ -11648,19 +12368,19 @@ async function processAgent(agent, agentStates) {
|
|
|
11648
12368
|
}
|
|
11649
12369
|
}
|
|
11650
12370
|
try {
|
|
11651
|
-
const { readdirSync: readdirSync6, rmSync:
|
|
11652
|
-
const { homedir:
|
|
12371
|
+
const { readdirSync: readdirSync6, rmSync: rmSync6 } = await import("fs");
|
|
12372
|
+
const { homedir: homedir13 } = await import("os");
|
|
11653
12373
|
const frameworkId2 = frameworkAdapter.id;
|
|
11654
12374
|
const candidateSkillDirs = [
|
|
11655
12375
|
// Claude Code — framework runtime tree
|
|
11656
|
-
|
|
12376
|
+
join23(homedir13(), ".augmented", agent.code_name, "skills"),
|
|
11657
12377
|
// Claude Code — project tree
|
|
11658
|
-
|
|
12378
|
+
join23(homedir13(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
11659
12379
|
// Defensive: legacy provision-side path, not currently an
|
|
11660
12380
|
// install target but cheap to sweep.
|
|
11661
|
-
|
|
12381
|
+
join23(agentDir, ".claude", "skills")
|
|
11662
12382
|
];
|
|
11663
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
12383
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync13(d));
|
|
11664
12384
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
11665
12385
|
for (const dir of existingDirs) {
|
|
11666
12386
|
try {
|
|
@@ -11674,9 +12394,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11674
12394
|
}
|
|
11675
12395
|
const removeSkillFolder = (entry, reason) => {
|
|
11676
12396
|
for (const dir of existingDirs) {
|
|
11677
|
-
const p =
|
|
11678
|
-
if (
|
|
11679
|
-
|
|
12397
|
+
const p = join23(dir, entry);
|
|
12398
|
+
if (existsSync13(p)) {
|
|
12399
|
+
rmSync6(p, { recursive: true, force: true });
|
|
11680
12400
|
}
|
|
11681
12401
|
}
|
|
11682
12402
|
log(`Removed ${reason} '${entry}' for '${agent.code_name}' (framework=${frameworkId2})`);
|
|
@@ -11694,7 +12414,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11694
12414
|
const sharedSkillsPayload = refreshAny.shared_skills;
|
|
11695
12415
|
const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
|
|
11696
12416
|
const manifestPath = managedSkillManifestPath(
|
|
11697
|
-
|
|
12417
|
+
join23(homedir12(), ".augmented", agent.code_name)
|
|
11698
12418
|
);
|
|
11699
12419
|
const prevIds = /* @__PURE__ */ new Set([
|
|
11700
12420
|
...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
@@ -11703,7 +12423,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11703
12423
|
const plan = planGlobalSkillSync(
|
|
11704
12424
|
[...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
|
|
11705
12425
|
prevIds,
|
|
11706
|
-
(content) =>
|
|
12426
|
+
(content) => createHash15("sha256").update(content).digest("hex").slice(0, 12),
|
|
11707
12427
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
|
|
11708
12428
|
{ desiredResolved }
|
|
11709
12429
|
);
|
|
@@ -11714,16 +12434,16 @@ async function processAgent(agent, agentStates) {
|
|
|
11714
12434
|
}
|
|
11715
12435
|
if (plan.removes.length) {
|
|
11716
12436
|
const globalSkillDirs = [
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
12437
|
+
join23(homedir12(), ".augmented", agent.code_name, "skills"),
|
|
12438
|
+
join23(homedir12(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
12439
|
+
join23(agentDir, ".claude", "skills")
|
|
11720
12440
|
];
|
|
11721
12441
|
for (const id of plan.removes) {
|
|
11722
12442
|
let prunedAny = false;
|
|
11723
12443
|
for (const dir of globalSkillDirs) {
|
|
11724
|
-
const p =
|
|
11725
|
-
if (
|
|
11726
|
-
|
|
12444
|
+
const p = join23(dir, id);
|
|
12445
|
+
if (existsSync13(p) && existsSync13(join23(p, "SKILL.md"))) {
|
|
12446
|
+
rmSync5(p, { recursive: true, force: true });
|
|
11727
12447
|
prunedAny = true;
|
|
11728
12448
|
}
|
|
11729
12449
|
}
|
|
@@ -11754,7 +12474,7 @@ async function processAgent(agent, agentStates) {
|
|
|
11754
12474
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
11755
12475
|
if (!slug) continue;
|
|
11756
12476
|
try {
|
|
11757
|
-
const scriptHash =
|
|
12477
|
+
const scriptHash = createHash15("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
11758
12478
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
11759
12479
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
11760
12480
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -11769,9 +12489,9 @@ async function processAgent(agent, agentStates) {
|
|
|
11769
12489
|
} else if (result.timedOut) {
|
|
11770
12490
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
11771
12491
|
} else {
|
|
11772
|
-
const stderrHash =
|
|
12492
|
+
const stderrHash = createHash15("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
11773
12493
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
11774
|
-
const missingCmdHash = missingCmd ?
|
|
12494
|
+
const missingCmdHash = missingCmd ? createHash15("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
11775
12495
|
log(
|
|
11776
12496
|
`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}]`
|
|
11777
12497
|
);
|
|
@@ -11928,8 +12648,8 @@ async function processAgent(agent, agentStates) {
|
|
|
11928
12648
|
const sess = getSessionState(agent.code_name);
|
|
11929
12649
|
let mcpJsonParsed = null;
|
|
11930
12650
|
try {
|
|
11931
|
-
const mcpPath =
|
|
11932
|
-
mcpJsonParsed = JSON.parse(
|
|
12651
|
+
const mcpPath = join23(getProjectDir(agent.code_name), ".mcp.json");
|
|
12652
|
+
mcpJsonParsed = JSON.parse(readFileSync19(mcpPath, "utf-8"));
|
|
11933
12653
|
} catch {
|
|
11934
12654
|
}
|
|
11935
12655
|
reapMissingMcpSessions({
|
|
@@ -12258,10 +12978,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
12258
12978
|
}
|
|
12259
12979
|
}
|
|
12260
12980
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
12261
|
-
if (trackedFiles.length > 0 &&
|
|
12981
|
+
if (trackedFiles.length > 0 && existsSync13(agentDir)) {
|
|
12262
12982
|
const hashes = /* @__PURE__ */ new Map();
|
|
12263
12983
|
for (const file of trackedFiles) {
|
|
12264
|
-
const h = hashFile(
|
|
12984
|
+
const h = hashFile(join23(agentDir, file));
|
|
12265
12985
|
if (h) hashes.set(file, h);
|
|
12266
12986
|
}
|
|
12267
12987
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -12276,7 +12996,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
12276
12996
|
refreshData.agent.onboarding_state
|
|
12277
12997
|
);
|
|
12278
12998
|
const obStep = obState.step;
|
|
12279
|
-
const markerPath =
|
|
12999
|
+
const markerPath = join23(homedir12(), ".augmented", agent.code_name, "onboarding-drive.json");
|
|
12280
13000
|
const marker = readOnboardingDriveMarker(markerPath);
|
|
12281
13001
|
const decision = decideOnboardingDrive(obStep, marker, Date.now(), obState.generation ?? 0);
|
|
12282
13002
|
if (decision.clearMarker) {
|
|
@@ -12341,10 +13061,12 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
12341
13061
|
const codeName = agent.code_name;
|
|
12342
13062
|
if (isOpencodeSessionHealthy(codeName)) {
|
|
12343
13063
|
await ensureOpencodeSlackIngest({ codeName, agentId: agent.agent_id, log });
|
|
13064
|
+
await ensureOpencodeTelegramIngest({ codeName, agentId: agent.agent_id, log });
|
|
12344
13065
|
return { decision: "healthy", spawnAttempted: false, sessionHealthyAfter: true };
|
|
12345
13066
|
}
|
|
12346
13067
|
stopOpencodeSlackIngest(codeName, log);
|
|
12347
|
-
|
|
13068
|
+
stopOpencodeTelegramIngest(codeName, log);
|
|
13069
|
+
const opencodeProjectDir = join23(getFramework("opencode").getAgentDir(codeName), "provision");
|
|
12348
13070
|
const openRouterRaw = refreshData.openrouter ?? null;
|
|
12349
13071
|
const serveEnv = {
|
|
12350
13072
|
AGT_HOST: requireHost(),
|
|
@@ -12384,8 +13106,8 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
12384
13106
|
async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
12385
13107
|
const codeName = agent.code_name;
|
|
12386
13108
|
const projectDir = getProjectDir(codeName);
|
|
12387
|
-
const mcpConfigPath =
|
|
12388
|
-
const claudeMdPath =
|
|
13109
|
+
const mcpConfigPath = join23(projectDir, ".mcp.json");
|
|
13110
|
+
const claudeMdPath = join23(projectDir, "CLAUDE.md");
|
|
12389
13111
|
if (restartBreaker.isTripped(codeName)) {
|
|
12390
13112
|
const trip = restartBreaker.getTrip(codeName);
|
|
12391
13113
|
return {
|
|
@@ -12543,7 +13265,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
12543
13265
|
const ctx = getLastFailureContext(codeName);
|
|
12544
13266
|
const recovery = prepareForRespawn(codeName);
|
|
12545
13267
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
12546
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
13268
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash14("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
12547
13269
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
12548
13270
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
12549
13271
|
log(
|
|
@@ -12557,7 +13279,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
12557
13279
|
);
|
|
12558
13280
|
getHostId().then((hostId) => {
|
|
12559
13281
|
if (!hostId) return;
|
|
12560
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
13282
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash14("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
12561
13283
|
return api.post("/host/events", {
|
|
12562
13284
|
host_id: hostId,
|
|
12563
13285
|
agent_code_name: codeName,
|
|
@@ -12706,7 +13428,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash13("sha256")
|
|
|
12706
13428
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
12707
13429
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
12708
13430
|
}
|
|
12709
|
-
const stableTasksHash =
|
|
13431
|
+
const stableTasksHash = createHash14("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
12710
13432
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
12711
13433
|
if (stableTasksHash !== prevHash) {
|
|
12712
13434
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -12923,7 +13645,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
12923
13645
|
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}`));
|
|
12924
13646
|
void (async () => {
|
|
12925
13647
|
try {
|
|
12926
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13648
|
+
const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
|
|
12927
13649
|
await api.post("/host/heartbeat", {
|
|
12928
13650
|
host_id: hostId,
|
|
12929
13651
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -12973,7 +13695,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
12973
13695
|
}
|
|
12974
13696
|
try {
|
|
12975
13697
|
const hostId = await getHostId();
|
|
12976
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
13698
|
+
const { collectDiagnostics } = await import("../persistent-session-6ADHOTEG.js");
|
|
12977
13699
|
await api.post("/host/heartbeat", {
|
|
12978
13700
|
host_id: hostId,
|
|
12979
13701
|
agent_diagnostics: collectDiagnostics([codeName])
|
|
@@ -13258,9 +13980,9 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
13258
13980
|
const useDoorbell = hostFlagStore().getBoolean("direct-chat-doorbell") || isolationMode(agent.codeName) === "docker";
|
|
13259
13981
|
if (useDoorbell) {
|
|
13260
13982
|
try {
|
|
13261
|
-
const doorbell = directChatDoorbellPath(agent.agentId,
|
|
13262
|
-
|
|
13263
|
-
|
|
13983
|
+
const doorbell = directChatDoorbellPath(agent.agentId, homedir12());
|
|
13984
|
+
mkdirSync10(dirname6(doorbell), { recursive: true });
|
|
13985
|
+
writeFileSync11(doorbell, String(Date.now()));
|
|
13264
13986
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
13265
13987
|
return;
|
|
13266
13988
|
} catch (err) {
|
|
@@ -13368,9 +14090,9 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
|
|
|
13368
14090
|
}
|
|
13369
14091
|
if (run_id) openInjectedRunByCode.set(codeName, run_id);
|
|
13370
14092
|
try {
|
|
13371
|
-
const doorbell = directChatDoorbellPath(agentId,
|
|
13372
|
-
|
|
13373
|
-
|
|
14093
|
+
const doorbell = directChatDoorbellPath(agentId, homedir12());
|
|
14094
|
+
mkdirSync10(dirname6(doorbell), { recursive: true });
|
|
14095
|
+
writeFileSync11(doorbell, String(Date.now()));
|
|
13374
14096
|
} catch (err) {
|
|
13375
14097
|
log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
13376
14098
|
}
|
|
@@ -13471,7 +14193,7 @@ async function processClaudePairSessions(agents) {
|
|
|
13471
14193
|
killPairSession,
|
|
13472
14194
|
pairTmuxSession,
|
|
13473
14195
|
finalizeClaudePairOnboarding
|
|
13474
|
-
} = await import("../claude-pair-runtime-
|
|
14196
|
+
} = await import("../claude-pair-runtime-2ZQZVFKF.js");
|
|
13475
14197
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
13476
14198
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
13477
14199
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -13721,8 +14443,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
13721
14443
|
};
|
|
13722
14444
|
}
|
|
13723
14445
|
async function syncMemories(agent, configDir, log2) {
|
|
13724
|
-
const projectDir =
|
|
13725
|
-
const memoryDir =
|
|
14446
|
+
const projectDir = join23(configDir, agent.code_name, "project");
|
|
14447
|
+
const memoryDir = join23(projectDir, "memory");
|
|
13726
14448
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
13727
14449
|
if (isFreshSync) {
|
|
13728
14450
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -13733,15 +14455,15 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13733
14455
|
}
|
|
13734
14456
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
13735
14457
|
}
|
|
13736
|
-
if (
|
|
14458
|
+
if (existsSync13(memoryDir)) {
|
|
13737
14459
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
13738
14460
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
13739
14461
|
const changedMemories = [];
|
|
13740
14462
|
for (const file of readdirSync5(memoryDir)) {
|
|
13741
14463
|
if (!file.endsWith(".md")) continue;
|
|
13742
14464
|
try {
|
|
13743
|
-
const raw =
|
|
13744
|
-
const fileHash =
|
|
14465
|
+
const raw = readFileSync19(join23(memoryDir, file), "utf-8");
|
|
14466
|
+
const fileHash = createHash14("sha256").update(raw).digest("hex").slice(0, 16);
|
|
13745
14467
|
currentHashes.set(file, fileHash);
|
|
13746
14468
|
if (prevHashes.get(file) === fileHash) continue;
|
|
13747
14469
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -13765,7 +14487,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13765
14487
|
} catch (err) {
|
|
13766
14488
|
for (const mem of changedMemories) {
|
|
13767
14489
|
for (const [file] of currentHashes) {
|
|
13768
|
-
const parsed = parseMemoryFile(
|
|
14490
|
+
const parsed = parseMemoryFile(readFileSync19(join23(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
13769
14491
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
13770
14492
|
}
|
|
13771
14493
|
}
|
|
@@ -13778,29 +14500,29 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
13778
14500
|
}
|
|
13779
14501
|
}
|
|
13780
14502
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
13781
|
-
const localFiles =
|
|
13782
|
-
const localListHash =
|
|
14503
|
+
const localFiles = existsSync13(memoryDir) ? readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
14504
|
+
const localListHash = createHash14("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
13783
14505
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
13784
14506
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
13785
14507
|
try {
|
|
13786
14508
|
const dbMemories = await api.post("/host/memories", {
|
|
13787
14509
|
agent_id: agent.agent_id
|
|
13788
14510
|
});
|
|
13789
|
-
const responseHash =
|
|
14511
|
+
const responseHash = createHash14("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
13790
14512
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
13791
14513
|
return true;
|
|
13792
14514
|
}
|
|
13793
14515
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
13794
14516
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
13795
14517
|
if (dbMemories.memories?.length) {
|
|
13796
|
-
|
|
14518
|
+
mkdirSync10(memoryDir, { recursive: true });
|
|
13797
14519
|
let written = 0;
|
|
13798
14520
|
let overwritten = 0;
|
|
13799
14521
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
13800
14522
|
const mem = dbMemories.memories[i];
|
|
13801
14523
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
13802
14524
|
const slug = rawSlug || `memory-${i}`;
|
|
13803
|
-
const filePath =
|
|
14525
|
+
const filePath = join23(memoryDir, `${slug}.md`);
|
|
13804
14526
|
const desired = `---
|
|
13805
14527
|
name: ${JSON.stringify(mem.name)}
|
|
13806
14528
|
type: ${mem.type}
|
|
@@ -13809,23 +14531,23 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
13809
14531
|
|
|
13810
14532
|
${mem.content}
|
|
13811
14533
|
`;
|
|
13812
|
-
if (
|
|
14534
|
+
if (existsSync13(filePath)) {
|
|
13813
14535
|
let existing = "";
|
|
13814
14536
|
try {
|
|
13815
|
-
existing =
|
|
14537
|
+
existing = readFileSync19(filePath, "utf-8");
|
|
13816
14538
|
} catch {
|
|
13817
14539
|
}
|
|
13818
14540
|
if (existing === desired) continue;
|
|
13819
|
-
|
|
14541
|
+
writeFileSync11(filePath, desired);
|
|
13820
14542
|
overwritten++;
|
|
13821
14543
|
} else {
|
|
13822
|
-
|
|
14544
|
+
writeFileSync11(filePath, desired);
|
|
13823
14545
|
written++;
|
|
13824
14546
|
}
|
|
13825
14547
|
}
|
|
13826
14548
|
if (written > 0 || overwritten > 0) {
|
|
13827
14549
|
const updatedFiles = readdirSync5(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
13828
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
14550
|
+
lastLocalFileHash.set(agent.agent_id, createHash14("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
13829
14551
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
13830
14552
|
}
|
|
13831
14553
|
}
|
|
@@ -13836,9 +14558,9 @@ ${mem.content}
|
|
|
13836
14558
|
}
|
|
13837
14559
|
}
|
|
13838
14560
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
13839
|
-
if (
|
|
14561
|
+
if (existsSync13(agentDir)) {
|
|
13840
14562
|
try {
|
|
13841
|
-
|
|
14563
|
+
rmSync5(agentDir, { recursive: true, force: true });
|
|
13842
14564
|
log(`Removed provision directory for '${codeName}'`);
|
|
13843
14565
|
} catch (err) {
|
|
13844
14566
|
log(`Failed to remove provision dir for '${codeName}': ${err.message}`);
|
|
@@ -14058,6 +14780,7 @@ async function stopPolling(opts = {}) {
|
|
|
14058
14780
|
stopCaffeinate();
|
|
14059
14781
|
stopRealtimeChat();
|
|
14060
14782
|
stopAllOpencodeSlackIngests(log);
|
|
14783
|
+
stopAllOpencodeTelegramIngests(log);
|
|
14061
14784
|
log(formatDrainShutdownLine({ step: "stop-subsystems", elapsedMs: Date.now() - subsysStartedAt }));
|
|
14062
14785
|
for (const codeName of [...scheduledRunsByCode.keys()]) {
|
|
14063
14786
|
closeScheduledRunsForCode(codeName, "cancelled", "manager shutdown");
|
|
@@ -14072,8 +14795,8 @@ function startManager(opts) {
|
|
|
14072
14795
|
config = opts;
|
|
14073
14796
|
try {
|
|
14074
14797
|
const stateFile = getStateFile();
|
|
14075
|
-
if (
|
|
14076
|
-
const raw =
|
|
14798
|
+
if (existsSync13(stateFile)) {
|
|
14799
|
+
const raw = readFileSync19(stateFile, "utf-8");
|
|
14077
14800
|
const parsed = JSON.parse(raw);
|
|
14078
14801
|
if (Array.isArray(parsed.agents)) {
|
|
14079
14802
|
state6.agents = parsed.agents;
|
|
@@ -14100,7 +14823,7 @@ function startManager(opts) {
|
|
|
14100
14823
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
14101
14824
|
}
|
|
14102
14825
|
log(
|
|
14103
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
14826
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join23(homedir12(), ".augmented", "manager.log")}`
|
|
14104
14827
|
);
|
|
14105
14828
|
deployMcpAssets();
|
|
14106
14829
|
reapOrphanChannelMcps({ log });
|
|
@@ -14121,7 +14844,7 @@ async function reapOrphanedClaudePids() {
|
|
|
14121
14844
|
const looksLikeClaude = (pid) => {
|
|
14122
14845
|
if (process.platform !== "linux") return true;
|
|
14123
14846
|
try {
|
|
14124
|
-
const comm =
|
|
14847
|
+
const comm = readFileSync19(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
14125
14848
|
return comm.includes("claude");
|
|
14126
14849
|
} catch {
|
|
14127
14850
|
return false;
|
|
@@ -14218,14 +14941,14 @@ function restartRunningChannelMcps(basenames) {
|
|
|
14218
14941
|
}
|
|
14219
14942
|
}
|
|
14220
14943
|
function deployMcpAssets() {
|
|
14221
|
-
const targetDir =
|
|
14222
|
-
|
|
14944
|
+
const targetDir = join23(homedir12(), ".augmented", "_mcp");
|
|
14945
|
+
mkdirSync10(targetDir, { recursive: true });
|
|
14223
14946
|
const moduleDir = dirname6(fileURLToPath(import.meta.url));
|
|
14224
14947
|
let mcpSourceDir = "";
|
|
14225
14948
|
let dir = moduleDir;
|
|
14226
14949
|
for (let i = 0; i < 6; i++) {
|
|
14227
|
-
const candidate =
|
|
14228
|
-
if (
|
|
14950
|
+
const candidate = join23(dir, "dist", "mcp");
|
|
14951
|
+
if (existsSync13(join23(candidate, "index.js"))) {
|
|
14229
14952
|
mcpSourceDir = candidate;
|
|
14230
14953
|
break;
|
|
14231
14954
|
}
|
|
@@ -14240,8 +14963,8 @@ function deployMcpAssets() {
|
|
|
14240
14963
|
const changedBasenames = [];
|
|
14241
14964
|
const fileHash = (p) => {
|
|
14242
14965
|
try {
|
|
14243
|
-
if (!
|
|
14244
|
-
return
|
|
14966
|
+
if (!existsSync13(p)) return null;
|
|
14967
|
+
return createHash14("sha256").update(readFileSync19(p)).digest("hex");
|
|
14245
14968
|
} catch {
|
|
14246
14969
|
return null;
|
|
14247
14970
|
}
|
|
@@ -14305,9 +15028,9 @@ function deployMcpAssets() {
|
|
|
14305
15028
|
// needs restarting to pick up a token rotation.
|
|
14306
15029
|
"xero.js"
|
|
14307
15030
|
]) {
|
|
14308
|
-
const src =
|
|
14309
|
-
const dst =
|
|
14310
|
-
if (!
|
|
15031
|
+
const src = join23(mcpSourceDir, file);
|
|
15032
|
+
const dst = join23(targetDir, file);
|
|
15033
|
+
if (!existsSync13(src)) continue;
|
|
14311
15034
|
const before = fileHash(dst);
|
|
14312
15035
|
try {
|
|
14313
15036
|
copyFileSync(src, dst);
|
|
@@ -14324,23 +15047,23 @@ function deployMcpAssets() {
|
|
|
14324
15047
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
14325
15048
|
restartRunningChannelMcps(changedBasenames);
|
|
14326
15049
|
}
|
|
14327
|
-
const localMcpPath =
|
|
15050
|
+
const localMcpPath = join23(targetDir, "index.js");
|
|
14328
15051
|
try {
|
|
14329
|
-
const agentsDir =
|
|
14330
|
-
if (
|
|
15052
|
+
const agentsDir = join23(homedir12(), ".augmented", "agents");
|
|
15053
|
+
if (existsSync13(agentsDir)) {
|
|
14331
15054
|
for (const entry of readdirSync5(agentsDir, { withFileTypes: true })) {
|
|
14332
15055
|
if (!entry.isDirectory()) continue;
|
|
14333
15056
|
for (const subdir of ["provision", "project"]) {
|
|
14334
|
-
const mcpJsonPath =
|
|
15057
|
+
const mcpJsonPath = join23(agentsDir, entry.name, subdir, ".mcp.json");
|
|
14335
15058
|
try {
|
|
14336
|
-
const raw =
|
|
15059
|
+
const raw = readFileSync19(mcpJsonPath, "utf-8");
|
|
14337
15060
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
14338
15061
|
const mcpConfig = JSON.parse(raw);
|
|
14339
15062
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
14340
15063
|
if (!augServer) continue;
|
|
14341
15064
|
augServer.command = "node";
|
|
14342
15065
|
augServer.args = [localMcpPath];
|
|
14343
|
-
|
|
15066
|
+
writeFileSync11(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
14344
15067
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
14345
15068
|
} catch {
|
|
14346
15069
|
}
|