@integrity-labs/agt-cli 0.28.564 → 0.28.566
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-TOGADVFR.js → chunk-2DEE3OVI.js} +30 -1
- package/dist/{chunk-TOGADVFR.js.map → chunk-2DEE3OVI.js.map} +1 -1
- package/dist/{chunk-WYZBXSRI.js → chunk-IEYDYCXL.js} +3 -3
- package/dist/{claude-pair-runtime-63Y4MIMU.js → claude-pair-runtime-6AOWAYHH.js} +2 -2
- package/dist/lib/manager-worker.js +1217 -416
- package/dist/lib/manager-worker.js.map +1 -1
- package/dist/mcp/direct-chat-channel.js +29 -0
- package/dist/mcp/origami.js +29 -0
- package/dist/mcp/slack-channel.js +29 -0
- package/dist/mcp/telegram-channel.js +29 -0
- package/dist/{persistent-session-MLLLDI5U.js → persistent-session-CI37H442.js} +2 -2
- package/dist/{responsiveness-probe-QMAIFW4P.js → responsiveness-probe-O33QL25H.js} +2 -2
- package/package.json +1 -1
- /package/dist/{chunk-WYZBXSRI.js.map → chunk-IEYDYCXL.js.map} +0 -0
- /package/dist/{claude-pair-runtime-63Y4MIMU.js.map → claude-pair-runtime-6AOWAYHH.js.map} +0 -0
- /package/dist/{persistent-session-MLLLDI5U.js.map → persistent-session-CI37H442.js.map} +0 -0
- /package/dist/{responsiveness-probe-QMAIFW4P.js.map → responsiveness-probe-O33QL25H.js.map} +0 -0
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
safeWriteJsonAtomic,
|
|
54
54
|
setConfigHash,
|
|
55
55
|
tripClass
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-IEYDYCXL.js";
|
|
57
57
|
import {
|
|
58
58
|
getProjectDir as getProjectDir2,
|
|
59
59
|
getReadyTasks,
|
|
@@ -182,17 +182,17 @@ import {
|
|
|
182
182
|
toOpencodeModel,
|
|
183
183
|
transcriptActivityAgeSeconds,
|
|
184
184
|
writeEgressAllowlist
|
|
185
|
-
} from "../chunk-
|
|
185
|
+
} from "../chunk-2DEE3OVI.js";
|
|
186
186
|
import {
|
|
187
187
|
reapOrphanChannelMcps
|
|
188
188
|
} from "../chunk-XWVM4KPK.js";
|
|
189
189
|
|
|
190
190
|
// src/lib/manager-worker.ts
|
|
191
|
-
import { createHash as
|
|
192
|
-
import { readFileSync as
|
|
191
|
+
import { createHash as createHash17 } from "crypto";
|
|
192
|
+
import { readFileSync as readFileSync26, writeFileSync as writeFileSync14, mkdirSync as mkdirSync11, existsSync as existsSync14, rmSync as rmSync5, readdirSync as readdirSync9, statSync as statSync8, copyFileSync } from "fs";
|
|
193
193
|
import { execFileSync as syncExecFile } from "child_process";
|
|
194
|
-
import { join as
|
|
195
|
-
import { homedir as
|
|
194
|
+
import { join as join33, dirname as dirname9, delimiter as pathDelimiter } from "path";
|
|
195
|
+
import { homedir as homedir15 } from "os";
|
|
196
196
|
import { fileURLToPath } from "url";
|
|
197
197
|
|
|
198
198
|
// src/lib/claude-code-upgrade-throttle.ts
|
|
@@ -3503,14 +3503,804 @@ async function reportSkip2(api2, agentId, conversationId, log2, codeName) {
|
|
|
3503
3503
|
}
|
|
3504
3504
|
}
|
|
3505
3505
|
|
|
3506
|
-
// src/lib/
|
|
3507
|
-
import {
|
|
3506
|
+
// src/lib/tool-call-audit.ts
|
|
3507
|
+
import { homedir as homedir9 } from "os";
|
|
3508
|
+
import { join as join17 } from "path";
|
|
3509
|
+
|
|
3510
|
+
// src/lib/tool-call-path-salt.ts
|
|
3511
|
+
import { randomBytes } from "crypto";
|
|
3512
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync5, readFileSync as readFileSync11, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
|
|
3508
3513
|
import { homedir as homedir7 } from "os";
|
|
3509
|
-
import { join as join14 } from "path";
|
|
3510
|
-
var
|
|
3511
|
-
var
|
|
3514
|
+
import { dirname as dirname5, join as join14 } from "path";
|
|
3515
|
+
var SALT_BYTES = 32;
|
|
3516
|
+
var SALT_RE = /^[0-9a-f]{64}$/;
|
|
3517
|
+
function pathSaltPath(codeName, homeDir) {
|
|
3518
|
+
const home = homeDir ?? (process.env["HOME"]?.trim() || homedir7());
|
|
3519
|
+
const key = agentRuntimeKey(codeName, homeDir);
|
|
3520
|
+
return join14(home, ".augmented", key, "tool-call-path-salt");
|
|
3521
|
+
}
|
|
3522
|
+
function readToolCallPathSalt(codeName, homeDir) {
|
|
3523
|
+
let file;
|
|
3524
|
+
try {
|
|
3525
|
+
file = pathSaltPath(codeName, homeDir);
|
|
3526
|
+
} catch {
|
|
3527
|
+
return null;
|
|
3528
|
+
}
|
|
3529
|
+
try {
|
|
3530
|
+
if (existsSync3(file)) {
|
|
3531
|
+
const existing = readFileSync11(file, "utf-8").trim();
|
|
3532
|
+
if (SALT_RE.test(existing)) return existing;
|
|
3533
|
+
}
|
|
3534
|
+
} catch {
|
|
3535
|
+
}
|
|
3536
|
+
const salt = randomBytes(SALT_BYTES).toString("hex");
|
|
3537
|
+
const tmp = `${file}.tmp.${process.pid}`;
|
|
3538
|
+
try {
|
|
3539
|
+
mkdirSync5(dirname5(file), { recursive: true, mode: 448 });
|
|
3540
|
+
writeFileSync6(tmp, `${salt}
|
|
3541
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
3542
|
+
renameSync3(tmp, file);
|
|
3543
|
+
} catch {
|
|
3544
|
+
try {
|
|
3545
|
+
unlinkSync(tmp);
|
|
3546
|
+
} catch {
|
|
3547
|
+
}
|
|
3548
|
+
return null;
|
|
3549
|
+
}
|
|
3550
|
+
return salt;
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
// src/lib/tool-call-scan.ts
|
|
3554
|
+
import { statSync as statSync4 } from "fs";
|
|
3555
|
+
|
|
3556
|
+
// src/lib/host-archive-address.ts
|
|
3557
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
3558
|
+
var DEFAULT_ARCHIVE_ADDRESS_PATH = "/var/lib/augmented/session-archive-address.json";
|
|
3559
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
3560
|
+
var cache = /* @__PURE__ */ new Map();
|
|
3561
|
+
function str(value) {
|
|
3562
|
+
if (typeof value !== "string") return null;
|
|
3563
|
+
const trimmed = value.trim();
|
|
3564
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3565
|
+
}
|
|
3566
|
+
function readHostArchiveAddress(path) {
|
|
3567
|
+
const file = path ?? process.env["ARCHIVE_ADDRESS_FILE"]?.trim() ?? process.env["AGT_SESSION_ARCHIVE_ADDRESS_FILE"]?.trim() ?? DEFAULT_ARCHIVE_ADDRESS_PATH;
|
|
3568
|
+
const now = Date.now();
|
|
3569
|
+
const hit = cache.get(file);
|
|
3570
|
+
if (hit && now - hit.at < CACHE_TTL_MS) return hit.value;
|
|
3571
|
+
let value = null;
|
|
3572
|
+
try {
|
|
3573
|
+
const raw = readFileSync12(file, "utf-8");
|
|
3574
|
+
const parsed = JSON.parse(raw);
|
|
3575
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
3576
|
+
const obj = parsed;
|
|
3577
|
+
const bucket = str(obj["bucket"]);
|
|
3578
|
+
const keyPrefix = str(obj["key_prefix"]);
|
|
3579
|
+
if (bucket && keyPrefix) {
|
|
3580
|
+
value = {
|
|
3581
|
+
bucket,
|
|
3582
|
+
region: str(obj["region"]),
|
|
3583
|
+
instanceId: str(obj["instance_id"]),
|
|
3584
|
+
keyPrefix
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
} catch {
|
|
3589
|
+
value = null;
|
|
3590
|
+
}
|
|
3591
|
+
cache.set(file, { at: now, value });
|
|
3592
|
+
return value;
|
|
3593
|
+
}
|
|
3594
|
+
|
|
3595
|
+
// src/lib/tool-call-extractor.ts
|
|
3596
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync13, readSync, readdirSync as readdirSync4 } from "fs";
|
|
3597
|
+
import { basename, join as join15, relative } from "path";
|
|
3598
|
+
import { StringDecoder } from "string_decoder";
|
|
3599
|
+
|
|
3600
|
+
// src/lib/tool-call-redaction.ts
|
|
3601
|
+
import { createHmac } from "crypto";
|
|
3602
|
+
|
|
3603
|
+
// ../../packages/core/dist/tool-calls/input-hash.js
|
|
3604
|
+
import { createHash as createHash7 } from "crypto";
|
|
3605
|
+
function hashToolInput(input) {
|
|
3606
|
+
if (input === void 0)
|
|
3607
|
+
return null;
|
|
3608
|
+
try {
|
|
3609
|
+
return createHash7("sha256").update(canonicalJson2(input)).digest("hex");
|
|
3610
|
+
} catch {
|
|
3611
|
+
return null;
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
function canonicalJson2(value) {
|
|
3615
|
+
if (value === null || typeof value !== "object")
|
|
3616
|
+
return JSON.stringify(value) ?? "null";
|
|
3617
|
+
if (Array.isArray(value))
|
|
3618
|
+
return `[${value.map(canonicalJson2).join(",")}]`;
|
|
3619
|
+
const entries = Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
3620
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson2(v)}`).join(",")}}`;
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
// src/lib/tool-call-redaction.ts
|
|
3624
|
+
var REDACTION_RULE_VERSION = "r2";
|
|
3625
|
+
var REDACTED_TARGET_SHAPES = {
|
|
3626
|
+
argv0: /^[A-Za-z0-9._+-]{1,64}$/,
|
|
3627
|
+
mcp_tool: /^[A-Za-z0-9._+-]{1,64}$/,
|
|
3628
|
+
path_hash: /^[0-9a-f]{32}$/,
|
|
3629
|
+
url_host: /^[A-Za-z0-9.-]{1,253}$/
|
|
3630
|
+
};
|
|
3631
|
+
var NONE = { target: null, kind: "none" };
|
|
3632
|
+
var SAFE_IDENTIFIER_RE = /^[A-Za-z0-9._+-]{1,64}$/;
|
|
3633
|
+
var ENV_ASSIGNMENT_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
3634
|
+
function hashPath(path, salt) {
|
|
3635
|
+
if (!salt) return null;
|
|
3636
|
+
return createHmac("sha256", salt).update(path).digest("hex").slice(0, 32);
|
|
3637
|
+
}
|
|
3638
|
+
function parseArgv0(command) {
|
|
3639
|
+
const firstLine = command.split("\n", 1)[0] ?? "";
|
|
3640
|
+
const tokens = firstLine.trim().split(/\s+/).filter(Boolean);
|
|
3641
|
+
let i = 0;
|
|
3642
|
+
while (i < tokens.length && ENV_ASSIGNMENT_RE.test(tokens[i])) i++;
|
|
3643
|
+
const token = tokens[i];
|
|
3644
|
+
if (!token) return { name: null, isPath: false };
|
|
3645
|
+
if (token.includes("/")) return { name: token, isPath: true };
|
|
3646
|
+
return { name: SAFE_IDENTIFIER_RE.test(token) ? token : null, isPath: false };
|
|
3647
|
+
}
|
|
3648
|
+
function stringField(input, field) {
|
|
3649
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return null;
|
|
3650
|
+
const v = input[field];
|
|
3651
|
+
return typeof v === "string" && v.length > 0 ? v : null;
|
|
3652
|
+
}
|
|
3653
|
+
function parseMcpTool(toolName) {
|
|
3654
|
+
const m = /^mcp__[^_]+(?:_[^_]+)*?__(.+)$/.exec(toolName);
|
|
3655
|
+
const tool = m?.[1];
|
|
3656
|
+
return tool && SAFE_IDENTIFIER_RE.test(tool) ? tool : null;
|
|
3657
|
+
}
|
|
3658
|
+
function redactToolTarget(toolName, input, ctx) {
|
|
3659
|
+
const out = redactToolTargetInner(toolName, input, ctx);
|
|
3660
|
+
if (out.kind === "none") return out.target === null ? out : NONE;
|
|
3661
|
+
return out.target !== null && REDACTED_TARGET_SHAPES[out.kind].test(out.target) ? out : NONE;
|
|
3662
|
+
}
|
|
3663
|
+
function redactToolTargetInner(toolName, input, ctx) {
|
|
3664
|
+
if (ctx.hashOnly) return NONE;
|
|
3665
|
+
if (toolName.startsWith("mcp__")) {
|
|
3666
|
+
const tool = parseMcpTool(toolName);
|
|
3667
|
+
return tool ? { target: tool, kind: "mcp_tool" } : NONE;
|
|
3668
|
+
}
|
|
3669
|
+
switch (toolName) {
|
|
3670
|
+
case "Bash": {
|
|
3671
|
+
const command = stringField(input, "command");
|
|
3672
|
+
if (!command) return NONE;
|
|
3673
|
+
const { name, isPath } = parseArgv0(command);
|
|
3674
|
+
if (!name) return NONE;
|
|
3675
|
+
if (isPath) {
|
|
3676
|
+
const hashed = hashPath(name, ctx.pathHashSalt);
|
|
3677
|
+
return hashed ? { target: hashed, kind: "path_hash" } : NONE;
|
|
3678
|
+
}
|
|
3679
|
+
const scrubbed = scrubSensitive(name);
|
|
3680
|
+
return SAFE_IDENTIFIER_RE.test(scrubbed) ? { target: scrubbed, kind: "argv0" } : NONE;
|
|
3681
|
+
}
|
|
3682
|
+
// `MultiEdit`, `NotebookEdit` and `NotebookRead` are allowlisted WITHOUT
|
|
3683
|
+
// having been observed — 0 occurrences each across 81 real transcripts,
|
|
3684
|
+
// where only `Edit` appears. Normally that is exactly the speculative
|
|
3685
|
+
// widening this module refuses to do, so the exception is worth stating: the
|
|
3686
|
+
// sole disclosure on this arm is a keyed hash. If one of these names turns
|
|
3687
|
+
// out to belong to some unrelated future tool, the row carries an unreadable
|
|
3688
|
+
// digest of whatever its path field held — no content escapes either way.
|
|
3689
|
+
// Against that, omitting a real file tool is a SILENT audit gap, which is
|
|
3690
|
+
// the costlier error. (CodeRabbit.)
|
|
3691
|
+
case "Read":
|
|
3692
|
+
case "Write":
|
|
3693
|
+
case "Edit":
|
|
3694
|
+
case "MultiEdit": {
|
|
3695
|
+
const path = stringField(input, "file_path");
|
|
3696
|
+
if (!path) return NONE;
|
|
3697
|
+
const hashed = hashPath(path, ctx.pathHashSalt);
|
|
3698
|
+
return hashed ? { target: hashed, kind: "path_hash" } : NONE;
|
|
3699
|
+
}
|
|
3700
|
+
case "NotebookEdit":
|
|
3701
|
+
case "NotebookRead": {
|
|
3702
|
+
const path = stringField(input, "notebook_path");
|
|
3703
|
+
if (!path) return NONE;
|
|
3704
|
+
const hashed = hashPath(path, ctx.pathHashSalt);
|
|
3705
|
+
return hashed ? { target: hashed, kind: "path_hash" } : NONE;
|
|
3706
|
+
}
|
|
3707
|
+
case "WebFetch": {
|
|
3708
|
+
const url = stringField(input, "url");
|
|
3709
|
+
if (!url) return NONE;
|
|
3710
|
+
try {
|
|
3711
|
+
const host = new URL(url).hostname;
|
|
3712
|
+
return host ? { target: host, kind: "url_host" } : NONE;
|
|
3713
|
+
} catch {
|
|
3714
|
+
return NONE;
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
default:
|
|
3718
|
+
return NONE;
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
// src/lib/tool-call-extractor.ts
|
|
3723
|
+
var EXTRACTOR_VERSION = "e1";
|
|
3724
|
+
function enumerateSessionTranscripts(transcriptDir, sessionId, projectsRoot) {
|
|
3725
|
+
const files = [];
|
|
3726
|
+
const mainAbs = join15(transcriptDir, `${sessionId}.jsonl`);
|
|
3727
|
+
files.push({
|
|
3728
|
+
absPath: mainAbs,
|
|
3729
|
+
relPath: relative(projectsRoot, mainAbs),
|
|
3730
|
+
ref: "main",
|
|
3731
|
+
isSubagent: false,
|
|
3732
|
+
subagentId: null
|
|
3733
|
+
});
|
|
3734
|
+
const subDir = join15(transcriptDir, sessionId, "subagents");
|
|
3735
|
+
let entries;
|
|
3736
|
+
try {
|
|
3737
|
+
entries = readdirSync4(subDir);
|
|
3738
|
+
} catch {
|
|
3739
|
+
return files;
|
|
3740
|
+
}
|
|
3741
|
+
for (const name of entries) {
|
|
3742
|
+
if (!name.endsWith(".jsonl")) continue;
|
|
3743
|
+
const abs = join15(subDir, name);
|
|
3744
|
+
const stem = basename(name, ".jsonl");
|
|
3745
|
+
files.push({
|
|
3746
|
+
absPath: abs,
|
|
3747
|
+
relPath: relative(projectsRoot, abs),
|
|
3748
|
+
ref: `subagent:${stem}`,
|
|
3749
|
+
isSubagent: true,
|
|
3750
|
+
subagentId: stem.startsWith("agent-") ? stem.slice("agent-".length) : stem
|
|
3751
|
+
});
|
|
3752
|
+
}
|
|
3753
|
+
return files;
|
|
3754
|
+
}
|
|
3755
|
+
function scanLines(lines, file, opts) {
|
|
3756
|
+
const pending2 = /* @__PURE__ */ new Map();
|
|
3757
|
+
const ordered = [];
|
|
3758
|
+
for (const [lineOffset, rawLine, byteStart] of lines) {
|
|
3759
|
+
const trimmed = rawLine.trim();
|
|
3760
|
+
if (trimmed.length === 0) continue;
|
|
3761
|
+
let record;
|
|
3762
|
+
try {
|
|
3763
|
+
record = JSON.parse(trimmed);
|
|
3764
|
+
} catch {
|
|
3765
|
+
continue;
|
|
3766
|
+
}
|
|
3767
|
+
if (typeof record !== "object" || record === null) continue;
|
|
3768
|
+
const message = record.message;
|
|
3769
|
+
const blocks = message && typeof message === "object" && Array.isArray(message.content) ? message.content : null;
|
|
3770
|
+
if (!blocks) continue;
|
|
3771
|
+
for (const raw of blocks) {
|
|
3772
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
3773
|
+
const block = raw;
|
|
3774
|
+
if (block.type === "tool_use") {
|
|
3775
|
+
const id = typeof block.id === "string" ? block.id : null;
|
|
3776
|
+
const name = typeof block.name === "string" ? block.name : null;
|
|
3777
|
+
if (!id || !name) continue;
|
|
3778
|
+
const { target, kind } = redactToolTarget(name, block.input, opts.redaction);
|
|
3779
|
+
const occurredAt = typeof record.timestamp === "string" ? record.timestamp : null;
|
|
3780
|
+
const built = {
|
|
3781
|
+
tool_use_id: id,
|
|
3782
|
+
tool_name: name,
|
|
3783
|
+
occurred_at: occurredAt,
|
|
3784
|
+
session_id: typeof record.sessionId === "string" ? record.sessionId : null,
|
|
3785
|
+
transcript_rel_path: file.relPath,
|
|
3786
|
+
transcript_ref: file.ref,
|
|
3787
|
+
line_offset: lineOffset,
|
|
3788
|
+
// The FILE is the sole authority, deliberately — not `record.isSidechain`.
|
|
3789
|
+
//
|
|
3790
|
+
// These three fields are one fact stated three ways and must not be
|
|
3791
|
+
// able to contradict each other: `is_subagent` true iff
|
|
3792
|
+
// `transcript_ref` starts with `subagent:` iff `subagent_id` is set.
|
|
3793
|
+
// Letting `record.isSidechain` raise `is_subagent` on its own could
|
|
3794
|
+
// produce is_subagent=true with transcript_ref='main' and a null
|
|
3795
|
+
// subagent_id — a row claiming a delegated worker made the call while
|
|
3796
|
+
// addressing the parent's transcript. `transcript_ref` is an EVIDENCE
|
|
3797
|
+
// ADDRESS: it must name the file the line actually lives in, so the
|
|
3798
|
+
// file has to win and the other two follow it. (CodeRabbit.)
|
|
3799
|
+
//
|
|
3800
|
+
// Nothing is lost today: no main-transcript record in the measured
|
|
3801
|
+
// corpus carried isSidechain on a tool_use, and every sidecar record
|
|
3802
|
+
// did. If Claude Code ever inlines sidechain turns into the main file,
|
|
3803
|
+
// record-level sidechain-ness needs its own field rather than
|
|
3804
|
+
// overloading this one.
|
|
3805
|
+
is_subagent: file.isSubagent,
|
|
3806
|
+
subagent_id: file.isSubagent ? typeof record.agentId === "string" && record.agentId ? record.agentId : file.subagentId : null,
|
|
3807
|
+
is_error: null,
|
|
3808
|
+
// resolved on pairing below
|
|
3809
|
+
input_sha256: hashToolInput(block.input),
|
|
3810
|
+
redacted_target: target,
|
|
3811
|
+
redacted_target_kind: kind,
|
|
3812
|
+
extractor_version: EXTRACTOR_VERSION,
|
|
3813
|
+
redaction_rule_version: REDACTION_RULE_VERSION,
|
|
3814
|
+
archive_bucket: opts.archive?.bucket ?? null,
|
|
3815
|
+
archive_region: opts.archive?.region ?? null,
|
|
3816
|
+
host_instance_id: opts.archive?.instanceId ?? null,
|
|
3817
|
+
archive_key_prefix: opts.archive?.keyPrefix ?? null
|
|
3818
|
+
};
|
|
3819
|
+
ordered.push(built);
|
|
3820
|
+
pending2.set(id, { record: built, byteStart });
|
|
3821
|
+
continue;
|
|
3822
|
+
}
|
|
3823
|
+
if (block.type === "tool_result") {
|
|
3824
|
+
const id = typeof block.tool_use_id === "string" ? block.tool_use_id : null;
|
|
3825
|
+
if (!id) continue;
|
|
3826
|
+
const call = pending2.get(id);
|
|
3827
|
+
if (!call) continue;
|
|
3828
|
+
call.record.is_error = "is_error" in block && block.is_error === null ? null : block.is_error === true;
|
|
3829
|
+
pending2.delete(id);
|
|
3830
|
+
}
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
return { records: ordered, pending: pending2 };
|
|
3834
|
+
}
|
|
3835
|
+
var WINDOW_CHUNK_BYTES = 64 * 1024;
|
|
3836
|
+
function extractTranscriptWindow(file, opts, from) {
|
|
3837
|
+
let fd;
|
|
3838
|
+
try {
|
|
3839
|
+
fd = openSync(file.absPath, "r");
|
|
3840
|
+
} catch {
|
|
3841
|
+
return {
|
|
3842
|
+
records: [],
|
|
3843
|
+
endByte: from.byteOffset,
|
|
3844
|
+
endLine: from.lineOffset,
|
|
3845
|
+
unpaired: [],
|
|
3846
|
+
missing: true,
|
|
3847
|
+
rewound: false
|
|
3848
|
+
};
|
|
3849
|
+
}
|
|
3850
|
+
let startByte = from.byteOffset;
|
|
3851
|
+
let startLine = from.lineOffset;
|
|
3852
|
+
let rewound = false;
|
|
3853
|
+
try {
|
|
3854
|
+
const { size } = fstatSync(fd);
|
|
3855
|
+
if (startByte > size) {
|
|
3856
|
+
startByte = 0;
|
|
3857
|
+
startLine = 0;
|
|
3858
|
+
rewound = true;
|
|
3859
|
+
}
|
|
3860
|
+
} catch {
|
|
3861
|
+
}
|
|
3862
|
+
const decoder = new StringDecoder("utf8");
|
|
3863
|
+
const buf = Buffer.allocUnsafe(WINDOW_CHUNK_BYTES);
|
|
3864
|
+
let position = startByte;
|
|
3865
|
+
let runningByte = startByte;
|
|
3866
|
+
let lineOffset = startLine;
|
|
3867
|
+
let carry = "";
|
|
3868
|
+
const pairs = [];
|
|
3869
|
+
try {
|
|
3870
|
+
for (; ; ) {
|
|
3871
|
+
const bytesRead = readSync(fd, buf, 0, WINDOW_CHUNK_BYTES, position);
|
|
3872
|
+
if (bytesRead <= 0) break;
|
|
3873
|
+
position += bytesRead;
|
|
3874
|
+
carry += decoder.write(buf.subarray(0, bytesRead));
|
|
3875
|
+
let nl;
|
|
3876
|
+
while ((nl = carry.indexOf("\n")) !== -1) {
|
|
3877
|
+
const line = carry.slice(0, nl);
|
|
3878
|
+
pairs.push([lineOffset, line, runningByte]);
|
|
3879
|
+
runningByte += Buffer.byteLength(line, "utf8") + 1;
|
|
3880
|
+
lineOffset += 1;
|
|
3881
|
+
carry = carry.slice(nl + 1);
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
} catch {
|
|
3885
|
+
} finally {
|
|
3886
|
+
try {
|
|
3887
|
+
closeSync(fd);
|
|
3888
|
+
} catch {
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
const { records, pending: pending2 } = scanLines(pairs, file, opts);
|
|
3892
|
+
const unpaired = [...pending2.values()].map((p) => ({
|
|
3893
|
+
toolUseId: p.record.tool_use_id,
|
|
3894
|
+
byteStart: p.byteStart,
|
|
3895
|
+
lineOffset: p.record.line_offset
|
|
3896
|
+
})).sort((a, b) => a.byteStart - b.byteStart);
|
|
3897
|
+
return { records, endByte: runningByte, endLine: lineOffset, unpaired, missing: false, rewound };
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
// src/lib/tool-call-cursor.ts
|
|
3901
|
+
import { existsSync as existsSync4, readFileSync as readFileSync14 } from "fs";
|
|
3902
|
+
import { homedir as homedir8 } from "os";
|
|
3903
|
+
import { join as join16 } from "path";
|
|
3904
|
+
var COVERAGE_DISPOSITIONS = [
|
|
3905
|
+
"ok",
|
|
3906
|
+
"not_entitled",
|
|
3907
|
+
"ingest_failed",
|
|
3908
|
+
"batch_rejected",
|
|
3909
|
+
"agent_unbound",
|
|
3910
|
+
"transcript_missing"
|
|
3911
|
+
];
|
|
3912
|
+
function mayAdvance(outcome) {
|
|
3913
|
+
switch (outcome.kind) {
|
|
3914
|
+
case "ingested":
|
|
3915
|
+
return true;
|
|
3916
|
+
// Definitive: nothing will EVER be collected for this org. Holding would
|
|
3917
|
+
// re-offer the same lines forever against an answer that cannot change.
|
|
3918
|
+
case "not_entitled":
|
|
3919
|
+
return true;
|
|
3920
|
+
// A producer bug. Retrying is futile and holding wedges every later call
|
|
3921
|
+
// behind it, so step over — loudly, and counted.
|
|
3922
|
+
case "batch_rejected":
|
|
3923
|
+
return true;
|
|
3924
|
+
case "agent_unbound":
|
|
3925
|
+
return false;
|
|
3926
|
+
case "ingest_failed":
|
|
3927
|
+
return false;
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
function dispositionFor(outcome) {
|
|
3931
|
+
switch (outcome.kind) {
|
|
3932
|
+
case "ingested":
|
|
3933
|
+
return "ok";
|
|
3934
|
+
case "not_entitled":
|
|
3935
|
+
return "not_entitled";
|
|
3936
|
+
case "batch_rejected":
|
|
3937
|
+
return "batch_rejected";
|
|
3938
|
+
case "agent_unbound":
|
|
3939
|
+
return "agent_unbound";
|
|
3940
|
+
case "ingest_failed":
|
|
3941
|
+
return "ingest_failed";
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
var UNPAIRED_GRACE_MS = 30 * 60 * 1e3;
|
|
3945
|
+
var KEY_SEP = "\0";
|
|
3946
|
+
function cursorKey(sessionId, transcriptRef) {
|
|
3947
|
+
return `${sessionId ?? ""}${KEY_SEP}${transcriptRef}`;
|
|
3948
|
+
}
|
|
3949
|
+
function parseCursorKey(key) {
|
|
3950
|
+
const i = key.indexOf(KEY_SEP);
|
|
3951
|
+
if (i < 0) return { sessionId: null, transcriptRef: key };
|
|
3952
|
+
const sessionId = key.slice(0, i);
|
|
3953
|
+
return { sessionId: sessionId.length > 0 ? sessionId : null, transcriptRef: key.slice(i + 1) };
|
|
3954
|
+
}
|
|
3955
|
+
function cursorStatePath(codeName, homeDir) {
|
|
3956
|
+
const home = homeDir ?? (process.env["HOME"]?.trim() || homedir8());
|
|
3957
|
+
const key = agentRuntimeKey(codeName, homeDir);
|
|
3958
|
+
return join16(home, ".augmented", key, "tool-call-cursors.json");
|
|
3959
|
+
}
|
|
3960
|
+
function loadCursors(path) {
|
|
3961
|
+
const out = /* @__PURE__ */ new Map();
|
|
3962
|
+
if (!existsSync4(path)) return out;
|
|
3963
|
+
try {
|
|
3964
|
+
const parsed = JSON.parse(readFileSync14(path, "utf-8"));
|
|
3965
|
+
if (!parsed || parsed.version !== 1 || typeof parsed.files !== "object") return out;
|
|
3966
|
+
for (const [k, v] of Object.entries(parsed.files)) {
|
|
3967
|
+
if (!v || typeof v !== "object") continue;
|
|
3968
|
+
const offsetsOk = ["byteOffset", "lineOffset", "startedFromLine", "callsExtracted"].every((f) => {
|
|
3969
|
+
const n = v[f];
|
|
3970
|
+
return n === void 0 || typeof n === "number" && Number.isSafeInteger(n) && n >= 0;
|
|
3971
|
+
});
|
|
3972
|
+
if (typeof v.byteOffset !== "number" || typeof v.lineOffset !== "number" || !offsetsOk) continue;
|
|
3973
|
+
out.set(k, {
|
|
3974
|
+
byteOffset: v.byteOffset,
|
|
3975
|
+
lineOffset: v.lineOffset,
|
|
3976
|
+
startedFromLine: typeof v.startedFromLine === "number" ? v.startedFromLine : 0,
|
|
3977
|
+
callsExtracted: typeof v.callsExtracted === "number" ? v.callsExtracted : 0,
|
|
3978
|
+
unpairedSince: typeof v.unpairedSince === "string" ? v.unpairedSince : null,
|
|
3979
|
+
lastDisposition: COVERAGE_DISPOSITIONS.includes(v.lastDisposition) ? v.lastDisposition : "ok",
|
|
3980
|
+
lastScanAt: typeof v.lastScanAt === "string" ? v.lastScanAt : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3981
|
+
firstScanAt: typeof v.firstScanAt === "string" ? v.firstScanAt : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3982
|
+
transcriptRelPath: typeof v.transcriptRelPath === "string" ? v.transcriptRelPath : null
|
|
3983
|
+
});
|
|
3984
|
+
}
|
|
3985
|
+
} catch {
|
|
3986
|
+
}
|
|
3987
|
+
return out;
|
|
3988
|
+
}
|
|
3989
|
+
function saveCursors(path, cursors) {
|
|
3990
|
+
const files = {};
|
|
3991
|
+
for (const [k, v] of cursors) files[k] = v;
|
|
3992
|
+
atomicWriteFileSync(path, JSON.stringify({ version: 1, files }, null, 2));
|
|
3993
|
+
}
|
|
3994
|
+
function nextCursor(args) {
|
|
3995
|
+
const { previous, endByte, endLine, earliestUnpaired, outcome, nowMs } = args;
|
|
3996
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
3997
|
+
const disposition = dispositionFor(outcome);
|
|
3998
|
+
if (!mayAdvance(outcome)) {
|
|
3999
|
+
return { ...previous, lastDisposition: disposition, lastScanAt: nowIso };
|
|
4000
|
+
}
|
|
4001
|
+
const callsExtracted = previous.callsExtracted + args.callsExtracted;
|
|
4002
|
+
if (!earliestUnpaired) {
|
|
4003
|
+
return {
|
|
4004
|
+
...previous,
|
|
4005
|
+
byteOffset: endByte,
|
|
4006
|
+
lineOffset: endLine,
|
|
4007
|
+
callsExtracted,
|
|
4008
|
+
unpairedSince: null,
|
|
4009
|
+
lastDisposition: disposition,
|
|
4010
|
+
lastScanAt: nowIso
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
const since = previous.unpairedSince ?? nowIso;
|
|
4014
|
+
const waited = nowMs - Date.parse(since);
|
|
4015
|
+
if (!Number.isFinite(waited) || waited >= UNPAIRED_GRACE_MS) {
|
|
4016
|
+
return {
|
|
4017
|
+
...previous,
|
|
4018
|
+
byteOffset: endByte,
|
|
4019
|
+
lineOffset: endLine,
|
|
4020
|
+
callsExtracted,
|
|
4021
|
+
unpairedSince: null,
|
|
4022
|
+
lastDisposition: disposition,
|
|
4023
|
+
lastScanAt: nowIso
|
|
4024
|
+
};
|
|
4025
|
+
}
|
|
4026
|
+
return {
|
|
4027
|
+
...previous,
|
|
4028
|
+
byteOffset: Math.max(previous.byteOffset, Math.min(endByte, earliestUnpaired.byteStart)),
|
|
4029
|
+
lineOffset: Math.max(previous.lineOffset, Math.min(endLine, earliestUnpaired.lineOffset)),
|
|
4030
|
+
callsExtracted,
|
|
4031
|
+
unpairedSince: since,
|
|
4032
|
+
lastDisposition: disposition,
|
|
4033
|
+
lastScanAt: nowIso
|
|
4034
|
+
};
|
|
4035
|
+
}
|
|
4036
|
+
function initialCursor(args) {
|
|
4037
|
+
const iso = new Date(args.nowMs).toISOString();
|
|
4038
|
+
return {
|
|
4039
|
+
byteOffset: args.startByte,
|
|
4040
|
+
lineOffset: args.startLine,
|
|
4041
|
+
startedFromLine: args.startLine,
|
|
4042
|
+
callsExtracted: 0,
|
|
4043
|
+
unpairedSince: null,
|
|
4044
|
+
lastDisposition: "ok",
|
|
4045
|
+
lastScanAt: iso,
|
|
4046
|
+
firstScanAt: iso,
|
|
4047
|
+
transcriptRelPath: args.transcriptRelPath
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
function coverageRowFor(key, cursor, versions) {
|
|
4051
|
+
const { sessionId, transcriptRef } = parseCursorKey(key);
|
|
4052
|
+
return {
|
|
4053
|
+
session_id: sessionId,
|
|
4054
|
+
transcript_ref: transcriptRef,
|
|
4055
|
+
transcript_rel_path: cursor.transcriptRelPath,
|
|
4056
|
+
through_line: cursor.lineOffset,
|
|
4057
|
+
through_byte: cursor.byteOffset,
|
|
4058
|
+
started_from_line: cursor.startedFromLine,
|
|
4059
|
+
calls_extracted: cursor.callsExtracted,
|
|
4060
|
+
last_disposition: cursor.lastDisposition,
|
|
4061
|
+
extractor_version: versions.extractor,
|
|
4062
|
+
redaction_rule_version: versions.redaction
|
|
4063
|
+
};
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
// src/lib/tool-call-scan.ts
|
|
4067
|
+
var MAX_CALLS_PER_POST = 500;
|
|
4068
|
+
var MAX_COVERAGE_ROWS_PER_POST = 200;
|
|
4069
|
+
function classifyIngestResult(result, error) {
|
|
4070
|
+
if (error) {
|
|
4071
|
+
if (error instanceof ApiError) {
|
|
4072
|
+
if (error.status === 404) return { kind: "agent_unbound" };
|
|
4073
|
+
if (error.status === 400 && error.body?.["code"] === "batch_too_large") {
|
|
4074
|
+
return { kind: "ingest_failed", reason: `oversize batch (HTTP 400): ${error.message}` };
|
|
4075
|
+
}
|
|
4076
|
+
if (error.status >= 400 && error.status < 500) {
|
|
4077
|
+
return { kind: "batch_rejected", reason: `HTTP ${error.status}: ${error.message}` };
|
|
4078
|
+
}
|
|
4079
|
+
return { kind: "ingest_failed", reason: `HTTP ${error.status}: ${error.message}` };
|
|
4080
|
+
}
|
|
4081
|
+
return { kind: "ingest_failed", reason: String(error?.message ?? error) };
|
|
4082
|
+
}
|
|
4083
|
+
const body = result ?? {};
|
|
4084
|
+
if (body["entitled"] === false) return { kind: "not_entitled" };
|
|
4085
|
+
if (body["ok"] === false) {
|
|
4086
|
+
return { kind: "ingest_failed", reason: String(body["message"] ?? "ok:false") };
|
|
4087
|
+
}
|
|
4088
|
+
return { kind: "ingested" };
|
|
4089
|
+
}
|
|
4090
|
+
function chunk(items, size) {
|
|
4091
|
+
const out = [];
|
|
4092
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
4093
|
+
return out;
|
|
4094
|
+
}
|
|
4095
|
+
function endOfFile(absPath) {
|
|
4096
|
+
try {
|
|
4097
|
+
return { byteOffset: statSync4(absPath).size, lineOffset: 0 };
|
|
4098
|
+
} catch {
|
|
4099
|
+
return null;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
async function scanAgentToolCalls(args) {
|
|
4103
|
+
const now = args.now ?? Date.now;
|
|
4104
|
+
const log2 = args.log;
|
|
4105
|
+
const summary = {
|
|
4106
|
+
filesScanned: 0,
|
|
4107
|
+
callsExtracted: 0,
|
|
4108
|
+
callsIngested: 0,
|
|
4109
|
+
filesHeld: 0,
|
|
4110
|
+
coverageReported: 0,
|
|
4111
|
+
notEntitled: false
|
|
4112
|
+
};
|
|
4113
|
+
const path = args.cursorPath ?? cursorStatePath(args.codeName, args.homeDir);
|
|
4114
|
+
const cursors = loadCursors(path);
|
|
4115
|
+
const extractOpts = {
|
|
4116
|
+
transcriptDir: args.transcriptDir,
|
|
4117
|
+
archive: readHostArchiveAddress(),
|
|
4118
|
+
redaction: { pathHashSalt: args.pathHashSalt, hashOnly: args.hashOnly }
|
|
4119
|
+
};
|
|
4120
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4121
|
+
const files = [];
|
|
4122
|
+
for (const sessionId of args.sessionIds) {
|
|
4123
|
+
for (const f of enumerateSessionTranscripts(args.transcriptDir, sessionId, args.projectsRoot)) {
|
|
4124
|
+
const key = cursorKey(sessionId, f.ref);
|
|
4125
|
+
if (seen.has(key)) continue;
|
|
4126
|
+
seen.add(key);
|
|
4127
|
+
files.push({ key, file: f });
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
const touched = [];
|
|
4131
|
+
for (const { key, file } of files) {
|
|
4132
|
+
let cursor = cursors.get(key);
|
|
4133
|
+
if (!cursor) {
|
|
4134
|
+
const end = endOfFile(file.absPath);
|
|
4135
|
+
if (!end) continue;
|
|
4136
|
+
cursor = initialCursor({
|
|
4137
|
+
startByte: args.backfill ? 0 : end.byteOffset,
|
|
4138
|
+
startLine: 0,
|
|
4139
|
+
transcriptRelPath: file.relPath,
|
|
4140
|
+
nowMs: now()
|
|
4141
|
+
});
|
|
4142
|
+
cursors.set(key, cursor);
|
|
4143
|
+
touched.push(key);
|
|
4144
|
+
if (!args.backfill && end.byteOffset > 0) {
|
|
4145
|
+
log2(
|
|
4146
|
+
`[tool-call-scan] first-seen ${file.relPath} ref=${file.ref} \u2014 starting at byte ${end.byteOffset} (scan-from-now; set backfill to include the existing ${end.byteOffset}B)`
|
|
4147
|
+
);
|
|
4148
|
+
}
|
|
4149
|
+
if (!args.backfill) continue;
|
|
4150
|
+
}
|
|
4151
|
+
const window = extractTranscriptWindow(file, extractOpts, {
|
|
4152
|
+
byteOffset: cursor.byteOffset,
|
|
4153
|
+
lineOffset: cursor.lineOffset
|
|
4154
|
+
});
|
|
4155
|
+
summary.filesScanned += 1;
|
|
4156
|
+
if (window.missing) {
|
|
4157
|
+
cursors.set(key, { ...cursor, lastDisposition: "transcript_missing", lastScanAt: new Date(now()).toISOString() });
|
|
4158
|
+
touched.push(key);
|
|
4159
|
+
continue;
|
|
4160
|
+
}
|
|
4161
|
+
if (window.rewound) {
|
|
4162
|
+
log2(`[tool-call-scan] ${file.relPath} ref=${file.ref} shrank below the cursor \u2014 re-reading from 0`);
|
|
4163
|
+
}
|
|
4164
|
+
summary.callsExtracted += window.records.length;
|
|
4165
|
+
const unpairedIds = new Set(window.unpaired.map((u) => u.toolUseId));
|
|
4166
|
+
const sendable = window.records.filter((r) => !unpairedIds.has(r.tool_use_id));
|
|
4167
|
+
let outcome = { kind: "ingested" };
|
|
4168
|
+
let attempted = false;
|
|
4169
|
+
let ingested = 0;
|
|
4170
|
+
for (const batch of chunk(sendable, MAX_CALLS_PER_POST)) {
|
|
4171
|
+
attempted = true;
|
|
4172
|
+
const res = await postCalls(args.api, args.agentId, batch);
|
|
4173
|
+
outcome = res.outcome;
|
|
4174
|
+
ingested += res.ingested;
|
|
4175
|
+
if (outcome.kind === "not_entitled") summary.notEntitled = true;
|
|
4176
|
+
if (outcome.kind !== "ingested" && outcome.kind !== "not_entitled") break;
|
|
4177
|
+
}
|
|
4178
|
+
summary.callsIngested += ingested;
|
|
4179
|
+
const computed = nextCursor({
|
|
4180
|
+
previous: cursor,
|
|
4181
|
+
endByte: window.endByte,
|
|
4182
|
+
endLine: window.endLine,
|
|
4183
|
+
earliestUnpaired: window.unpaired[0] ?? null,
|
|
4184
|
+
outcome,
|
|
4185
|
+
callsExtracted: sendable.length,
|
|
4186
|
+
nowMs: now()
|
|
4187
|
+
});
|
|
4188
|
+
const advanced = attempted ? computed : { ...computed, lastDisposition: cursor.lastDisposition };
|
|
4189
|
+
if (advanced.byteOffset === cursor.byteOffset && outcome.kind === "ingest_failed") {
|
|
4190
|
+
summary.filesHeld += 1;
|
|
4191
|
+
log2(
|
|
4192
|
+
`[tool-call-scan] HOLDING cursor for ${file.relPath} ref=${file.ref} at byte ${cursor.byteOffset}: ${outcome.reason}`
|
|
4193
|
+
);
|
|
4194
|
+
}
|
|
4195
|
+
cursors.set(key, advanced);
|
|
4196
|
+
touched.push(key);
|
|
4197
|
+
if (outcome.kind === "agent_unbound") {
|
|
4198
|
+
log2(`[tool-call-scan] host is not bound to agent ${args.agentId} \u2014 stopping this tick`);
|
|
4199
|
+
break;
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
saveCursors(path, cursors);
|
|
4203
|
+
summary.coverageReported = await reportCoverage(args, cursors, touched, log2);
|
|
4204
|
+
return summary;
|
|
4205
|
+
}
|
|
4206
|
+
async function postCalls(api2, agentId, calls) {
|
|
4207
|
+
try {
|
|
4208
|
+
const res = await api2.post(
|
|
4209
|
+
"/host/tool-calls",
|
|
4210
|
+
{ agent_id: agentId, calls }
|
|
4211
|
+
);
|
|
4212
|
+
const outcome = classifyIngestResult(res);
|
|
4213
|
+
return { outcome, ingested: typeof res?.ingested === "number" ? res.ingested : 0 };
|
|
4214
|
+
} catch (err) {
|
|
4215
|
+
return { outcome: classifyIngestResult(void 0, err), ingested: 0 };
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
async function reportCoverage(args, cursors, touched, log2) {
|
|
4219
|
+
const rows = [];
|
|
4220
|
+
for (const key of new Set(touched)) {
|
|
4221
|
+
const c = cursors.get(key);
|
|
4222
|
+
if (c) rows.push(coverageRowFor(key, c, { extractor: EXTRACTOR_VERSION, redaction: REDACTION_RULE_VERSION }));
|
|
4223
|
+
}
|
|
4224
|
+
if (rows.length === 0) return 0;
|
|
4225
|
+
let reported = 0;
|
|
4226
|
+
for (const batch of chunk(rows, MAX_COVERAGE_ROWS_PER_POST)) {
|
|
4227
|
+
try {
|
|
4228
|
+
await args.api.post("/host/tool-calls/coverage", { agent_id: args.agentId, coverage: batch });
|
|
4229
|
+
reported += batch.length;
|
|
4230
|
+
} catch (err) {
|
|
4231
|
+
log2(`[tool-call-scan] coverage report failed (${batch.length} rows): ${String(err)}`);
|
|
4232
|
+
break;
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
return reported;
|
|
4236
|
+
}
|
|
4237
|
+
|
|
4238
|
+
// src/lib/tool-call-audit.ts
|
|
4239
|
+
var MIN_CHECK_INTERVAL_MS6 = 10 * 6e4;
|
|
4240
|
+
var ENTITLEMENT_RECHECK_MS = 30 * 6e4;
|
|
4241
|
+
var state5 = /* @__PURE__ */ new Map();
|
|
4242
|
+
function entitlementLatch(entry, nowMs) {
|
|
4243
|
+
if (!entry?.notEntitledAt) return false;
|
|
4244
|
+
return !hasElapsed(entry.notEntitledAt, nowMs, ENTITLEMENT_RECHECK_MS);
|
|
4245
|
+
}
|
|
4246
|
+
function hasElapsed(sinceMs, nowMs, windowMs) {
|
|
4247
|
+
const delta = nowMs - sinceMs;
|
|
4248
|
+
if (!Number.isFinite(delta) || delta < 0) return true;
|
|
4249
|
+
return delta >= windowMs;
|
|
4250
|
+
}
|
|
4251
|
+
async function maybeScanToolCalls(args) {
|
|
4252
|
+
const { api: api2, codeName, agentId, log: log2 } = args;
|
|
4253
|
+
const nowFn = args.now ?? Date.now;
|
|
4254
|
+
const nowMs = nowFn();
|
|
4255
|
+
try {
|
|
4256
|
+
const existing = state5.get(codeName);
|
|
4257
|
+
if (existing && !hasElapsed(existing.lastCheckedAt, nowMs, MIN_CHECK_INTERVAL_MS6)) return;
|
|
4258
|
+
if (entitlementLatch(existing, nowMs)) {
|
|
4259
|
+
state5.set(codeName, { lastCheckedAt: nowMs, notEntitledAt: existing?.notEntitledAt ?? null });
|
|
4260
|
+
return;
|
|
4261
|
+
}
|
|
4262
|
+
state5.set(codeName, { lastCheckedAt: nowMs, notEntitledAt: existing?.notEntitledAt ?? null });
|
|
4263
|
+
const salt = readToolCallPathSalt(codeName, args.homeDir);
|
|
4264
|
+
if (!salt) {
|
|
4265
|
+
log2(`[tool-call-audit] ${codeName}: no path-hash salt available \u2014 file targets withheld`);
|
|
4266
|
+
}
|
|
4267
|
+
const home = args.homeDir ?? (process.env["HOME"]?.trim() || homedir9());
|
|
4268
|
+
const projectsRoot = args.projectsRoot ?? join17(home, ".claude", "projects");
|
|
4269
|
+
const transcriptDir = args.transcriptDir ?? sessionTranscriptDir(getProjectDir(codeName));
|
|
4270
|
+
const current = peekCurrentSession(codeName);
|
|
4271
|
+
const sessionIds = current ? [current.sessionId] : [];
|
|
4272
|
+
const summary = await scanAgentToolCalls({
|
|
4273
|
+
api: api2,
|
|
4274
|
+
agentId,
|
|
4275
|
+
codeName,
|
|
4276
|
+
transcriptDir,
|
|
4277
|
+
projectsRoot,
|
|
4278
|
+
sessionIds,
|
|
4279
|
+
pathHashSalt: salt ?? "",
|
|
4280
|
+
...args.hashOnly === void 0 ? {} : { hashOnly: args.hashOnly },
|
|
4281
|
+
log: log2,
|
|
4282
|
+
now: nowFn,
|
|
4283
|
+
...args.homeDir === void 0 ? {} : { homeDir: args.homeDir },
|
|
4284
|
+
...args.cursorPath === void 0 ? {} : { cursorPath: args.cursorPath }
|
|
4285
|
+
});
|
|
4286
|
+
if (summary.notEntitled) {
|
|
4287
|
+
state5.set(codeName, { lastCheckedAt: nowMs, notEntitledAt: nowMs });
|
|
4288
|
+
} else if (summary.callsIngested > 0) {
|
|
4289
|
+
state5.set(codeName, { lastCheckedAt: nowMs, notEntitledAt: null });
|
|
4290
|
+
}
|
|
4291
|
+
} catch (err) {
|
|
4292
|
+
log2(`[tool-call-audit] ${codeName}: scan failed: ${err.message}`);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
|
|
4296
|
+
// src/lib/activity-cache-monitor.ts
|
|
4297
|
+
import { existsSync as existsSync5, readFileSync as readFileSync15 } from "fs";
|
|
4298
|
+
import { homedir as homedir10 } from "os";
|
|
4299
|
+
import { join as join18 } from "path";
|
|
4300
|
+
var MIN_CHECK_INTERVAL_MS7 = 6e4;
|
|
4301
|
+
var STATS_CACHE_PATH = join18(homedir10(), ".claude", "stats-cache.json");
|
|
3512
4302
|
var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
3513
|
-
var
|
|
4303
|
+
var state6 = { lastObservedDate: null, lastCheckedAt: 0 };
|
|
3514
4304
|
function selectNewDailyRows(raw, lastObservedDate) {
|
|
3515
4305
|
let parsed;
|
|
3516
4306
|
try {
|
|
@@ -3549,24 +4339,24 @@ async function maybeReportActivityCache(args) {
|
|
|
3549
4339
|
const { api: api2, log: log2 } = args;
|
|
3550
4340
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
3551
4341
|
const nowMs = now.getTime();
|
|
3552
|
-
if (nowMs -
|
|
3553
|
-
|
|
3554
|
-
if (!
|
|
4342
|
+
if (nowMs - state6.lastCheckedAt < MIN_CHECK_INTERVAL_MS7) return;
|
|
4343
|
+
state6.lastCheckedAt = nowMs;
|
|
4344
|
+
if (!existsSync5(STATS_CACHE_PATH)) {
|
|
3555
4345
|
return;
|
|
3556
4346
|
}
|
|
3557
4347
|
let raw;
|
|
3558
4348
|
try {
|
|
3559
|
-
raw =
|
|
4349
|
+
raw = readFileSync15(STATS_CACHE_PATH, "utf-8");
|
|
3560
4350
|
} catch (err) {
|
|
3561
4351
|
log2(`[activity-cache] readFileSync failed: ${err.message}`);
|
|
3562
4352
|
return;
|
|
3563
4353
|
}
|
|
3564
|
-
const rows = selectNewDailyRows(raw,
|
|
4354
|
+
const rows = selectNewDailyRows(raw, state6.lastObservedDate);
|
|
3565
4355
|
if (rows.length === 0) return;
|
|
3566
4356
|
for (const row of rows) {
|
|
3567
4357
|
try {
|
|
3568
4358
|
await api2.post("/host/activity-observations", row);
|
|
3569
|
-
|
|
4359
|
+
state6.lastObservedDate = row.date;
|
|
3570
4360
|
} catch (err) {
|
|
3571
4361
|
log2(
|
|
3572
4362
|
`[activity-cache] POST /host/activity-observations failed for date=${row.date}: ${err.message}`
|
|
@@ -3741,7 +4531,7 @@ function bundleFingerprint(files) {
|
|
|
3741
4531
|
}
|
|
3742
4532
|
|
|
3743
4533
|
// src/lib/channel-config-hash.ts
|
|
3744
|
-
import { createHash as
|
|
4534
|
+
import { createHash as createHash8 } from "crypto";
|
|
3745
4535
|
var DERIVED_PRINCIPAL_ID_LIST_KEYS = [
|
|
3746
4536
|
"allowed_users",
|
|
3747
4537
|
"diagnostic_chat_ids",
|
|
@@ -3762,7 +4552,7 @@ function normalizeChannelConfigForHash(config2) {
|
|
|
3762
4552
|
}
|
|
3763
4553
|
var CHANNEL_WRITE_VERSION = 9;
|
|
3764
4554
|
function computeChannelConfigHash(input) {
|
|
3765
|
-
return
|
|
4555
|
+
return createHash8("sha256").update(
|
|
3766
4556
|
canonicalJson({
|
|
3767
4557
|
writeVersion: CHANNEL_WRITE_VERSION,
|
|
3768
4558
|
cliVersion: input.cliVersion,
|
|
@@ -3781,18 +4571,18 @@ function computeChannelConfigHash(input) {
|
|
|
3781
4571
|
}
|
|
3782
4572
|
|
|
3783
4573
|
// src/lib/channel-hash-cache.ts
|
|
3784
|
-
import { existsSync as
|
|
3785
|
-
import { join as
|
|
4574
|
+
import { existsSync as existsSync6, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
4575
|
+
import { join as join19 } from "path";
|
|
3786
4576
|
var CACHE_FILENAME = "channel-hash-cache.json";
|
|
3787
4577
|
function getChannelHashCacheFile(configDir) {
|
|
3788
|
-
return
|
|
4578
|
+
return join19(configDir, CACHE_FILENAME);
|
|
3789
4579
|
}
|
|
3790
4580
|
function loadChannelHashCache(target, configDir) {
|
|
3791
4581
|
const path = getChannelHashCacheFile(configDir);
|
|
3792
|
-
if (!
|
|
4582
|
+
if (!existsSync6(path)) return;
|
|
3793
4583
|
let parsed;
|
|
3794
4584
|
try {
|
|
3795
|
-
parsed = JSON.parse(
|
|
4585
|
+
parsed = JSON.parse(readFileSync16(path, "utf-8"));
|
|
3796
4586
|
} catch {
|
|
3797
4587
|
return;
|
|
3798
4588
|
}
|
|
@@ -3806,14 +4596,14 @@ function saveChannelHashCache(source, configDir) {
|
|
|
3806
4596
|
const obj = {};
|
|
3807
4597
|
for (const [key, value] of source) obj[key] = value;
|
|
3808
4598
|
try {
|
|
3809
|
-
|
|
4599
|
+
writeFileSync7(path, JSON.stringify(obj, null, 2));
|
|
3810
4600
|
} catch {
|
|
3811
4601
|
}
|
|
3812
4602
|
}
|
|
3813
4603
|
|
|
3814
4604
|
// src/lib/sender-policy-baseline.ts
|
|
3815
|
-
import { existsSync as
|
|
3816
|
-
import { join as
|
|
4605
|
+
import { existsSync as existsSync7, readFileSync as readFileSync17 } from "fs";
|
|
4606
|
+
import { join as join20 } from "path";
|
|
3817
4607
|
var BASELINE_FILENAME = "sender-policy-baseline.json";
|
|
3818
4608
|
var SENDER_POLICY_BASELINE_VERSION = 1;
|
|
3819
4609
|
var BASELINE_CONCERNS = ["senderPolicy", "slackBehaviour", "msteamsBehaviour"];
|
|
@@ -3825,14 +4615,14 @@ function createDeliveryBaselineMaps() {
|
|
|
3825
4615
|
};
|
|
3826
4616
|
}
|
|
3827
4617
|
function getSenderPolicyBaselineFile(configDir) {
|
|
3828
|
-
return
|
|
4618
|
+
return join20(configDir, BASELINE_FILENAME);
|
|
3829
4619
|
}
|
|
3830
4620
|
function loadSenderPolicyBaseline(target, configDir, log2) {
|
|
3831
4621
|
const path = getSenderPolicyBaselineFile(configDir);
|
|
3832
|
-
if (!
|
|
4622
|
+
if (!existsSync7(path)) return;
|
|
3833
4623
|
let parsed;
|
|
3834
4624
|
try {
|
|
3835
|
-
parsed = JSON.parse(
|
|
4625
|
+
parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
3836
4626
|
} catch (err) {
|
|
3837
4627
|
log2?.(
|
|
3838
4628
|
`[sender-policy] discarding corrupt ${BASELINE_FILENAME} (${err.message}) - restrictive-policy agents will take one fail-closed restart`
|
|
@@ -4355,37 +5145,37 @@ function planGlobalSkillSync(globalSkills, prevIds, hashOf, knownHash, options)
|
|
|
4355
5145
|
}
|
|
4356
5146
|
|
|
4357
5147
|
// src/lib/manager/integration-skill-cache.ts
|
|
4358
|
-
import { join as
|
|
5148
|
+
import { join as join21 } from "path";
|
|
4359
5149
|
function integrationSkillHashKey(agentId, skillId) {
|
|
4360
5150
|
return `plugin-skill:${agentId}:${skillId}`;
|
|
4361
5151
|
}
|
|
4362
|
-
function shouldWriteIntegrationSkill(
|
|
4363
|
-
return
|
|
5152
|
+
function shouldWriteIntegrationSkill(cache3, agentId, skillId, contentHash) {
|
|
5153
|
+
return cache3.get(integrationSkillHashKey(agentId, skillId)) !== contentHash;
|
|
4364
5154
|
}
|
|
4365
|
-
function rememberIntegrationSkill(
|
|
4366
|
-
|
|
5155
|
+
function rememberIntegrationSkill(cache3, agentId, skillId, contentHash) {
|
|
5156
|
+
cache3.set(integrationSkillHashKey(agentId, skillId), contentHash);
|
|
4367
5157
|
}
|
|
4368
|
-
function forgetIntegrationSkill(
|
|
4369
|
-
|
|
5158
|
+
function forgetIntegrationSkill(cache3, agentId, skillId) {
|
|
5159
|
+
cache3.delete(integrationSkillHashKey(agentId, skillId));
|
|
4370
5160
|
}
|
|
4371
5161
|
function removeIntegrationSkillFolder(opts) {
|
|
4372
5162
|
forgetIntegrationSkill(opts.cache, opts.agentId, opts.entry);
|
|
4373
5163
|
for (const dir of opts.dirs) {
|
|
4374
|
-
opts.removeDir(
|
|
5164
|
+
opts.removeDir(join21(dir, opts.entry));
|
|
4375
5165
|
}
|
|
4376
5166
|
}
|
|
4377
5167
|
|
|
4378
5168
|
// src/lib/manager/managed-skill-manifest.ts
|
|
4379
|
-
import { existsSync as
|
|
4380
|
-
import { dirname as
|
|
5169
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync18, writeFileSync as writeFileSync8 } from "fs";
|
|
5170
|
+
import { dirname as dirname6, join as join22 } from "path";
|
|
4381
5171
|
var MANIFEST_VERSION = 1;
|
|
4382
5172
|
function managedSkillManifestPath(agentRootDir) {
|
|
4383
|
-
return
|
|
5173
|
+
return join22(agentRootDir, "managed-skills.json");
|
|
4384
5174
|
}
|
|
4385
5175
|
function readManagedSkillManifest(path) {
|
|
4386
5176
|
try {
|
|
4387
|
-
if (!
|
|
4388
|
-
const parsed = JSON.parse(
|
|
5177
|
+
if (!existsSync8(path)) return /* @__PURE__ */ new Set();
|
|
5178
|
+
const parsed = JSON.parse(readFileSync18(path, "utf-8"));
|
|
4389
5179
|
const ids = Array.isArray(parsed?.globalSkillIds) ? parsed.globalSkillIds : [];
|
|
4390
5180
|
return new Set(ids.filter((id) => typeof id === "string" && id.length > 0));
|
|
4391
5181
|
} catch {
|
|
@@ -4394,12 +5184,12 @@ function readManagedSkillManifest(path) {
|
|
|
4394
5184
|
}
|
|
4395
5185
|
function writeManagedSkillManifest(path, ids) {
|
|
4396
5186
|
try {
|
|
4397
|
-
|
|
5187
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
4398
5188
|
const body = {
|
|
4399
5189
|
version: MANIFEST_VERSION,
|
|
4400
5190
|
globalSkillIds: [...ids].sort()
|
|
4401
5191
|
};
|
|
4402
|
-
|
|
5192
|
+
writeFileSync8(path, JSON.stringify(body, null, 2));
|
|
4403
5193
|
} catch {
|
|
4404
5194
|
}
|
|
4405
5195
|
}
|
|
@@ -4497,9 +5287,9 @@ function resolveModelChain(refreshData) {
|
|
|
4497
5287
|
}
|
|
4498
5288
|
|
|
4499
5289
|
// src/lib/manager/claude-auth.ts
|
|
4500
|
-
import { existsSync as
|
|
4501
|
-
import { join as
|
|
4502
|
-
import { homedir as
|
|
5290
|
+
import { existsSync as existsSync9, rmSync as rmSync3 } from "fs";
|
|
5291
|
+
import { join as join23 } from "path";
|
|
5292
|
+
import { homedir as homedir11 } from "os";
|
|
4503
5293
|
async function applyClaudeAuthToEnv(childEnv, label) {
|
|
4504
5294
|
const apiKey = getApiKey();
|
|
4505
5295
|
if (!apiKey) {
|
|
@@ -4511,10 +5301,10 @@ async function applyClaudeAuthToEnv(childEnv, label) {
|
|
|
4511
5301
|
throw new Error("claude_auth_mode=api_key but /host/exchange returned no decrypted key");
|
|
4512
5302
|
}
|
|
4513
5303
|
childEnv.ANTHROPIC_API_KEY = exchange.anthropicApiKey;
|
|
4514
|
-
const claudeDir =
|
|
5304
|
+
const claudeDir = join23(homedir11(), ".claude");
|
|
4515
5305
|
for (const filename of [".credentials.json", "credentials.json"]) {
|
|
4516
|
-
const p =
|
|
4517
|
-
if (
|
|
5306
|
+
const p = join23(claudeDir, filename);
|
|
5307
|
+
if (existsSync9(p)) {
|
|
4518
5308
|
try {
|
|
4519
5309
|
rmSync3(p, { force: true });
|
|
4520
5310
|
log(`[${label}] Removed ${p} (api_key mode \u2014 preventing OAuth fallback)`);
|
|
@@ -4595,8 +5385,8 @@ function heartbeatRuntimeAuthFields(probeVerdict) {
|
|
|
4595
5385
|
}
|
|
4596
5386
|
|
|
4597
5387
|
// src/lib/manager/kanban/parsers.ts
|
|
4598
|
-
import { existsSync as
|
|
4599
|
-
import { join as
|
|
5388
|
+
import { existsSync as existsSync10, readFileSync as readFileSync19 } from "fs";
|
|
5389
|
+
import { join as join24 } from "path";
|
|
4600
5390
|
var STANDUP_TEMPLATES = /* @__PURE__ */ new Set(["daily-standup", "end-of-day-summary"]);
|
|
4601
5391
|
var TASK_UPDATE_TEMPLATES = /* @__PURE__ */ new Set(["hourly-status", "task-update"]);
|
|
4602
5392
|
var PLAN_TEMPLATES = /* @__PURE__ */ new Set(["morning-plan"]);
|
|
@@ -4632,21 +5422,21 @@ function nudgeIntervalForCount(nudgeCount, fullCadenceMs, maxCadenceMs) {
|
|
|
4632
5422
|
const exponent = Math.max(0, Math.min(nudgeCount - 1, 8));
|
|
4633
5423
|
return Math.min(fullCadenceMs * 3 ** exponent, Math.max(maxCadenceMs, fullCadenceMs));
|
|
4634
5424
|
}
|
|
4635
|
-
function shouldNudgeUnchangedBoard(signature,
|
|
4636
|
-
if (!
|
|
4637
|
-
if (
|
|
4638
|
-
return now -
|
|
5425
|
+
function shouldNudgeUnchangedBoard(signature, state8, now, fullCadenceMs, maxCadenceMs = fullCadenceMs) {
|
|
5426
|
+
if (!state8) return true;
|
|
5427
|
+
if (state8.signature !== signature) return true;
|
|
5428
|
+
return now - state8.nudgedAt >= nudgeIntervalForCount(state8.nudgeCount ?? 1, fullCadenceMs, maxCadenceMs);
|
|
4639
5429
|
}
|
|
4640
5430
|
function failureRetryIntervalForCount(failureCount, baseMs, maxMs) {
|
|
4641
5431
|
const exponent = Math.max(0, Math.min(failureCount - 1, 8));
|
|
4642
5432
|
return Math.min(baseMs * 3 ** exponent, Math.max(maxMs, baseMs));
|
|
4643
5433
|
}
|
|
4644
5434
|
var KANBAN_NOTICE_BREAKER_THRESHOLD = 5;
|
|
4645
|
-
function shouldAttemptNoticeEnqueue(
|
|
4646
|
-
const failures =
|
|
5435
|
+
function shouldAttemptNoticeEnqueue(state8, now, baseMs, maxMs, breakerThreshold = KANBAN_NOTICE_BREAKER_THRESHOLD) {
|
|
5436
|
+
const failures = state8?.failureCount ?? 0;
|
|
4647
5437
|
if (failures <= 0) return { attempt: true, breakerOpen: false };
|
|
4648
5438
|
const breakerOpen = failures >= breakerThreshold;
|
|
4649
|
-
const failedAt =
|
|
5439
|
+
const failedAt = state8?.failedAt;
|
|
4650
5440
|
const due = failedAt === void 0 || failedAt > now || now - failedAt >= failureRetryIntervalForCount(failures, baseMs, maxMs);
|
|
4651
5441
|
return { attempt: due, breakerOpen };
|
|
4652
5442
|
}
|
|
@@ -4748,12 +5538,12 @@ function getBuiltInSkillContent(skillId) {
|
|
|
4748
5538
|
if (builtInSkillCache.has(skillId)) return builtInSkillCache.get(skillId);
|
|
4749
5539
|
try {
|
|
4750
5540
|
const candidates = [
|
|
4751
|
-
|
|
4752
|
-
|
|
5541
|
+
join24(process.cwd(), "skills", skillId, "SKILL.md"),
|
|
5542
|
+
join24(new URL(".", import.meta.url).pathname, "..", "..", "..", "..", "..", "..", "skills", skillId, "SKILL.md")
|
|
4753
5543
|
];
|
|
4754
5544
|
for (const candidate of candidates) {
|
|
4755
|
-
if (
|
|
4756
|
-
const content =
|
|
5545
|
+
if (existsSync10(candidate)) {
|
|
5546
|
+
const content = readFileSync19(candidate, "utf-8");
|
|
4757
5547
|
const files = [{ relativePath: "SKILL.md", content }];
|
|
4758
5548
|
builtInSkillCache.set(skillId, files);
|
|
4759
5549
|
return files;
|
|
@@ -4894,19 +5684,19 @@ function formatBoardForPrompt(items, template) {
|
|
|
4894
5684
|
}
|
|
4895
5685
|
|
|
4896
5686
|
// src/lib/manager/kanban/nudge-state-cache.ts
|
|
4897
|
-
import { existsSync as
|
|
4898
|
-
import { join as
|
|
5687
|
+
import { existsSync as existsSync11, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
|
|
5688
|
+
import { join as join25 } from "path";
|
|
4899
5689
|
var CACHE_FILENAME2 = "kanban-nudge-state.json";
|
|
4900
5690
|
var KANBAN_NUDGE_STATE_VERSION = 1;
|
|
4901
5691
|
function getKanbanNudgeStateFile(configDir) {
|
|
4902
|
-
return
|
|
5692
|
+
return join25(configDir, CACHE_FILENAME2);
|
|
4903
5693
|
}
|
|
4904
5694
|
function loadKanbanNudgeState(target, configDir) {
|
|
4905
5695
|
const path = getKanbanNudgeStateFile(configDir);
|
|
4906
|
-
if (!
|
|
5696
|
+
if (!existsSync11(path)) return;
|
|
4907
5697
|
let parsed;
|
|
4908
5698
|
try {
|
|
4909
|
-
parsed = JSON.parse(
|
|
5699
|
+
parsed = JSON.parse(readFileSync20(path, "utf-8"));
|
|
4910
5700
|
} catch {
|
|
4911
5701
|
return;
|
|
4912
5702
|
}
|
|
@@ -4936,15 +5726,15 @@ function loadKanbanNudgeState(target, configDir) {
|
|
|
4936
5726
|
function saveKanbanNudgeState(source, configDir) {
|
|
4937
5727
|
const path = getKanbanNudgeStateFile(configDir);
|
|
4938
5728
|
const agents = {};
|
|
4939
|
-
for (const [codeName,
|
|
5729
|
+
for (const [codeName, state8] of source) agents[codeName] = state8;
|
|
4940
5730
|
try {
|
|
4941
|
-
|
|
5731
|
+
writeFileSync9(path, JSON.stringify({ version: KANBAN_NUDGE_STATE_VERSION, agents }, null, 2));
|
|
4942
5732
|
} catch {
|
|
4943
5733
|
}
|
|
4944
5734
|
}
|
|
4945
5735
|
|
|
4946
5736
|
// src/lib/manager/kanban/notify.ts
|
|
4947
|
-
import { createHash as
|
|
5737
|
+
import { createHash as createHash9 } from "crypto";
|
|
4948
5738
|
async function enqueueKanbanNotice(opts) {
|
|
4949
5739
|
try {
|
|
4950
5740
|
const res = await api.post(
|
|
@@ -4959,7 +5749,7 @@ async function enqueueKanbanNotice(opts) {
|
|
|
4959
5749
|
return res.ok === true;
|
|
4960
5750
|
} catch (err) {
|
|
4961
5751
|
const errText = err instanceof Error ? err.message : String(err);
|
|
4962
|
-
const errId =
|
|
5752
|
+
const errId = createHash9("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4963
5753
|
log(`[kanban] notice enqueue failed for agent_id=${opts.agentId} error_id=${errId}`);
|
|
4964
5754
|
return false;
|
|
4965
5755
|
}
|
|
@@ -4974,7 +5764,7 @@ async function cancelKanbanNotice(agentId) {
|
|
|
4974
5764
|
return typeof res.cancelled === "number" ? res.cancelled : 0;
|
|
4975
5765
|
} catch (err) {
|
|
4976
5766
|
const errText = err instanceof Error ? err.message : String(err);
|
|
4977
|
-
const errId =
|
|
5767
|
+
const errId = createHash9("sha256").update(errText).digest("hex").slice(0, 12);
|
|
4978
5768
|
log(`[kanban] notice cancel failed for agent_id=${agentId} error_id=${errId}`);
|
|
4979
5769
|
return null;
|
|
4980
5770
|
}
|
|
@@ -5380,7 +6170,7 @@ function deriveScheduledTaskNotify(task) {
|
|
|
5380
6170
|
}
|
|
5381
6171
|
|
|
5382
6172
|
// src/lib/manager/scheduler/runs.ts
|
|
5383
|
-
import { createHash as
|
|
6173
|
+
import { createHash as createHash10 } from "crypto";
|
|
5384
6174
|
async function startRun(opts) {
|
|
5385
6175
|
try {
|
|
5386
6176
|
const res = await api.post(
|
|
@@ -5395,7 +6185,7 @@ async function startRun(opts) {
|
|
|
5395
6185
|
};
|
|
5396
6186
|
} catch (err) {
|
|
5397
6187
|
const errText = err instanceof Error ? err.message : String(err);
|
|
5398
|
-
const errId =
|
|
6188
|
+
const errId = createHash10("sha256").update(errText).digest("hex").slice(0, 12);
|
|
5399
6189
|
log(`[runs] start failed for agent_id=${opts.agent_id} source_type=${opts.source_type} error_id=${errId}`);
|
|
5400
6190
|
return { run_id: null, kanban_item_id: null };
|
|
5401
6191
|
}
|
|
@@ -5434,7 +6224,7 @@ async function finishRun(runId, outcome, options = {}) {
|
|
|
5434
6224
|
return;
|
|
5435
6225
|
} catch (err) {
|
|
5436
6226
|
const errText = err instanceof Error ? err.message : String(err);
|
|
5437
|
-
const errId =
|
|
6227
|
+
const errId = createHash10("sha256").update(errText).digest("hex").slice(0, 12);
|
|
5438
6228
|
const status = err instanceof ApiError ? err.status : 0;
|
|
5439
6229
|
if (isRetryableFinishError(err) && attempt < maxRetries) {
|
|
5440
6230
|
log(
|
|
@@ -5463,7 +6253,7 @@ async function fetchPriorScheduledRuns(agentId, taskId) {
|
|
|
5463
6253
|
return rows.filter((r) => typeof r.output_text === "string" && r.output_text.length > 0).map((r) => ({ startedAt: r.started_at, output: r.output_text }));
|
|
5464
6254
|
} catch (err) {
|
|
5465
6255
|
const errText = err instanceof Error ? err.message : String(err);
|
|
5466
|
-
const errId =
|
|
6256
|
+
const errId = createHash10("sha256").update(errText).digest("hex").slice(0, 12);
|
|
5467
6257
|
log(`[runs] prior-runs lookup failed for task_id=${taskId} error_id=${errId}`);
|
|
5468
6258
|
return [];
|
|
5469
6259
|
}
|
|
@@ -5512,13 +6302,13 @@ function closeScheduledRunsForCode(codeName, outcome, reason) {
|
|
|
5512
6302
|
}
|
|
5513
6303
|
|
|
5514
6304
|
// src/lib/manager/scheduler/kanban-route.ts
|
|
5515
|
-
import { createHash as
|
|
5516
|
-
import { writeFileSync as
|
|
5517
|
-
import { homedir as
|
|
5518
|
-
import { join as
|
|
6305
|
+
import { createHash as createHash12 } from "crypto";
|
|
6306
|
+
import { writeFileSync as writeFileSync10, renameSync as renameSync4, mkdirSync as mkdirSync7, readFileSync as readFileSync21, unlinkSync as unlinkSync2 } from "fs";
|
|
6307
|
+
import { homedir as homedir12 } from "os";
|
|
6308
|
+
import { join as join26, dirname as dirname7 } from "path";
|
|
5519
6309
|
|
|
5520
6310
|
// src/lib/manager/scheduler/notify.ts
|
|
5521
|
-
import { createHash as
|
|
6311
|
+
import { createHash as createHash11 } from "crypto";
|
|
5522
6312
|
async function enqueueScheduledTaskNotice(opts) {
|
|
5523
6313
|
try {
|
|
5524
6314
|
const res = await api.post(
|
|
@@ -5535,7 +6325,7 @@ async function enqueueScheduledTaskNotice(opts) {
|
|
|
5535
6325
|
return res.ok === true;
|
|
5536
6326
|
} catch (err) {
|
|
5537
6327
|
const errText = err instanceof Error ? err.message : String(err);
|
|
5538
|
-
const errId =
|
|
6328
|
+
const errId = createHash11("sha256").update(errText).digest("hex").slice(0, 12);
|
|
5539
6329
|
log(
|
|
5540
6330
|
`[scheduled-kanban] notice enqueue failed for agent_id=${opts.agentId} task_id=${opts.taskId} error_id=${errId}`
|
|
5541
6331
|
);
|
|
@@ -5765,8 +6555,8 @@ async function runScheduledCardDelivery(codeName, agentId, cardId, completedBy,
|
|
|
5765
6555
|
markScheduledCardDeliveryComplete(cardId);
|
|
5766
6556
|
return "terminal";
|
|
5767
6557
|
}
|
|
5768
|
-
const
|
|
5769
|
-
const task =
|
|
6558
|
+
const state8 = claudeSchedulerStates.get(codeName) ?? loadSchedulerState(codeName);
|
|
6559
|
+
const task = state8.tasks[card.source_ref];
|
|
5770
6560
|
if (!task) {
|
|
5771
6561
|
log(`[scheduled-kanban] delivery: no scheduler task for source_ref=${card.source_ref} on '${codeName}' \u2014 skipping`);
|
|
5772
6562
|
markScheduledCardDeliveryComplete(cardId);
|
|
@@ -5874,21 +6664,21 @@ function resolveScheduledSlackTarget(task) {
|
|
|
5874
6664
|
}
|
|
5875
6665
|
function stampScheduledTurnMarker(codeName, taskId, target) {
|
|
5876
6666
|
try {
|
|
5877
|
-
const file =
|
|
6667
|
+
const file = join26(homedir12(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
|
|
5878
6668
|
const marker = { ts: Date.now(), task_id: taskId, ...target ? { target } : {} };
|
|
5879
6669
|
const tmp = `${file}.tmp`;
|
|
5880
|
-
|
|
5881
|
-
|
|
6670
|
+
writeFileSync10(tmp, JSON.stringify(marker), "utf8");
|
|
6671
|
+
renameSync4(tmp, file);
|
|
5882
6672
|
} catch (err) {
|
|
5883
6673
|
log(`[scheduled-kanban] scheduled-turn marker write failed for '${codeName}': ${err.message}`);
|
|
5884
6674
|
}
|
|
5885
6675
|
}
|
|
5886
6676
|
function clearScheduledTurnMarkerForTask(codeName, taskId) {
|
|
5887
|
-
const file =
|
|
6677
|
+
const file = join26(homedir12(), ".augmented", codeName, SCHEDULED_TURN_MARKER_FILENAME);
|
|
5888
6678
|
try {
|
|
5889
|
-
const raw = JSON.parse(
|
|
6679
|
+
const raw = JSON.parse(readFileSync21(file, "utf8"));
|
|
5890
6680
|
if (typeof raw?.task_id !== "string" || raw.task_id !== taskId) return;
|
|
5891
|
-
|
|
6681
|
+
unlinkSync2(file);
|
|
5892
6682
|
log(`[scheduled-kanban] scheduled-turn marker cleared for '${codeName}' (task ${taskId} complete)`);
|
|
5893
6683
|
} catch {
|
|
5894
6684
|
}
|
|
@@ -5946,9 +6736,9 @@ async function routeScheduledTaskViaKanban(codeName, agentId, task, prompt, dura
|
|
|
5946
6736
|
return false;
|
|
5947
6737
|
}
|
|
5948
6738
|
try {
|
|
5949
|
-
const doorbell = directChatDoorbellPath(agentId,
|
|
5950
|
-
|
|
5951
|
-
|
|
6739
|
+
const doorbell = directChatDoorbellPath(agentId, homedir12());
|
|
6740
|
+
mkdirSync7(dirname7(doorbell), { recursive: true });
|
|
6741
|
+
writeFileSync10(doorbell, String(Date.now()));
|
|
5952
6742
|
} catch (err) {
|
|
5953
6743
|
log(`[scheduled-kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
5954
6744
|
}
|
|
@@ -6014,24 +6804,24 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
6014
6804
|
const assertion = parseDeliverAssertion(rawOutput);
|
|
6015
6805
|
if (!assertion.deliver) {
|
|
6016
6806
|
const trimmed = (rawOutput ?? "").trim();
|
|
6017
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
6807
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash12("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
6018
6808
|
log(`[claude-scheduler] Suppressed by delivery_policy=conditional for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 ${assertion.vacuous ? "deliver marker had a vacuous reason" : "no deliver marker"}; output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
6019
6809
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
6020
6810
|
await reportDeliveryStatus(agentId, delivery.taskId, { status: "skipped", error_code: "SUPPRESSED_BY_POLICY" });
|
|
6021
6811
|
}
|
|
6022
6812
|
return { ok: true };
|
|
6023
6813
|
}
|
|
6024
|
-
const reasonHash =
|
|
6814
|
+
const reasonHash = createHash12("sha256").update(assertion.reason ?? "").digest("hex").slice(0, 12);
|
|
6025
6815
|
log(`[claude-scheduler] Delivering conditional run for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 agent asserted a concrete trigger (reason_len=${(assertion.reason ?? "").length} reason_hash=${reasonHash})`);
|
|
6026
6816
|
rawOutput = assertion.deliverable;
|
|
6027
6817
|
}
|
|
6028
6818
|
const classification = classifyOutput(rawOutput);
|
|
6029
6819
|
if (classification.action === "suppress") {
|
|
6030
6820
|
const trimmed = (rawOutput ?? "").trim();
|
|
6031
|
-
const outputHash = trimmed.length === 0 ? "empty" :
|
|
6821
|
+
const outputHash = trimmed.length === 0 ? "empty" : createHash12("sha256").update(trimmed).digest("hex").slice(0, 12);
|
|
6032
6822
|
log(`[claude-scheduler] Suppressing delivery for '${codeName}' (template=${templateId}, task=${delivery?.taskId ?? "n/a"}) \u2014 output_len=${trimmed.length} output_hash=${outputHash}`);
|
|
6033
6823
|
if (classification.suppressedNotes) {
|
|
6034
|
-
const notesHash =
|
|
6824
|
+
const notesHash = createHash12("sha256").update(classification.suppressedNotes).digest("hex").slice(0, 12);
|
|
6035
6825
|
log(`[claude-scheduler] Suppressed notes for '${codeName}' (task=${delivery?.taskId ?? "n/a"}) \u2014 notes_len=${classification.suppressedNotes.length} notes_hash=${notesHash}`);
|
|
6036
6826
|
}
|
|
6037
6827
|
if (delivery?.mode === "announce" && delivery.to) {
|
|
@@ -6097,13 +6887,13 @@ async function processClaudeTaskResult(codeName, agentId, templateId, rawOutput,
|
|
|
6097
6887
|
}
|
|
6098
6888
|
|
|
6099
6889
|
// src/lib/manager/scheduler/execution.ts
|
|
6100
|
-
import { createHash as
|
|
6101
|
-
import { homedir as
|
|
6102
|
-
import { join as
|
|
6890
|
+
import { createHash as createHash13 } from "crypto";
|
|
6891
|
+
import { homedir as homedir13 } from "os";
|
|
6892
|
+
import { join as join28 } from "path";
|
|
6103
6893
|
|
|
6104
6894
|
// src/lib/agent-serving-probe.ts
|
|
6105
|
-
import { readFileSync as
|
|
6106
|
-
import { join as
|
|
6895
|
+
import { readFileSync as readFileSync22, readdirSync as readdirSync5, statSync as statSync5 } from "fs";
|
|
6896
|
+
import { join as join27 } from "path";
|
|
6107
6897
|
var RATE_LIMIT_WINDOW_MS = 6 * 60 * 60 * 1e3;
|
|
6108
6898
|
function probeRateLimit(args) {
|
|
6109
6899
|
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
@@ -6112,23 +6902,23 @@ function probeRateLimit(args) {
|
|
|
6112
6902
|
const dir = args.transcriptDir ?? sessionTranscriptDir(args.projectDir);
|
|
6113
6903
|
let entries;
|
|
6114
6904
|
try {
|
|
6115
|
-
entries =
|
|
6905
|
+
entries = readdirSync5(dir);
|
|
6116
6906
|
} catch {
|
|
6117
6907
|
return UNKNOWN_RATE_LIMIT;
|
|
6118
6908
|
}
|
|
6119
6909
|
let newest = UNKNOWN_RATE_LIMIT;
|
|
6120
6910
|
for (const name of entries) {
|
|
6121
6911
|
if (!name.endsWith(".jsonl")) continue;
|
|
6122
|
-
const path =
|
|
6912
|
+
const path = join27(dir, name);
|
|
6123
6913
|
try {
|
|
6124
|
-
const st =
|
|
6914
|
+
const st = statSync5(path);
|
|
6125
6915
|
if (!st.isFile() || st.mtimeMs < startMs) continue;
|
|
6126
6916
|
} catch {
|
|
6127
6917
|
continue;
|
|
6128
6918
|
}
|
|
6129
6919
|
let content;
|
|
6130
6920
|
try {
|
|
6131
|
-
content =
|
|
6921
|
+
content = readFileSync22(path, "utf-8");
|
|
6132
6922
|
} catch {
|
|
6133
6923
|
continue;
|
|
6134
6924
|
}
|
|
@@ -6190,7 +6980,7 @@ function shouldLogUsageCapDeferral(site, codeName, limitedUntil) {
|
|
|
6190
6980
|
|
|
6191
6981
|
// src/lib/manager/scheduler/execution.ts
|
|
6192
6982
|
function claudePidFilePath() {
|
|
6193
|
-
return
|
|
6983
|
+
return join28(homedir13(), ".augmented", "manager-claude-pids.json");
|
|
6194
6984
|
}
|
|
6195
6985
|
var inFlightClaudePids = /* @__PURE__ */ new Map();
|
|
6196
6986
|
function registerClaudeSpawn(record) {
|
|
@@ -6210,24 +7000,24 @@ function unregisterClaudeSpawn(pid) {
|
|
|
6210
7000
|
}
|
|
6211
7001
|
async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData, durableScheduledNudge = false) {
|
|
6212
7002
|
const codeName = agent.code_name;
|
|
6213
|
-
const stableTasksHash =
|
|
6214
|
-
const boardHash = boardItems.length > 0 ?
|
|
7003
|
+
const stableTasksHash = createHash13("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
7004
|
+
const boardHash = boardItems.length > 0 ? createHash13("sha256").update(JSON.stringify(boardItems.map((b) => ({ id: b.id, title: b.title, status: b.status, priority: b.priority, deliverable: b.deliverable })))).digest("hex").slice(0, 16) : "empty";
|
|
6215
7005
|
const resolvedModels = resolveModelChain(refreshData);
|
|
6216
|
-
const modelsHash =
|
|
7006
|
+
const modelsHash = createHash13("sha256").update(JSON.stringify(resolvedModels)).digest("hex").slice(0, 16);
|
|
6217
7007
|
const combinedHash = `${stableTasksHash}:${boardHash}:${modelsHash}`;
|
|
6218
7008
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
6219
7009
|
if (combinedHash !== prevHash) {
|
|
6220
7010
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
6221
|
-
const
|
|
6222
|
-
claudeSchedulerStates.set(codeName,
|
|
7011
|
+
const state9 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
7012
|
+
claudeSchedulerStates.set(codeName, state9);
|
|
6223
7013
|
agentState.knownTasksHashes.set(agent.agent_id, combinedHash);
|
|
6224
7014
|
log(`[claude-scheduler] Tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
6225
7015
|
}
|
|
6226
7016
|
if (!claudeSchedulerStates.has(codeName)) {
|
|
6227
7017
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
6228
7018
|
}
|
|
6229
|
-
const
|
|
6230
|
-
const ready = getReadyTasks(
|
|
7019
|
+
const state8 = claudeSchedulerStates.get(codeName);
|
|
7020
|
+
const ready = getReadyTasks(state8, inFlightClaudeTasks);
|
|
6231
7021
|
if (ready.length === 0) return;
|
|
6232
7022
|
const limitedUntil = readUsageCapUntil({ codeName, projectDir: getProjectDir(codeName) });
|
|
6233
7023
|
if (limitedUntil) {
|
|
@@ -6260,19 +7050,19 @@ async function syncAndCheckClaudeScheduler(agent, tasks, boardItems, refreshData
|
|
|
6260
7050
|
}
|
|
6261
7051
|
|
|
6262
7052
|
// src/lib/occupancy-gate.ts
|
|
6263
|
-
import { closeSync, openSync, readdirSync as
|
|
6264
|
-
import { join as
|
|
7053
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync6, readSync as readSync2, statSync as statSync6 } from "fs";
|
|
7054
|
+
import { join as join29 } from "path";
|
|
6265
7055
|
var TAIL_BYTES = 256 * 1024;
|
|
6266
7056
|
var MAX_RECORD_ALIGN_BYTES = 8 * 1024 * 1024;
|
|
6267
7057
|
var GATE_MAX_BUCKET_AGE_MS = 30 * 6e4;
|
|
6268
7058
|
var TRANSCRIPT_MTIME_WINDOW_MS4 = GATE_MAX_BUCKET_AGE_MS + DEFAULT_NEIGHBOURHOOD_MS + 6e4;
|
|
6269
|
-
var
|
|
7059
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
6270
7060
|
function readAlignedTail(path, tailBytes, maxAlignBytes) {
|
|
6271
7061
|
let fd = null;
|
|
6272
7062
|
try {
|
|
6273
|
-
const size =
|
|
7063
|
+
const size = statSync6(path).size;
|
|
6274
7064
|
if (size <= 0) return { content: "", complete: true, truncated: false };
|
|
6275
|
-
fd =
|
|
7065
|
+
fd = openSync2(path, "r");
|
|
6276
7066
|
let readStart = Math.max(0, size - tailBytes);
|
|
6277
7067
|
if (readStart > 0) {
|
|
6278
7068
|
const scanFloor = Math.max(0, readStart - maxAlignBytes);
|
|
@@ -6283,7 +7073,7 @@ function readAlignedTail(path, tailBytes, maxAlignBytes) {
|
|
|
6283
7073
|
const from = Math.max(scanFloor, pos - CHUNK);
|
|
6284
7074
|
const len2 = pos - from;
|
|
6285
7075
|
const b = Buffer.allocUnsafe(len2);
|
|
6286
|
-
if (
|
|
7076
|
+
if (readSync2(fd, b, 0, len2, from) !== len2) return null;
|
|
6287
7077
|
const idx = b.lastIndexOf(10);
|
|
6288
7078
|
if (idx !== -1) {
|
|
6289
7079
|
boundary = from + idx + 1;
|
|
@@ -6301,14 +7091,14 @@ function readAlignedTail(path, tailBytes, maxAlignBytes) {
|
|
|
6301
7091
|
}
|
|
6302
7092
|
const len = size - readStart;
|
|
6303
7093
|
const buf = Buffer.allocUnsafe(len);
|
|
6304
|
-
if (
|
|
7094
|
+
if (readSync2(fd, buf, 0, len, readStart) !== len) return null;
|
|
6305
7095
|
return { content: buf.toString("utf-8", 0, len), complete: true, truncated: readStart > 0 };
|
|
6306
7096
|
} catch {
|
|
6307
7097
|
return null;
|
|
6308
7098
|
} finally {
|
|
6309
7099
|
if (fd != null) {
|
|
6310
7100
|
try {
|
|
6311
|
-
|
|
7101
|
+
closeSync2(fd);
|
|
6312
7102
|
} catch {
|
|
6313
7103
|
}
|
|
6314
7104
|
}
|
|
@@ -6340,26 +7130,26 @@ function candidateTranscriptPaths(dir) {
|
|
|
6340
7130
|
const paths = [];
|
|
6341
7131
|
let top;
|
|
6342
7132
|
try {
|
|
6343
|
-
top =
|
|
7133
|
+
top = readdirSync6(dir);
|
|
6344
7134
|
} catch {
|
|
6345
7135
|
return { paths, complete: false };
|
|
6346
7136
|
}
|
|
6347
7137
|
let complete = true;
|
|
6348
7138
|
for (const name of top) {
|
|
6349
7139
|
if (name.endsWith(".jsonl")) {
|
|
6350
|
-
paths.push(
|
|
7140
|
+
paths.push(join29(dir, name));
|
|
6351
7141
|
continue;
|
|
6352
7142
|
}
|
|
6353
|
-
const subDir =
|
|
7143
|
+
const subDir = join29(dir, name, "subagents");
|
|
6354
7144
|
let subs;
|
|
6355
7145
|
try {
|
|
6356
|
-
subs =
|
|
7146
|
+
subs = readdirSync6(subDir);
|
|
6357
7147
|
} catch (err) {
|
|
6358
7148
|
if (!isAbsentDirError(err)) complete = false;
|
|
6359
7149
|
continue;
|
|
6360
7150
|
}
|
|
6361
7151
|
for (const sub of subs) {
|
|
6362
|
-
if (sub.endsWith(".jsonl")) paths.push(
|
|
7152
|
+
if (sub.endsWith(".jsonl")) paths.push(join29(subDir, sub));
|
|
6363
7153
|
}
|
|
6364
7154
|
}
|
|
6365
7155
|
return { paths, complete };
|
|
@@ -6369,7 +7159,7 @@ function collectQualifyingTurns(codeName, nowMs, opts = {}) {
|
|
|
6369
7159
|
const tailBytes = opts.tailBytes ?? TAIL_BYTES;
|
|
6370
7160
|
const maxAlignBytes = opts.maxAlignBytes ?? MAX_RECORD_ALIGN_BYTES;
|
|
6371
7161
|
const sinceMs = nowMs - TRANSCRIPT_MTIME_WINDOW_MS4;
|
|
6372
|
-
const agentCache =
|
|
7162
|
+
const agentCache = cache2.get(codeName) ?? /* @__PURE__ */ new Map();
|
|
6373
7163
|
const nextCache = /* @__PURE__ */ new Map();
|
|
6374
7164
|
const all = /* @__PURE__ */ new Set();
|
|
6375
7165
|
let transcriptsRead = 0;
|
|
@@ -6382,7 +7172,7 @@ function collectQualifyingTurns(codeName, nowMs, opts = {}) {
|
|
|
6382
7172
|
for (const path of paths) {
|
|
6383
7173
|
let st;
|
|
6384
7174
|
try {
|
|
6385
|
-
st =
|
|
7175
|
+
st = statSync6(path);
|
|
6386
7176
|
} catch (err) {
|
|
6387
7177
|
if (!isAbsentDirError(err)) complete = false;
|
|
6388
7178
|
continue;
|
|
@@ -6418,7 +7208,7 @@ function collectQualifyingTurns(codeName, nowMs, opts = {}) {
|
|
|
6418
7208
|
}
|
|
6419
7209
|
for (const t of turns) all.add(t);
|
|
6420
7210
|
}
|
|
6421
|
-
|
|
7211
|
+
cache2.set(codeName, nextCache);
|
|
6422
7212
|
return { turns: [...all].sort((a, b) => a - b), transcriptsRead, complete, coveredFromMs };
|
|
6423
7213
|
}
|
|
6424
7214
|
function qualifyBuckets(buckets, qualifying, mode, nowMs, neighbourhoodMs = DEFAULT_NEIGHBOURHOOD_MS) {
|
|
@@ -6454,14 +7244,14 @@ function qualifyBuckets(buckets, qualifying, mode, nowMs, neighbourhoodMs = DEFA
|
|
|
6454
7244
|
}
|
|
6455
7245
|
|
|
6456
7246
|
// src/lib/pane-occupancy-sampler.ts
|
|
6457
|
-
import { statSync as
|
|
7247
|
+
import { statSync as statSync7 } from "fs";
|
|
6458
7248
|
var SAMPLE_INTERVAL_MS = 1e4;
|
|
6459
7249
|
var IDLE_GAP_MS = 12e4;
|
|
6460
7250
|
var POST_IDLE_CREDIT_MS = 6e4;
|
|
6461
7251
|
var lastMtimeMs = /* @__PURE__ */ new Map();
|
|
6462
7252
|
function paneMtimeMs(codeName) {
|
|
6463
7253
|
try {
|
|
6464
|
-
return
|
|
7254
|
+
return statSync7(paneLogPath(codeName)).mtimeMs;
|
|
6465
7255
|
} catch {
|
|
6466
7256
|
return null;
|
|
6467
7257
|
}
|
|
@@ -6507,7 +7297,7 @@ function stopPaneOccupancySampler() {
|
|
|
6507
7297
|
}
|
|
6508
7298
|
|
|
6509
7299
|
// src/lib/opencode-slack-ingest.ts
|
|
6510
|
-
import { createHash as
|
|
7300
|
+
import { createHash as createHash14 } from "crypto";
|
|
6511
7301
|
|
|
6512
7302
|
// ../../packages/core/dist/channels/slack-rich-text.js
|
|
6513
7303
|
var MAX_DEPTH = 12;
|
|
@@ -7458,7 +8248,7 @@ function startSlackIngest(config2) {
|
|
|
7458
8248
|
// src/lib/opencode-slack-ingest.ts
|
|
7459
8249
|
var ingests = /* @__PURE__ */ new Map();
|
|
7460
8250
|
function fingerprint(params) {
|
|
7461
|
-
return
|
|
8251
|
+
return createHash14("sha256").update(
|
|
7462
8252
|
JSON.stringify({
|
|
7463
8253
|
appToken: params.appToken,
|
|
7464
8254
|
botToken: params.botToken,
|
|
@@ -7539,7 +8329,7 @@ function stopAllOpencodeSlackIngests(log2) {
|
|
|
7539
8329
|
}
|
|
7540
8330
|
|
|
7541
8331
|
// src/lib/manager/opencode-scheduler.ts
|
|
7542
|
-
import { createHash as
|
|
8332
|
+
import { createHash as createHash15 } from "crypto";
|
|
7543
8333
|
var MAX_OPENCODE_SCHED_CONCURRENCY = 2;
|
|
7544
8334
|
var opencodeSchedulerStates = /* @__PURE__ */ new Map();
|
|
7545
8335
|
var inFlightOpencodeTasks = /* @__PURE__ */ new Set();
|
|
@@ -7553,19 +8343,19 @@ function shouldDeliver(task, reply) {
|
|
|
7553
8343
|
}
|
|
7554
8344
|
async function syncAndCheckOpencodeScheduler(agent, tasks, refreshData) {
|
|
7555
8345
|
const codeName = agent.code_name;
|
|
7556
|
-
const tasksHash =
|
|
8346
|
+
const tasksHash = createHash15("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
7557
8347
|
if (knownOpencodeTaskHashes.get(agent.agent_id) !== tasksHash) {
|
|
7558
8348
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
7559
|
-
const
|
|
7560
|
-
opencodeSchedulerStates.set(codeName,
|
|
8349
|
+
const state9 = syncTasksToScheduler(codeName, agent.agent_id, taskInputs);
|
|
8350
|
+
opencodeSchedulerStates.set(codeName, state9);
|
|
7561
8351
|
knownOpencodeTaskHashes.set(agent.agent_id, tasksHash);
|
|
7562
8352
|
log(`[opencode-scheduler] tasks synced for '${codeName}' (${taskInputs.length} task(s))`);
|
|
7563
8353
|
}
|
|
7564
8354
|
if (!opencodeSchedulerStates.has(codeName)) {
|
|
7565
8355
|
opencodeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
7566
8356
|
}
|
|
7567
|
-
const
|
|
7568
|
-
const ready = getReadyTasks(
|
|
8357
|
+
const state8 = opencodeSchedulerStates.get(codeName);
|
|
8358
|
+
const ready = getReadyTasks(state8, inFlightOpencodeTasks);
|
|
7569
8359
|
if (ready.length === 0) return;
|
|
7570
8360
|
if (!isOpencodeSessionHealthy(codeName)) {
|
|
7571
8361
|
log(`[opencode-scheduler] '${codeName}' has ${ready.length} due task(s) but no healthy serve yet - deferring`);
|
|
@@ -7631,10 +8421,10 @@ async function fireOpencodeScheduledTask(agent, task) {
|
|
|
7631
8421
|
}
|
|
7632
8422
|
|
|
7633
8423
|
// src/lib/opencode-telegram-ingest.ts
|
|
7634
|
-
import { createHash as
|
|
7635
|
-
import { existsSync as
|
|
8424
|
+
import { createHash as createHash16 } from "crypto";
|
|
8425
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync23, renameSync as renameSync5, unlinkSync as unlinkSync3, writeFileSync as writeFileSync11 } from "fs";
|
|
7636
8426
|
import { randomUUID } from "crypto";
|
|
7637
|
-
import { join as
|
|
8427
|
+
import { join as join30 } from "path";
|
|
7638
8428
|
|
|
7639
8429
|
// src/lib/telegram-ingest.ts
|
|
7640
8430
|
import https2 from "https";
|
|
@@ -8167,7 +8957,7 @@ function anySignal(signals) {
|
|
|
8167
8957
|
// src/lib/opencode-telegram-ingest.ts
|
|
8168
8958
|
var ingests2 = /* @__PURE__ */ new Map();
|
|
8169
8959
|
function fingerprint2(params) {
|
|
8170
|
-
return
|
|
8960
|
+
return createHash16("sha256").update(
|
|
8171
8961
|
JSON.stringify({
|
|
8172
8962
|
botToken: params.botToken,
|
|
8173
8963
|
allowedChats: [...params.allowedChats].sort(),
|
|
@@ -8182,7 +8972,7 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
8182
8972
|
let filePath;
|
|
8183
8973
|
try {
|
|
8184
8974
|
dir = getFramework("opencode").getAgentDir(codeName);
|
|
8185
|
-
filePath =
|
|
8975
|
+
filePath = join30(dir, "telegram-getupdates-offset-opencode.json");
|
|
8186
8976
|
} catch {
|
|
8187
8977
|
dir = null;
|
|
8188
8978
|
filePath = null;
|
|
@@ -8191,7 +8981,7 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
8191
8981
|
load() {
|
|
8192
8982
|
if (!filePath) return 0;
|
|
8193
8983
|
try {
|
|
8194
|
-
const parsed = JSON.parse(
|
|
8984
|
+
const parsed = JSON.parse(readFileSync23(filePath, "utf-8"));
|
|
8195
8985
|
if (currentBotId != null && typeof parsed?.bot_id === "number" && parsed.bot_id !== currentBotId) {
|
|
8196
8986
|
log2(`[telegram-ingest:${codeName}] offset cursor belongs to a different bot; ignoring (bot swap)`);
|
|
8197
8987
|
return 0;
|
|
@@ -8209,8 +8999,8 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
8209
8999
|
if (!filePath || !dir) return;
|
|
8210
9000
|
const tmpPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
8211
9001
|
try {
|
|
8212
|
-
|
|
8213
|
-
|
|
9002
|
+
mkdirSync8(dir, { recursive: true, mode: 448 });
|
|
9003
|
+
writeFileSync11(
|
|
8214
9004
|
tmpPath,
|
|
8215
9005
|
JSON.stringify({
|
|
8216
9006
|
offset,
|
|
@@ -8220,11 +9010,11 @@ function createFileOffsetStore(codeName, log2, currentBotId) {
|
|
|
8220
9010
|
}),
|
|
8221
9011
|
{ mode: 384 }
|
|
8222
9012
|
);
|
|
8223
|
-
|
|
9013
|
+
renameSync5(tmpPath, filePath);
|
|
8224
9014
|
} catch (err) {
|
|
8225
9015
|
log2(`[telegram-ingest:${codeName}] offset persist failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8226
9016
|
try {
|
|
8227
|
-
if (
|
|
9017
|
+
if (existsSync12(tmpPath)) unlinkSync3(tmpPath);
|
|
8228
9018
|
} catch {
|
|
8229
9019
|
}
|
|
8230
9020
|
}
|
|
@@ -8440,10 +9230,10 @@ function recordWedgeForCards(states, inProgressCardIds, nowMs, config2) {
|
|
|
8440
9230
|
function pruneCardStates(states, liveInProgressCardIds, nowMs, config2) {
|
|
8441
9231
|
const live = liveInProgressCardIds instanceof Set ? liveInProgressCardIds : new Set(liveInProgressCardIds);
|
|
8442
9232
|
const next = /* @__PURE__ */ new Map();
|
|
8443
|
-
for (const [id,
|
|
9233
|
+
for (const [id, state8] of states) {
|
|
8444
9234
|
if (!live.has(id)) continue;
|
|
8445
|
-
if (nowMs -
|
|
8446
|
-
next.set(id,
|
|
9235
|
+
if (nowMs - state8.lastWedgeAtMs > config2.cooldownMs) continue;
|
|
9236
|
+
next.set(id, state8);
|
|
8447
9237
|
}
|
|
8448
9238
|
return next;
|
|
8449
9239
|
}
|
|
@@ -8461,24 +9251,24 @@ function partitionActionableByPoison(actionable, states, config2) {
|
|
|
8461
9251
|
}
|
|
8462
9252
|
|
|
8463
9253
|
// src/lib/restart-flags.ts
|
|
8464
|
-
import { existsSync as
|
|
8465
|
-
import { homedir as
|
|
8466
|
-
import { join as
|
|
9254
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync24, renameSync as renameSync6, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "fs";
|
|
9255
|
+
import { homedir as homedir14 } from "os";
|
|
9256
|
+
import { join as join31 } from "path";
|
|
8467
9257
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
8468
9258
|
function restartFlagsDir() {
|
|
8469
|
-
return
|
|
9259
|
+
return join31(homedir14(), ".augmented", "restart-flags");
|
|
8470
9260
|
}
|
|
8471
9261
|
function flagPath(codeName) {
|
|
8472
|
-
return
|
|
9262
|
+
return join31(restartFlagsDir(), `${codeName}.flag`);
|
|
8473
9263
|
}
|
|
8474
9264
|
function readRestartFlags() {
|
|
8475
9265
|
const dir = restartFlagsDir();
|
|
8476
|
-
if (!
|
|
9266
|
+
if (!existsSync13(dir)) return [];
|
|
8477
9267
|
const out = [];
|
|
8478
|
-
for (const entry of
|
|
9268
|
+
for (const entry of readdirSync7(dir)) {
|
|
8479
9269
|
if (!entry.endsWith(".flag")) continue;
|
|
8480
9270
|
try {
|
|
8481
|
-
const raw =
|
|
9271
|
+
const raw = readFileSync24(join31(dir, entry), "utf8");
|
|
8482
9272
|
const parsed = JSON.parse(raw);
|
|
8483
9273
|
if (typeof parsed.codeName !== "string" || parsed.codeName.length === 0) {
|
|
8484
9274
|
parsed.codeName = entry.replace(/\.flag$/, "");
|
|
@@ -8496,7 +9286,7 @@ function readRestartFlags() {
|
|
|
8496
9286
|
}
|
|
8497
9287
|
function deleteRestartFlag(codeName) {
|
|
8498
9288
|
const path = flagPath(codeName);
|
|
8499
|
-
if (
|
|
9289
|
+
if (existsSync13(path)) {
|
|
8500
9290
|
rmSync4(path, { force: true });
|
|
8501
9291
|
}
|
|
8502
9292
|
}
|
|
@@ -8596,8 +9386,8 @@ async function sendError(flag, opts, text) {
|
|
|
8596
9386
|
}
|
|
8597
9387
|
|
|
8598
9388
|
// src/lib/restart-context.ts
|
|
8599
|
-
import { readdirSync as
|
|
8600
|
-
import { dirname as
|
|
9389
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync25, writeFileSync as writeFileSync13, mkdirSync as mkdirSync10, unlinkSync as unlinkSync4 } from "fs";
|
|
9390
|
+
import { dirname as dirname8, join as join32 } from "path";
|
|
8601
9391
|
var SLACK_PENDING_INBOUND_DIRNAME = "slack-pending-inbound";
|
|
8602
9392
|
var SLACK_RESTART_CONTEXT_DIRNAME = "slack-restart-context";
|
|
8603
9393
|
var MAX_TOPIC_CHARS = 140;
|
|
@@ -8606,13 +9396,13 @@ var RECONSTRUCT_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
8606
9396
|
var WINDOW_PAD_MS3 = 5 * 6e4;
|
|
8607
9397
|
var DEFAULT_MAX_MARKERS_PER_AGENT = 25;
|
|
8608
9398
|
function augmentedAgentDir(codeName) {
|
|
8609
|
-
return
|
|
9399
|
+
return dirname8(getProjectDir(codeName));
|
|
8610
9400
|
}
|
|
8611
9401
|
function slackPendingInboundDir(codeName) {
|
|
8612
|
-
return
|
|
9402
|
+
return join32(augmentedAgentDir(codeName), SLACK_PENDING_INBOUND_DIRNAME);
|
|
8613
9403
|
}
|
|
8614
9404
|
function slackRestartContextDir(codeName) {
|
|
8615
|
-
return
|
|
9405
|
+
return join32(augmentedAgentDir(codeName), SLACK_RESTART_CONTEXT_DIRNAME);
|
|
8616
9406
|
}
|
|
8617
9407
|
function sanitizeTopic(raw) {
|
|
8618
9408
|
const cleaned = raw.replace(/\s+/g, " ").trim().replace(/[<>]/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -8647,14 +9437,14 @@ function computeRestartContextHints(markers, allTurns, nowMs, reconstruct = reco
|
|
|
8647
9437
|
}
|
|
8648
9438
|
function safeReaddir(dir) {
|
|
8649
9439
|
try {
|
|
8650
|
-
return
|
|
9440
|
+
return readdirSync8(dir);
|
|
8651
9441
|
} catch {
|
|
8652
9442
|
return [];
|
|
8653
9443
|
}
|
|
8654
9444
|
}
|
|
8655
9445
|
function readStrandedMarker(path) {
|
|
8656
9446
|
try {
|
|
8657
|
-
const parsed = JSON.parse(
|
|
9447
|
+
const parsed = JSON.parse(readFileSync25(path, "utf-8"));
|
|
8658
9448
|
if (typeof parsed.channel === "string" && typeof parsed.thread_ts === "string") {
|
|
8659
9449
|
return { channel: parsed.channel, thread_ts: parsed.thread_ts };
|
|
8660
9450
|
}
|
|
@@ -8663,8 +9453,8 @@ function readStrandedMarker(path) {
|
|
|
8663
9453
|
return null;
|
|
8664
9454
|
}
|
|
8665
9455
|
function writeHintFile(path, dir, hint) {
|
|
8666
|
-
|
|
8667
|
-
|
|
9456
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
9457
|
+
writeFileSync13(path, JSON.stringify(hint), { mode: 384 });
|
|
8668
9458
|
}
|
|
8669
9459
|
function pruneHintsExcept(codeName, freshFilenames) {
|
|
8670
9460
|
const ctxDir = slackRestartContextDir(codeName);
|
|
@@ -8672,7 +9462,7 @@ function pruneHintsExcept(codeName, freshFilenames) {
|
|
|
8672
9462
|
if (!filename.endsWith(".json")) continue;
|
|
8673
9463
|
if (freshFilenames.has(filename)) continue;
|
|
8674
9464
|
try {
|
|
8675
|
-
|
|
9465
|
+
unlinkSync4(join32(ctxDir, filename));
|
|
8676
9466
|
} catch {
|
|
8677
9467
|
}
|
|
8678
9468
|
}
|
|
@@ -8693,7 +9483,7 @@ function refreshSlackRestartContextHints(codeNames, opts = {}) {
|
|
|
8693
9483
|
}
|
|
8694
9484
|
const markers = [];
|
|
8695
9485
|
for (const filename of markerFilenames.slice(0, cap)) {
|
|
8696
|
-
const parsed = readStrandedMarker(
|
|
9486
|
+
const parsed = readStrandedMarker(join32(markerDir, filename));
|
|
8697
9487
|
if (parsed) markers.push({ filename, channel: parsed.channel, thread_ts: parsed.thread_ts });
|
|
8698
9488
|
}
|
|
8699
9489
|
if (markers.length === 0) {
|
|
@@ -8707,7 +9497,7 @@ function refreshSlackRestartContextHints(codeNames, opts = {}) {
|
|
|
8707
9497
|
const freshFilenames = /* @__PURE__ */ new Set();
|
|
8708
9498
|
for (const { filename, hint } of hints) {
|
|
8709
9499
|
try {
|
|
8710
|
-
writeHintFile(
|
|
9500
|
+
writeHintFile(join32(ctxDir, filename), ctxDir, hint);
|
|
8711
9501
|
freshFilenames.add(filename);
|
|
8712
9502
|
} catch (err) {
|
|
8713
9503
|
log2(`[restart-context] ${codeName}: hint write failed for ${filename}: ${err.message}`);
|
|
@@ -9329,8 +10119,8 @@ var KNOWN_SAFE_TAIL_SIGNATURES = /* @__PURE__ */ new Set(["session_id_in_use"]);
|
|
|
9329
10119
|
function shouldSkipRevokedCleanup(previousKnownStatus) {
|
|
9330
10120
|
return previousKnownStatus === "revoked";
|
|
9331
10121
|
}
|
|
9332
|
-
function hasRevokedResiduals(
|
|
9333
|
-
return
|
|
10122
|
+
function hasRevokedResiduals(state8) {
|
|
10123
|
+
return state8.gatewayRunning || state8.portAllocated || state8.provisionDirExists;
|
|
9334
10124
|
}
|
|
9335
10125
|
var pendingSessionRestarts = /* @__PURE__ */ new Map();
|
|
9336
10126
|
var lastRestartWasExternal = /* @__PURE__ */ new Map();
|
|
@@ -9508,12 +10298,12 @@ function maybeClearStaleTrip(agent) {
|
|
|
9508
10298
|
const marker = getAutoResumeMarker(codeName);
|
|
9509
10299
|
const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
|
|
9510
10300
|
if (!isSelfResume) deleteAutoResumeMarker(codeName);
|
|
9511
|
-
|
|
9512
|
-
...
|
|
10301
|
+
state7 = {
|
|
10302
|
+
...state7,
|
|
9513
10303
|
circuitBreakerTrips: restartBreaker.serialize(),
|
|
9514
10304
|
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
9515
10305
|
};
|
|
9516
|
-
send({ type: "state-update", state:
|
|
10306
|
+
send({ type: "state-update", state: state7 });
|
|
9517
10307
|
}
|
|
9518
10308
|
var autoResumeMarkers = /* @__PURE__ */ new Map();
|
|
9519
10309
|
function autoResumeMarkerKey(codeName) {
|
|
@@ -9605,12 +10395,12 @@ function maybeAutoResume(agent) {
|
|
|
9605
10395
|
restartBreaker.clear(codeName);
|
|
9606
10396
|
reportedTrips.delete(codeName);
|
|
9607
10397
|
dependencyRecoveryLedger.clear(codeName);
|
|
9608
|
-
|
|
9609
|
-
...
|
|
10398
|
+
state7 = {
|
|
10399
|
+
...state7,
|
|
9610
10400
|
circuitBreakerTrips: restartBreaker.serialize(),
|
|
9611
10401
|
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
9612
10402
|
};
|
|
9613
|
-
send({ type: "state-update", state:
|
|
10403
|
+
send({ type: "state-update", state: state7 });
|
|
9614
10404
|
log(`[auto-resume] agent=${codeName} resumed \u2014 re-trip within backoff window will stay paused (ENG-6088)`);
|
|
9615
10405
|
} else {
|
|
9616
10406
|
autoResumeStandDowns.add(`${codeName}:${trippedAt}`);
|
|
@@ -9719,12 +10509,12 @@ async function maybeResumeReconcile(agent) {
|
|
|
9719
10509
|
restartBreaker.clear(codeName);
|
|
9720
10510
|
reportedTrips.delete(codeName);
|
|
9721
10511
|
dependencyRecoveryLedger.clear(codeName);
|
|
9722
|
-
|
|
9723
|
-
...
|
|
10512
|
+
state7 = {
|
|
10513
|
+
...state7,
|
|
9724
10514
|
circuitBreakerTrips: restartBreaker.serialize(),
|
|
9725
10515
|
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
9726
10516
|
};
|
|
9727
|
-
send({ type: "state-update", state:
|
|
10517
|
+
send({ type: "state-update", state: state7 });
|
|
9728
10518
|
log(`[resume-reconciler] agent=${codeName} resumed \u2014 a re-trip within the backoff window will latch unstable (ENG-6383)`);
|
|
9729
10519
|
} else {
|
|
9730
10520
|
autoResumeStandDowns.add(standDownKey);
|
|
@@ -10057,7 +10847,7 @@ function performLazyDayRolloverReset(codeName, agentTimezone) {
|
|
|
10057
10847
|
}
|
|
10058
10848
|
function paneLogAgeSecondsFor(codeName) {
|
|
10059
10849
|
try {
|
|
10060
|
-
const mtimeMs =
|
|
10850
|
+
const mtimeMs = statSync8(paneLogPath(codeName)).mtimeMs;
|
|
10061
10851
|
return Math.max(0, Math.floor((Date.now() - mtimeMs) / 1e3));
|
|
10062
10852
|
} catch (err) {
|
|
10063
10853
|
if (err?.code === "ENOENT") return null;
|
|
@@ -10106,7 +10896,7 @@ function restartGateFor(codeName, reason) {
|
|
|
10106
10896
|
}
|
|
10107
10897
|
function isHostBusyForForcedUpdate(opts) {
|
|
10108
10898
|
const now = /* @__PURE__ */ new Date();
|
|
10109
|
-
for (const agent of
|
|
10899
|
+
for (const agent of state7.agents) {
|
|
10110
10900
|
const decision = decideRestartGate({
|
|
10111
10901
|
window: null,
|
|
10112
10902
|
paneLogAgeSeconds: paneLogAgeSecondsFor(agent.codeName),
|
|
@@ -10139,7 +10929,7 @@ function runPendingForcedUpdate() {
|
|
|
10139
10929
|
relaxed
|
|
10140
10930
|
} = decidePendingForcedUpdate({
|
|
10141
10931
|
requestedAt: requestedUpdateAt,
|
|
10142
|
-
lastProcessedAt:
|
|
10932
|
+
lastProcessedAt: state7.lastUpdateRequestProcessedAt ?? null,
|
|
10143
10933
|
deferStreak: forcedUpdateDeferStreak,
|
|
10144
10934
|
nowMs: Date.now(),
|
|
10145
10935
|
isHostBusy: (isRelaxed) => isHostBusyForForcedUpdate({ relaxed: isRelaxed })
|
|
@@ -10173,7 +10963,7 @@ function runPendingForcedUpdate() {
|
|
|
10173
10963
|
log(
|
|
10174
10964
|
forcedUpdate === "consume-deadline" ? `[self-update] WARN "Update CLI now" requested at ${requestedUpdateAt} has been pending ${describePendingFor(pendingForMs)} and the host still reads busy \u2014 running the window-bypassing self-update anyway (ENG-8449 hard deadline). A live turn may be interrupted; the operator asked for this update explicitly.` : `[self-update] "Update CLI now" requested at ${requestedUpdateAt} \u2014 running window-bypassing self-update now (host idle${relaxed ? ", relaxed gate" : ""}).`
|
|
10175
10965
|
);
|
|
10176
|
-
const prevProcessedAt =
|
|
10966
|
+
const prevProcessedAt = state7.lastUpdateRequestProcessedAt ?? null;
|
|
10177
10967
|
void checkAndUpdateCli({ force: true }).then((outcome) => {
|
|
10178
10968
|
if (!shouldConsumeForcedUpdate(outcome)) {
|
|
10179
10969
|
log(
|
|
@@ -10184,11 +10974,11 @@ function runPendingForcedUpdate() {
|
|
|
10184
10974
|
log(
|
|
10185
10975
|
outcome === "updated" ? `[self-update] "Update CLI now" completed \u2014 upgrade installed; manager restart scheduled.` : `[self-update] "Update CLI now" completed \u2014 host was already on the latest in-channel build; nothing to install.`
|
|
10186
10976
|
);
|
|
10187
|
-
|
|
10977
|
+
state7.lastUpdateRequestProcessedAt = requestedUpdateAt;
|
|
10188
10978
|
try {
|
|
10189
|
-
atomicWriteFileSync(getStateFile(), JSON.stringify(
|
|
10979
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(state7, null, 2));
|
|
10190
10980
|
} catch (err) {
|
|
10191
|
-
|
|
10981
|
+
state7.lastUpdateRequestProcessedAt = prevProcessedAt;
|
|
10192
10982
|
log(
|
|
10193
10983
|
`[self-update] failed to persist update-request ack; retrying next poll: ${err.message}`
|
|
10194
10984
|
);
|
|
@@ -10207,15 +10997,15 @@ var runningChannelSecretHashes = /* @__PURE__ */ new Map();
|
|
|
10207
10997
|
var sessionLaunchManagedStructure = /* @__PURE__ */ new Map();
|
|
10208
10998
|
function projectMcpHash(_codeName, projectDir) {
|
|
10209
10999
|
try {
|
|
10210
|
-
const raw =
|
|
10211
|
-
return
|
|
11000
|
+
const raw = readFileSync26(join33(projectDir, ".mcp.json"), "utf-8");
|
|
11001
|
+
return createHash17("sha256").update(canonicalJson(JSON.parse(raw))).digest("hex");
|
|
10212
11002
|
} catch {
|
|
10213
11003
|
return null;
|
|
10214
11004
|
}
|
|
10215
11005
|
}
|
|
10216
11006
|
function projectMcpKeys(_codeName, projectDir) {
|
|
10217
11007
|
try {
|
|
10218
|
-
const raw =
|
|
11008
|
+
const raw = readFileSync26(join33(projectDir, ".mcp.json"), "utf-8");
|
|
10219
11009
|
const parsed = JSON.parse(raw);
|
|
10220
11010
|
const servers = parsed.mcpServers;
|
|
10221
11011
|
if (!servers || typeof servers !== "object") return /* @__PURE__ */ new Set();
|
|
@@ -10233,7 +11023,7 @@ function seedSessionLaunchBaselines(codeName, projectDir) {
|
|
|
10233
11023
|
else runningMcpServerKeys.delete(codeName);
|
|
10234
11024
|
let launchStructure = null;
|
|
10235
11025
|
try {
|
|
10236
|
-
const raw =
|
|
11026
|
+
const raw = readFileSync26(join33(projectDir, ".mcp.json"), "utf-8");
|
|
10237
11027
|
launchStructure = managedMcpStructureHashFromFile(
|
|
10238
11028
|
JSON.parse(raw),
|
|
10239
11029
|
isManagedMcpServerKey
|
|
@@ -10343,7 +11133,7 @@ async function runAgentSessionToolBindProbes(agent, integrations, projectDir, op
|
|
|
10343
11133
|
if (result && result.rebindCandidates.length > 0 && hostFlagStore().getBoolean("session-tool-rebind")) {
|
|
10344
11134
|
let mcpJsonForRebind = null;
|
|
10345
11135
|
try {
|
|
10346
|
-
mcpJsonForRebind = JSON.parse(
|
|
11136
|
+
mcpJsonForRebind = JSON.parse(readFileSync26(join33(projectDir, ".mcp.json"), "utf-8"));
|
|
10347
11137
|
} catch {
|
|
10348
11138
|
mcpJsonForRebind = null;
|
|
10349
11139
|
}
|
|
@@ -10487,7 +11277,7 @@ function checkMcpConfigDriftAndScheduleRestart(codeName, projectDir) {
|
|
|
10487
11277
|
function projectChannelSecretHash(projectDir) {
|
|
10488
11278
|
try {
|
|
10489
11279
|
const entries = parseEnvIntegrations(
|
|
10490
|
-
|
|
11280
|
+
readFileSync26(join33(projectDir, ".env.integrations"), "utf-8")
|
|
10491
11281
|
);
|
|
10492
11282
|
return channelSecretValueHash(entries, CHANNEL_SECRET_ENV_KEYS);
|
|
10493
11283
|
} catch {
|
|
@@ -10528,7 +11318,7 @@ var STALE_TASK_THRESHOLD_MS = (() => {
|
|
|
10528
11318
|
})();
|
|
10529
11319
|
var taskDisplayInfo = /* @__PURE__ */ new Map();
|
|
10530
11320
|
var activeChannels = /* @__PURE__ */ new Map();
|
|
10531
|
-
var
|
|
11321
|
+
var state7 = {
|
|
10532
11322
|
pid: process.pid,
|
|
10533
11323
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10534
11324
|
lastPollAt: null,
|
|
@@ -10583,7 +11373,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
10583
11373
|
var lastVersionCheckAt = 0;
|
|
10584
11374
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
10585
11375
|
var lastResponsivenessProbeAt = 0;
|
|
10586
|
-
var agtCliVersion = true ? "0.28.
|
|
11376
|
+
var agtCliVersion = true ? "0.28.566" : "dev";
|
|
10587
11377
|
function resolveBrewPath(execFileSync2) {
|
|
10588
11378
|
try {
|
|
10589
11379
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
|
@@ -10596,7 +11386,7 @@ function resolveBrewPath(execFileSync2) {
|
|
|
10596
11386
|
"/usr/local/bin/brew"
|
|
10597
11387
|
];
|
|
10598
11388
|
for (const path of fallbacks) {
|
|
10599
|
-
if (
|
|
11389
|
+
if (existsSync14(path)) return path;
|
|
10600
11390
|
}
|
|
10601
11391
|
return null;
|
|
10602
11392
|
}
|
|
@@ -10606,7 +11396,7 @@ function claudeBinaryInstalled(execFileSync2) {
|
|
|
10606
11396
|
"/opt/homebrew/bin/claude",
|
|
10607
11397
|
"/usr/local/bin/claude"
|
|
10608
11398
|
];
|
|
10609
|
-
if (canonical.some((path) =>
|
|
11399
|
+
if (canonical.some((path) => existsSync14(path))) return true;
|
|
10610
11400
|
try {
|
|
10611
11401
|
execFileSync2("which", ["claude"], { timeout: 5e3 });
|
|
10612
11402
|
return true;
|
|
@@ -10678,7 +11468,7 @@ async function ensureToolkitCli(toolkitSlug) {
|
|
|
10678
11468
|
toolkitCliEnsured.add(toolkitSlug);
|
|
10679
11469
|
return;
|
|
10680
11470
|
}
|
|
10681
|
-
brewBinDir =
|
|
11471
|
+
brewBinDir = dirname9(brewPath);
|
|
10682
11472
|
const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
|
|
10683
11473
|
log(`[toolkit-install] ${toolkitSlug}: installing via brew (${pkg})\u2026`);
|
|
10684
11474
|
if (isRoot) {
|
|
@@ -10915,8 +11705,8 @@ function claudeManagedSettingsPath() {
|
|
|
10915
11705
|
function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
10916
11706
|
try {
|
|
10917
11707
|
let settings = {};
|
|
10918
|
-
if (
|
|
10919
|
-
const raw =
|
|
11708
|
+
if (existsSync14(path)) {
|
|
11709
|
+
const raw = readFileSync26(path, "utf-8").trim();
|
|
10920
11710
|
if (raw) {
|
|
10921
11711
|
let parsed;
|
|
10922
11712
|
try {
|
|
@@ -10932,8 +11722,8 @@ function ensureClaudeManagedSettings(path = claudeManagedSettingsPath()) {
|
|
|
10932
11722
|
}
|
|
10933
11723
|
if (settings.channelsEnabled === true) return "ok";
|
|
10934
11724
|
settings.channelsEnabled = true;
|
|
10935
|
-
|
|
10936
|
-
|
|
11725
|
+
mkdirSync11(dirname9(path), { recursive: true });
|
|
11726
|
+
writeFileSync14(path, `${JSON.stringify(settings, null, 2)}
|
|
10937
11727
|
`);
|
|
10938
11728
|
log(`[managed-settings] set channelsEnabled:true in ${path} (ENG-5786 \u2014 unblocks Claude Code channels)`);
|
|
10939
11729
|
return "ok";
|
|
@@ -10971,7 +11761,7 @@ async function ensureOpencodeBinary() {
|
|
|
10971
11761
|
try {
|
|
10972
11762
|
const prefix = execFileSync2("npm", ["prefix", "-g"], { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
10973
11763
|
if (prefix) {
|
|
10974
|
-
const npmBin =
|
|
11764
|
+
const npmBin = join33(prefix, "bin");
|
|
10975
11765
|
const current = (process.env.PATH ?? "").split(pathDelimiter);
|
|
10976
11766
|
if (!current.includes(npmBin)) {
|
|
10977
11767
|
process.env.PATH = [npmBin, ...current.filter(Boolean)].join(pathDelimiter);
|
|
@@ -11028,11 +11818,11 @@ async function ensureFrameworkBinary(frameworkId) {
|
|
|
11028
11818
|
log(`Claude Code install failed: ${err.message}`);
|
|
11029
11819
|
return;
|
|
11030
11820
|
}
|
|
11031
|
-
const brewBinDir =
|
|
11821
|
+
const brewBinDir = dirname9(brewPath);
|
|
11032
11822
|
if (!process.env.PATH?.split(":").includes(brewBinDir)) {
|
|
11033
11823
|
process.env.PATH = `${brewBinDir}:${process.env.PATH ?? ""}`;
|
|
11034
11824
|
}
|
|
11035
|
-
if (
|
|
11825
|
+
if (existsSync14("/home/linuxbrew/.linuxbrew/bin/claude")) {
|
|
11036
11826
|
log("Claude Code installed successfully");
|
|
11037
11827
|
} else {
|
|
11038
11828
|
log("Claude Code install completed but binary not found at expected path \u2014 check brew logs");
|
|
@@ -11088,7 +11878,7 @@ ${r.stderr}`;
|
|
|
11088
11878
|
}
|
|
11089
11879
|
var UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
11090
11880
|
function selfUpdateAppliedMarkerPath() {
|
|
11091
|
-
return
|
|
11881
|
+
return join33(homedir15(), ".augmented", ".last-self-update-applied");
|
|
11092
11882
|
}
|
|
11093
11883
|
var selfUpdateUpToDateLogged = false;
|
|
11094
11884
|
var selfUpdatePinnedLogged = false;
|
|
@@ -11117,7 +11907,7 @@ async function checkAndUpdateCli(opts) {
|
|
|
11117
11907
|
const isNpmGlobal = !isBrewFormula && resolvedPath.includes("node_modules");
|
|
11118
11908
|
if (!isBrewFormula && !isNpmGlobal) return "noop";
|
|
11119
11909
|
const { readFileSync: readF, writeFileSync: writeF } = await import("fs");
|
|
11120
|
-
const markerPath =
|
|
11910
|
+
const markerPath = join33(homedir15(), ".augmented", ".last-update-check");
|
|
11121
11911
|
if (!force) {
|
|
11122
11912
|
try {
|
|
11123
11913
|
const lastCheck = parseInt(readF(markerPath, "utf-8").trim(), 10);
|
|
@@ -11523,7 +12313,7 @@ async function runClaudeRuntimeAuthProbe() {
|
|
|
11523
12313
|
];
|
|
11524
12314
|
try {
|
|
11525
12315
|
const { stdout, stderr } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
11526
|
-
cwd:
|
|
12316
|
+
cwd: homedir15(),
|
|
11527
12317
|
timeout: RUNTIME_AUTH_PROBE_TIMEOUT_MS,
|
|
11528
12318
|
stdin: "ignore",
|
|
11529
12319
|
env: childEnv,
|
|
@@ -11569,14 +12359,14 @@ async function checkClaudeAuth() {
|
|
|
11569
12359
|
}
|
|
11570
12360
|
var evalEmptyMcpConfigPath = null;
|
|
11571
12361
|
function ensureEvalEmptyMcpConfig() {
|
|
11572
|
-
if (evalEmptyMcpConfigPath &&
|
|
11573
|
-
const dir =
|
|
12362
|
+
if (evalEmptyMcpConfigPath && existsSync14(evalEmptyMcpConfigPath)) return evalEmptyMcpConfigPath;
|
|
12363
|
+
const dir = join33(homedir15(), ".augmented");
|
|
11574
12364
|
try {
|
|
11575
|
-
|
|
12365
|
+
mkdirSync11(dir, { recursive: true });
|
|
11576
12366
|
} catch {
|
|
11577
12367
|
}
|
|
11578
|
-
const p =
|
|
11579
|
-
|
|
12368
|
+
const p = join33(dir, ".eval-empty-mcp.json");
|
|
12369
|
+
writeFileSync14(p, JSON.stringify({ mcpServers: {} }));
|
|
11580
12370
|
evalEmptyMcpConfigPath = p;
|
|
11581
12371
|
return p;
|
|
11582
12372
|
}
|
|
@@ -11601,7 +12391,7 @@ async function runEvalClaude(prompt, model) {
|
|
|
11601
12391
|
""
|
|
11602
12392
|
];
|
|
11603
12393
|
const { stdout } = await execFilePromiseLong(resolveClaudeBinary(), args, {
|
|
11604
|
-
cwd:
|
|
12394
|
+
cwd: homedir15(),
|
|
11605
12395
|
timeout: 12e4,
|
|
11606
12396
|
stdin: "ignore",
|
|
11607
12397
|
env: childEnv,
|
|
@@ -11613,6 +12403,9 @@ async function runEvalClaude(prompt, model) {
|
|
|
11613
12403
|
function memoryExtractionEnabled() {
|
|
11614
12404
|
return hostFlagStore().getBoolean("memory-extraction");
|
|
11615
12405
|
}
|
|
12406
|
+
function toolCallAuditEnabled() {
|
|
12407
|
+
return hostFlagStore().getBoolean("tool-call-audit");
|
|
12408
|
+
}
|
|
11616
12409
|
function conversationScorerSuppressed() {
|
|
11617
12410
|
return meteredScorerSuppressed(
|
|
11618
12411
|
getCachedClaudeAuthMode(),
|
|
@@ -11667,10 +12460,10 @@ function resolveConversationEvalBackend() {
|
|
|
11667
12460
|
return conversationEvalBackend;
|
|
11668
12461
|
}
|
|
11669
12462
|
function getStateFile() {
|
|
11670
|
-
return
|
|
12463
|
+
return join33(config?.configDir ?? join33(process.env["HOME"] ?? "/tmp", ".augmented"), "manager-state.json");
|
|
11671
12464
|
}
|
|
11672
12465
|
function channelHashCacheDir() {
|
|
11673
|
-
return config?.configDir ??
|
|
12466
|
+
return config?.configDir ?? join33(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
11674
12467
|
}
|
|
11675
12468
|
function loadChannelHashCache2() {
|
|
11676
12469
|
loadChannelHashCache(agentState.knownChannelConfigHashes, channelHashCacheDir());
|
|
@@ -11724,7 +12517,7 @@ function removeDeliveryBaselineEntries(agentId) {
|
|
|
11724
12517
|
var _channelQuarantineStore = null;
|
|
11725
12518
|
function channelQuarantineStore() {
|
|
11726
12519
|
if (!_channelQuarantineStore) {
|
|
11727
|
-
const dir = config?.configDir ??
|
|
12520
|
+
const dir = config?.configDir ?? join33(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
11728
12521
|
_channelQuarantineStore = new ChannelQuarantineStore(defaultQuarantinePath(dir));
|
|
11729
12522
|
}
|
|
11730
12523
|
return _channelQuarantineStore;
|
|
@@ -11741,7 +12534,7 @@ function claudeMdSizeFor(codeName) {
|
|
|
11741
12534
|
var _hostFlagStore = null;
|
|
11742
12535
|
function hostFlagStore() {
|
|
11743
12536
|
if (!_hostFlagStore) {
|
|
11744
|
-
const dir = config?.configDir ??
|
|
12537
|
+
const dir = config?.configDir ?? join33(process.env["HOME"] ?? "/tmp", ".augmented");
|
|
11745
12538
|
_hostFlagStore = new HostFlagStore({ cachePath: defaultFlagsCachePath(dir), log });
|
|
11746
12539
|
}
|
|
11747
12540
|
return _hostFlagStore;
|
|
@@ -11814,13 +12607,13 @@ function parseSkillFrontmatter(content) {
|
|
|
11814
12607
|
return out;
|
|
11815
12608
|
}
|
|
11816
12609
|
async function refreshSkillsIndexInClaudeMd(configDir, codeName, log2) {
|
|
11817
|
-
const { readdirSync:
|
|
11818
|
-
const skillsDir =
|
|
11819
|
-
const claudeMdPath =
|
|
12610
|
+
const { readdirSync: readdirSync10, readFileSync: rfs, existsSync: ex, writeFileSync: writeFileSync15 } = await import("fs");
|
|
12611
|
+
const skillsDir = join33(configDir, codeName, "project", ".claude", "skills");
|
|
12612
|
+
const claudeMdPath = join33(configDir, codeName, "project", "CLAUDE.md");
|
|
11820
12613
|
if (!ex(skillsDir) || !ex(claudeMdPath)) return;
|
|
11821
12614
|
const entries = [];
|
|
11822
|
-
for (const dir of
|
|
11823
|
-
const skillFile =
|
|
12615
|
+
for (const dir of readdirSync10(skillsDir).sort()) {
|
|
12616
|
+
const skillFile = join33(skillsDir, dir, "SKILL.md");
|
|
11824
12617
|
if (!ex(skillFile)) continue;
|
|
11825
12618
|
try {
|
|
11826
12619
|
const { name, description } = parseSkillFrontmatter(rfs(skillFile, "utf-8"));
|
|
@@ -11864,7 +12657,7 @@ ${SKILLS_INDEX_END}`;
|
|
|
11864
12657
|
next = current.trimEnd() + "\n\n" + section + "\n";
|
|
11865
12658
|
}
|
|
11866
12659
|
if (next !== current) {
|
|
11867
|
-
|
|
12660
|
+
writeFileSync15(claudeMdPath, next, "utf-8");
|
|
11868
12661
|
log2(
|
|
11869
12662
|
injectSkillList ? `Refreshed skills index in CLAUDE.md for '${codeName}' (${entries.length} skills)` : `Refreshed CLAUDE.md managed block for '${codeName}' (skill list suppressed by claude-md-skills-index=false; ${entries.length} skills on disk)`
|
|
11870
12663
|
);
|
|
@@ -11887,7 +12680,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
|
|
|
11887
12680
|
if (codeNames.length === 0) return;
|
|
11888
12681
|
void (async () => {
|
|
11889
12682
|
try {
|
|
11890
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
12683
|
+
const { collectDiagnostics } = await import("../persistent-session-CI37H442.js");
|
|
11891
12684
|
await api.post("/host/heartbeat", {
|
|
11892
12685
|
host_id: hostId,
|
|
11893
12686
|
agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -11984,7 +12777,7 @@ async function pollCycle() {
|
|
|
11984
12777
|
const now = Date.now();
|
|
11985
12778
|
if (now - lastVersionCheckAt > VERSION_CHECK_INTERVAL_MS) {
|
|
11986
12779
|
try {
|
|
11987
|
-
const firstAgent =
|
|
12780
|
+
const firstAgent = state7.agents[0];
|
|
11988
12781
|
const versionAdapter = firstAgent ? resolveAgentFramework(firstAgent.codeName) : getFramework(DEFAULT_FRAMEWORK);
|
|
11989
12782
|
if (versionAdapter.getVersion) {
|
|
11990
12783
|
cachedFrameworkVersion = await versionAdapter.getVersion();
|
|
@@ -11995,7 +12788,7 @@ async function pollCycle() {
|
|
|
11995
12788
|
}
|
|
11996
12789
|
try {
|
|
11997
12790
|
const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
|
|
11998
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
12791
|
+
const { collectDiagnostics } = await import("../persistent-session-CI37H442.js");
|
|
11999
12792
|
const diagCodeNames = [...agentState.persistentSessionAgents];
|
|
12000
12793
|
const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor) : void 0;
|
|
12001
12794
|
let tailscaleHostname;
|
|
@@ -12025,10 +12818,10 @@ async function pollCycle() {
|
|
|
12025
12818
|
claudeAuth = await detectClaudeAuth();
|
|
12026
12819
|
} catch (err) {
|
|
12027
12820
|
const errText = err instanceof Error ? err.message : String(err);
|
|
12028
|
-
const errId =
|
|
12821
|
+
const errId = createHash17("sha256").update(errText).digest("hex").slice(0, 12);
|
|
12029
12822
|
log(`Claude auth detection failed (error_id=${errId})`);
|
|
12030
12823
|
}
|
|
12031
|
-
const hostHasClaudeCode =
|
|
12824
|
+
const hostHasClaudeCode = state7.agents.some(
|
|
12032
12825
|
(a) => agentFrameworkCache.get(a.codeName) === "claude-code"
|
|
12033
12826
|
);
|
|
12034
12827
|
if (hostHasClaudeCode) {
|
|
@@ -12078,7 +12871,7 @@ async function pollCycle() {
|
|
|
12078
12871
|
// ENG-6692: ack the last consumed "Update CLI now" timestamp so the API
|
|
12079
12872
|
// clears hosts.update_requested_at. Echoes our persisted dedup marker;
|
|
12080
12873
|
// null until we've ever consumed one.
|
|
12081
|
-
update_request_processed_at:
|
|
12874
|
+
update_request_processed_at: state7.lastUpdateRequestProcessedAt ?? null
|
|
12082
12875
|
});
|
|
12083
12876
|
if (hbResp?.maintenance_window) {
|
|
12084
12877
|
cachedMaintenanceWindow = hbResp.maintenance_window;
|
|
@@ -12118,7 +12911,7 @@ async function pollCycle() {
|
|
|
12118
12911
|
collectPanelessActivityProbes,
|
|
12119
12912
|
getResponsivenessIntervalMs,
|
|
12120
12913
|
occupancyQualificationClassifications
|
|
12121
|
-
} = await import("../responsiveness-probe-
|
|
12914
|
+
} = await import("../responsiveness-probe-O33QL25H.js");
|
|
12122
12915
|
const probeIntervalMs = getResponsivenessIntervalMs();
|
|
12123
12916
|
if (now - lastResponsivenessProbeAt > probeIntervalMs) {
|
|
12124
12917
|
const probeCodeNames = [...agentState.persistentSessionAgents];
|
|
@@ -12209,7 +13002,7 @@ async function pollCycle() {
|
|
|
12209
13002
|
collectResponsivenessProbes,
|
|
12210
13003
|
livePendingInboundOldestAgeSeconds,
|
|
12211
13004
|
parkPendingInbound
|
|
12212
|
-
} = await import("../responsiveness-probe-
|
|
13005
|
+
} = await import("../responsiveness-probe-O33QL25H.js");
|
|
12213
13006
|
const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
|
|
12214
13007
|
const wedgeNow = /* @__PURE__ */ new Date();
|
|
12215
13008
|
const liveAgents = agentState.persistentSessionAgents;
|
|
@@ -12298,13 +13091,13 @@ async function pollCycle() {
|
|
|
12298
13091
|
);
|
|
12299
13092
|
if (hostFlagStore().getBoolean("wedge-transient-notice")) {
|
|
12300
13093
|
try {
|
|
12301
|
-
const paneTail =
|
|
13094
|
+
const paneTail = readFileSync26(paneLogPath(codeName), "utf8").slice(-65536);
|
|
12302
13095
|
const transient = detectTransientApiErrorInLog(paneTail);
|
|
12303
13096
|
if (transient) {
|
|
12304
|
-
const wedgeHome =
|
|
12305
|
-
if (
|
|
13097
|
+
const wedgeHome = join33(homedir15(), ".augmented", codeName);
|
|
13098
|
+
if (existsSync14(wedgeHome)) {
|
|
12306
13099
|
atomicWriteFileSync(
|
|
12307
|
-
|
|
13100
|
+
join33(wedgeHome, "watchdog-give-up.json"),
|
|
12308
13101
|
JSON.stringify({
|
|
12309
13102
|
gave_up_at: wedgeNow.toISOString(),
|
|
12310
13103
|
reason: "transient_overload"
|
|
@@ -12434,7 +13227,7 @@ async function pollCycle() {
|
|
|
12434
13227
|
const requested = agent.restart_requested_at ?? null;
|
|
12435
13228
|
if (!requested) continue;
|
|
12436
13229
|
if (restartInFlight.has(agent.agent_id)) continue;
|
|
12437
|
-
const prev =
|
|
13230
|
+
const prev = state7.agents.find((a) => a.agentId === agent.agent_id);
|
|
12438
13231
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
12439
13232
|
const alreadyServiced = lastProcessed != null && Date.parse(lastProcessed) >= Date.parse(requested);
|
|
12440
13233
|
if (!alreadyServiced) {
|
|
@@ -12513,7 +13306,7 @@ async function pollCycle() {
|
|
|
12513
13306
|
const processOrder = fastRespawn ? reorderRestartedFirst(agents, new Set(restartAcks.keys())) : agents;
|
|
12514
13307
|
for (const agent of processOrder) {
|
|
12515
13308
|
if (restartInFlight.has(agent.agent_id)) {
|
|
12516
|
-
const existing =
|
|
13309
|
+
const existing = state7.agents.find((a) => a.agentId === agent.agent_id);
|
|
12517
13310
|
if (existing) {
|
|
12518
13311
|
agentStates.push(existing);
|
|
12519
13312
|
continue;
|
|
@@ -12523,7 +13316,7 @@ async function pollCycle() {
|
|
|
12523
13316
|
await processAgent(agent, agentStates);
|
|
12524
13317
|
} catch (err) {
|
|
12525
13318
|
log(`Error processing agent '${agent.code_name}': ${err.message}`);
|
|
12526
|
-
const existing =
|
|
13319
|
+
const existing = state7.agents.find((a) => a.agentId === agent.agent_id);
|
|
12527
13320
|
if (existing) {
|
|
12528
13321
|
agentStates.push(existing);
|
|
12529
13322
|
} else {
|
|
@@ -12552,12 +13345,12 @@ async function pollCycle() {
|
|
|
12552
13345
|
void maybeReportActivityCache({ api, log });
|
|
12553
13346
|
const restartAckStateChanged = applyRestartAcks({
|
|
12554
13347
|
agentStates,
|
|
12555
|
-
priorAgents:
|
|
13348
|
+
priorAgents: state7.agents,
|
|
12556
13349
|
restartAcks
|
|
12557
13350
|
});
|
|
12558
13351
|
if (restartAckStateChanged) {
|
|
12559
13352
|
try {
|
|
12560
|
-
const ackedState = { ...
|
|
13353
|
+
const ackedState = { ...state7, agents: agentStates };
|
|
12561
13354
|
atomicWriteFileSync(getStateFile(), JSON.stringify(ackedState, null, 2));
|
|
12562
13355
|
} catch (err) {
|
|
12563
13356
|
log(`[restart] failed to persist ack immediately: ${err.message}`);
|
|
@@ -12579,13 +13372,13 @@ async function pollCycle() {
|
|
|
12579
13372
|
} catch {
|
|
12580
13373
|
}
|
|
12581
13374
|
const currentIds = new Set(agents.map((a) => a.agent_id));
|
|
12582
|
-
for (const prev of
|
|
13375
|
+
for (const prev of state7.agents) {
|
|
12583
13376
|
if (!currentIds.has(prev.agentId)) {
|
|
12584
13377
|
log(`Agent '${prev.codeName}' removed from host (deleted or unassigned)`);
|
|
12585
13378
|
const adapter = resolveAgentFramework(prev.codeName);
|
|
12586
13379
|
stopAgentRuntime2(prev.codeName, "removed-from-host");
|
|
12587
13380
|
killAgentChannelProcesses(prev.codeName, { log });
|
|
12588
|
-
const agentDir =
|
|
13381
|
+
const agentDir = join33(adapter.getAgentDir(prev.codeName), "provision");
|
|
12589
13382
|
await cleanupAgentFiles(prev.codeName, agentDir);
|
|
12590
13383
|
clearAgentCaches(prev.agentId, prev.codeName);
|
|
12591
13384
|
}
|
|
@@ -12672,10 +13465,10 @@ async function pollCycle() {
|
|
|
12672
13465
|
// pending-inbound marker. Best-effort: a write failure is logged by
|
|
12673
13466
|
// the watchdog, never fails the poll cycle.
|
|
12674
13467
|
signalGiveUp: (codeName) => {
|
|
12675
|
-
const dir =
|
|
12676
|
-
if (!
|
|
13468
|
+
const dir = join33(homedir15(), ".augmented", codeName);
|
|
13469
|
+
if (!existsSync14(dir)) return;
|
|
12677
13470
|
atomicWriteFileSync(
|
|
12678
|
-
|
|
13471
|
+
join33(dir, "watchdog-give-up.json"),
|
|
12679
13472
|
JSON.stringify({ gave_up_at: (/* @__PURE__ */ new Date()).toISOString() })
|
|
12680
13473
|
);
|
|
12681
13474
|
}
|
|
@@ -12716,10 +13509,10 @@ async function pollCycle() {
|
|
|
12716
13509
|
}
|
|
12717
13510
|
} catch {
|
|
12718
13511
|
}
|
|
12719
|
-
|
|
12720
|
-
...
|
|
13512
|
+
state7 = {
|
|
13513
|
+
...state7,
|
|
12721
13514
|
lastPollAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12722
|
-
pollCount:
|
|
13515
|
+
pollCount: state7.pollCount + 1,
|
|
12723
13516
|
agents: agentStates,
|
|
12724
13517
|
// ENG-5441: serialise trip state on every poll so manager restarts
|
|
12725
13518
|
// never silently clear a tripped breaker. Cheap — only tripped
|
|
@@ -12733,9 +13526,9 @@ async function pollCycle() {
|
|
|
12733
13526
|
consecutivePollFailures = 0;
|
|
12734
13527
|
}
|
|
12735
13528
|
verifyPendingRestarts(Date.now());
|
|
12736
|
-
send({ type: "state-update", state:
|
|
13529
|
+
send({ type: "state-update", state: state7 });
|
|
12737
13530
|
} catch (err) {
|
|
12738
|
-
|
|
13531
|
+
state7.errorCount++;
|
|
12739
13532
|
const message = err.message;
|
|
12740
13533
|
log(`Poll error: ${message}`);
|
|
12741
13534
|
send({ type: "error", message });
|
|
@@ -12820,10 +13613,18 @@ async function processAgent(agent, agentStates) {
|
|
|
12820
13613
|
onModelCallError: (err) => reportManagerModelCallError("memory_extraction", err, memBackend.model ?? null)
|
|
12821
13614
|
});
|
|
12822
13615
|
}
|
|
13616
|
+
if (toolCallAuditEnabled()) {
|
|
13617
|
+
void maybeScanToolCalls({
|
|
13618
|
+
api,
|
|
13619
|
+
codeName: agent.code_name,
|
|
13620
|
+
agentId: agent.agent_id,
|
|
13621
|
+
log
|
|
13622
|
+
});
|
|
13623
|
+
}
|
|
12823
13624
|
}
|
|
12824
13625
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12825
13626
|
const adapter = resolveAgentFramework(agent.code_name);
|
|
12826
|
-
let agentDir =
|
|
13627
|
+
let agentDir = join33(adapter.getAgentDir(agent.code_name), "provision");
|
|
12827
13628
|
if (agent.status === "draft" || agent.status === "paused") {
|
|
12828
13629
|
if (previousKnownStatus !== agent.status) {
|
|
12829
13630
|
log(`Agent '${agent.code_name}' is ${agent.status}, skipping provisioning`);
|
|
@@ -12863,7 +13664,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12863
13664
|
const residuals = {
|
|
12864
13665
|
gatewayRunning: false,
|
|
12865
13666
|
portAllocated: false,
|
|
12866
|
-
provisionDirExists:
|
|
13667
|
+
provisionDirExists: existsSync14(agentDir)
|
|
12867
13668
|
};
|
|
12868
13669
|
if (!hasRevokedResiduals(residuals)) {
|
|
12869
13670
|
agentStates.push({
|
|
@@ -12920,11 +13721,11 @@ async function processAgent(agent, agentStates) {
|
|
|
12920
13721
|
const marker = getAutoResumeMarker(agent.code_name);
|
|
12921
13722
|
const isSelfResume = marker !== void 0 && Date.now() - marker.autoResumedAt < AUTO_RESUME_SELF_WINDOW_MS;
|
|
12922
13723
|
if (!isSelfResume && deleteAutoResumeMarker(agent.code_name)) {
|
|
12923
|
-
|
|
12924
|
-
...
|
|
13724
|
+
state7 = {
|
|
13725
|
+
...state7,
|
|
12925
13726
|
circuitBreakerAutoResumes: Object.fromEntries(autoResumeMarkers.entries())
|
|
12926
13727
|
};
|
|
12927
|
-
send({ type: "state-update", state:
|
|
13728
|
+
send({ type: "state-update", state: state7 });
|
|
12928
13729
|
log(`[auto-resume] Cleared auto-resume marker for '${agent.code_name}' on operator resume \u2014 credit re-armed (ENG-6088)`);
|
|
12929
13730
|
}
|
|
12930
13731
|
}
|
|
@@ -12955,7 +13756,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12955
13756
|
});
|
|
12956
13757
|
} catch (err) {
|
|
12957
13758
|
log(`Refresh failed for '${agent.code_name}': ${err.message}`);
|
|
12958
|
-
const existing =
|
|
13759
|
+
const existing = state7.agents.find((a) => a.agentId === agent.agent_id);
|
|
12959
13760
|
agentStates.push(existing ?? {
|
|
12960
13761
|
agentId: agent.agent_id,
|
|
12961
13762
|
codeName: agent.code_name,
|
|
@@ -12997,7 +13798,7 @@ async function processAgent(agent, agentStates) {
|
|
|
12997
13798
|
const frameworkId = refreshData.agent.framework ?? DEFAULT_FRAMEWORK;
|
|
12998
13799
|
agentFrameworkCache.set(agent.code_name, frameworkId);
|
|
12999
13800
|
const frameworkAdapter = getFramework(frameworkId);
|
|
13000
|
-
agentDir =
|
|
13801
|
+
agentDir = join33(frameworkAdapter.getAgentDir(agent.code_name), "provision");
|
|
13001
13802
|
cacheAgentDeliveryMetadata(agent.code_name, refreshData);
|
|
13002
13803
|
agentRestartTimezoneInputs.set(agent.code_name, {
|
|
13003
13804
|
agentTimezone: typeof refreshData.agent.timezone === "string" ? refreshData.agent.timezone : null,
|
|
@@ -13019,7 +13820,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13019
13820
|
const charterVersion = refreshData.charter.version;
|
|
13020
13821
|
const toolsVersion = refreshData.tools.version;
|
|
13021
13822
|
const known = agentState.knownVersions.get(agent.agent_id);
|
|
13022
|
-
let lastProvisionAt =
|
|
13823
|
+
let lastProvisionAt = state7.agents.find((a) => a.agentId === agent.agent_id)?.lastProvisionAt ?? null;
|
|
13023
13824
|
const quarantinedChannels = channelQuarantineStore().getQuarantinedKeys(agent.code_name);
|
|
13024
13825
|
const currentChannelIds = setWithout(
|
|
13025
13826
|
launchableChannelIds(refreshData.channel_configs),
|
|
@@ -13044,9 +13845,9 @@ async function processAgent(agent, agentStates) {
|
|
|
13044
13845
|
try {
|
|
13045
13846
|
const artifacts = generateArtifacts(agent, refreshData, frameworkAdapter, renderIntegrationsSection);
|
|
13046
13847
|
const changedFiles = [];
|
|
13047
|
-
|
|
13848
|
+
mkdirSync11(agentDir, { recursive: true });
|
|
13048
13849
|
for (const artifact of artifacts) {
|
|
13049
|
-
const filePath =
|
|
13850
|
+
const filePath = join33(agentDir, artifact.relativePath);
|
|
13050
13851
|
let existingHash;
|
|
13051
13852
|
let newHash;
|
|
13052
13853
|
let writeContent = artifact.content;
|
|
@@ -13065,8 +13866,8 @@ async function processAgent(agent, agentStates) {
|
|
|
13065
13866
|
};
|
|
13066
13867
|
newHash = sha256(stripDynamicSections(artifact.content));
|
|
13067
13868
|
try {
|
|
13068
|
-
const projectClaudeMd =
|
|
13069
|
-
const existing =
|
|
13869
|
+
const projectClaudeMd = join33(config.configDir, agent.code_name, "project", "CLAUDE.md");
|
|
13870
|
+
const existing = readFileSync26(projectClaudeMd, "utf-8");
|
|
13070
13871
|
existingHash = sha256(stripDynamicSections(existing));
|
|
13071
13872
|
} catch {
|
|
13072
13873
|
existingHash = null;
|
|
@@ -13084,7 +13885,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13084
13885
|
const generatorKeys = Object.keys(generatorServers);
|
|
13085
13886
|
let existingRaw = "";
|
|
13086
13887
|
try {
|
|
13087
|
-
existingRaw =
|
|
13888
|
+
existingRaw = readFileSync26(filePath, "utf-8");
|
|
13088
13889
|
} catch {
|
|
13089
13890
|
}
|
|
13090
13891
|
const existingServers = parseMcp(existingRaw);
|
|
@@ -13100,7 +13901,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13100
13901
|
} else if (artifact.relativePath === "opencode.json") {
|
|
13101
13902
|
let existingRaw = null;
|
|
13102
13903
|
try {
|
|
13103
|
-
existingRaw =
|
|
13904
|
+
existingRaw = readFileSync26(filePath, "utf-8");
|
|
13104
13905
|
} catch {
|
|
13105
13906
|
}
|
|
13106
13907
|
const mergeResult = mergeOpencodeConfigArtifact(artifact.content, existingRaw);
|
|
@@ -13116,26 +13917,26 @@ async function processAgent(agent, agentStates) {
|
|
|
13116
13917
|
}
|
|
13117
13918
|
}
|
|
13118
13919
|
if (changedFiles.length > 0) {
|
|
13119
|
-
const isFirst = !
|
|
13920
|
+
const isFirst = !existsSync14(join33(agentDir, "CHARTER.md"));
|
|
13120
13921
|
const verb = isFirst ? "Provisioning" : "Updating";
|
|
13121
13922
|
const fileNames = changedFiles.map((f) => f.relativePath).join(", ");
|
|
13122
13923
|
log(`${verb} '${agent.code_name}': ${fileNames}`);
|
|
13123
13924
|
for (const file of changedFiles) {
|
|
13124
|
-
const filePath =
|
|
13125
|
-
|
|
13925
|
+
const filePath = join33(agentDir, file.relativePath);
|
|
13926
|
+
mkdirSync11(dirname9(filePath), { recursive: true });
|
|
13126
13927
|
if (file.relativePath === ".mcp.json") {
|
|
13127
13928
|
safeWriteJsonAtomic(filePath, file.content, { mode: 384 });
|
|
13128
13929
|
} else {
|
|
13129
|
-
|
|
13930
|
+
writeFileSync14(filePath, file.content);
|
|
13130
13931
|
}
|
|
13131
13932
|
}
|
|
13132
13933
|
try {
|
|
13133
|
-
const provSkillsDir =
|
|
13134
|
-
if (
|
|
13135
|
-
for (const folder of
|
|
13934
|
+
const provSkillsDir = join33(agentDir, ".claude", "skills");
|
|
13935
|
+
if (existsSync14(provSkillsDir)) {
|
|
13936
|
+
for (const folder of readdirSync9(provSkillsDir)) {
|
|
13136
13937
|
if (folder.startsWith("knowledge-")) {
|
|
13137
13938
|
try {
|
|
13138
|
-
rmSync5(
|
|
13939
|
+
rmSync5(join33(provSkillsDir, folder), { recursive: true });
|
|
13139
13940
|
} catch {
|
|
13140
13941
|
}
|
|
13141
13942
|
}
|
|
@@ -13148,7 +13949,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13148
13949
|
const trackedFiles2 = frameworkAdapter.driftTrackedFiles();
|
|
13149
13950
|
const hashes = /* @__PURE__ */ new Map();
|
|
13150
13951
|
for (const file of trackedFiles2) {
|
|
13151
|
-
const h = hashFile(
|
|
13952
|
+
const h = hashFile(join33(agentDir, file));
|
|
13152
13953
|
if (h) hashes.set(file, h);
|
|
13153
13954
|
}
|
|
13154
13955
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -13166,14 +13967,14 @@ async function processAgent(agent, agentStates) {
|
|
|
13166
13967
|
}
|
|
13167
13968
|
if (Array.isArray(refreshData.workflows)) {
|
|
13168
13969
|
try {
|
|
13169
|
-
const provWorkflowsDir =
|
|
13170
|
-
if (
|
|
13970
|
+
const provWorkflowsDir = join33(agentDir, ".claude", "workflows");
|
|
13971
|
+
if (existsSync14(provWorkflowsDir)) {
|
|
13171
13972
|
const expected = new Set(refreshData.workflows.map((w) => `${w.name}.js`));
|
|
13172
|
-
for (const file of
|
|
13973
|
+
for (const file of readdirSync9(provWorkflowsDir)) {
|
|
13173
13974
|
if (!file.endsWith(".js")) continue;
|
|
13174
13975
|
if (expected.has(file)) continue;
|
|
13175
13976
|
try {
|
|
13176
|
-
rmSync5(
|
|
13977
|
+
rmSync5(join33(provWorkflowsDir, file));
|
|
13177
13978
|
} catch {
|
|
13178
13979
|
}
|
|
13179
13980
|
}
|
|
@@ -13252,10 +14053,10 @@ async function processAgent(agent, agentStates) {
|
|
|
13252
14053
|
}
|
|
13253
14054
|
let lastDriftCheckAt = now;
|
|
13254
14055
|
const written = agentState.writtenHashes.get(agent.agent_id);
|
|
13255
|
-
if (written &&
|
|
14056
|
+
if (written && existsSync14(agentDir)) {
|
|
13256
14057
|
const driftedFiles = [];
|
|
13257
14058
|
for (const [file, expectedHash] of written) {
|
|
13258
|
-
const localHash = hashFile(
|
|
14059
|
+
const localHash = hashFile(join33(agentDir, file));
|
|
13259
14060
|
if (localHash && localHash !== expectedHash) {
|
|
13260
14061
|
driftedFiles.push(file);
|
|
13261
14062
|
}
|
|
@@ -13266,7 +14067,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13266
14067
|
try {
|
|
13267
14068
|
const localHashes = {};
|
|
13268
14069
|
for (const file of driftedFiles) {
|
|
13269
|
-
localHashes[file] = hashFile(
|
|
14070
|
+
localHashes[file] = hashFile(join33(agentDir, file));
|
|
13270
14071
|
}
|
|
13271
14072
|
await api.post("/host/drift", {
|
|
13272
14073
|
agent_id: agent.agent_id,
|
|
@@ -13468,15 +14269,15 @@ async function processAgent(agent, agentStates) {
|
|
|
13468
14269
|
const addedChannels = [...restartDecision.added];
|
|
13469
14270
|
const writeDmNoticeMarkers = isChannelAddRestart ? () => {
|
|
13470
14271
|
try {
|
|
13471
|
-
const agentAugmentedDir =
|
|
13472
|
-
|
|
14272
|
+
const agentAugmentedDir = join33(homedir15(), ".augmented", agent.code_name);
|
|
14273
|
+
mkdirSync11(agentAugmentedDir, { recursive: true });
|
|
13473
14274
|
const markerJson = JSON.stringify({
|
|
13474
14275
|
version: 1,
|
|
13475
14276
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13476
14277
|
added: addedChannels
|
|
13477
14278
|
});
|
|
13478
14279
|
for (const file of ["slack-channel-add-restart.json", "telegram-channel-add-restart.json"]) {
|
|
13479
|
-
atomicWriteFileSync(
|
|
14280
|
+
atomicWriteFileSync(join33(agentAugmentedDir, file), markerJson);
|
|
13480
14281
|
}
|
|
13481
14282
|
} catch (err) {
|
|
13482
14283
|
log(`[hot-reload] channel-add DM-notice marker write failed for '${agent.code_name}' (non-fatal): ${err.message}`);
|
|
@@ -13552,7 +14353,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13552
14353
|
const behaviourSubset = extractMsTeamsBehaviourSubset(
|
|
13553
14354
|
msteamsEntry?.config
|
|
13554
14355
|
);
|
|
13555
|
-
const behaviourHash =
|
|
14356
|
+
const behaviourHash = createHash17("sha256").update(canonicalJson(behaviourSubset)).digest("hex");
|
|
13556
14357
|
const prevBehaviourHash = agentState.knownMsTeamsBehaviourHashes.get(agent.agent_id);
|
|
13557
14358
|
const msteamsBehaviourRestrictive = isMsTeamsBehaviourRestrictive(behaviourSubset);
|
|
13558
14359
|
const behaviourDecision = decideSenderPolicyRestart({
|
|
@@ -13608,7 +14409,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13608
14409
|
const slackBehaviourSubset = extractSlackBehaviourSubset(
|
|
13609
14410
|
slackEntry?.config
|
|
13610
14411
|
);
|
|
13611
|
-
const slackBehaviourHash =
|
|
14412
|
+
const slackBehaviourHash = createHash17("sha256").update(canonicalJson(slackBehaviourSubset)).digest("hex");
|
|
13612
14413
|
const prevSlackBehaviourHash = agentState.knownSlackBehaviourHashes.get(agent.agent_id);
|
|
13613
14414
|
const slackBehaviourRestrictive = isSlackBehaviourRestrictive(slackBehaviourSubset);
|
|
13614
14415
|
const slackBehaviourDecision = decideSenderPolicyRestart({
|
|
@@ -13665,24 +14466,24 @@ async function processAgent(agent, agentStates) {
|
|
|
13665
14466
|
if (agentSessionMode === "persistent" && (agentFrameworkCache.get(agent.code_name) ?? DEFAULT_FRAMEWORK) === "claude-code") {
|
|
13666
14467
|
try {
|
|
13667
14468
|
const agentProvisionDir = agentDir;
|
|
13668
|
-
const projectDir =
|
|
13669
|
-
|
|
13670
|
-
|
|
13671
|
-
const provisionMcpPath =
|
|
13672
|
-
const projectMcpPath =
|
|
14469
|
+
const projectDir = join33(homedir15(), ".augmented", agent.code_name, "project");
|
|
14470
|
+
mkdirSync11(agentProvisionDir, { recursive: true });
|
|
14471
|
+
mkdirSync11(projectDir, { recursive: true });
|
|
14472
|
+
const provisionMcpPath = join33(agentProvisionDir, ".mcp.json");
|
|
14473
|
+
const projectMcpPath = join33(projectDir, ".mcp.json");
|
|
13673
14474
|
let mcpConfig = { mcpServers: {} };
|
|
13674
14475
|
try {
|
|
13675
|
-
mcpConfig = JSON.parse(
|
|
14476
|
+
mcpConfig = JSON.parse(readFileSync26(provisionMcpPath, "utf-8"));
|
|
13676
14477
|
if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
|
|
13677
14478
|
} catch {
|
|
13678
14479
|
}
|
|
13679
|
-
const localDirectChatChannel =
|
|
14480
|
+
const localDirectChatChannel = join33(homedir15(), ".augmented", "_mcp", "direct-chat-channel.js");
|
|
13680
14481
|
const directChatTeamSettings = refreshData.team?.settings;
|
|
13681
14482
|
const directChatTz = (() => {
|
|
13682
14483
|
const tz = directChatTeamSettings?.["timezone"];
|
|
13683
14484
|
return typeof tz === "string" && tz.trim() !== "" ? tz.trim() : void 0;
|
|
13684
14485
|
})();
|
|
13685
|
-
if (
|
|
14486
|
+
if (existsSync14(localDirectChatChannel)) {
|
|
13686
14487
|
const directChatEnv = {
|
|
13687
14488
|
AGT_HOST: requireHost(),
|
|
13688
14489
|
// ENG-5901 Track D: templated — the manager exports the real
|
|
@@ -13702,7 +14503,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13702
14503
|
// ~/.augmented/<codeName>/.current-turn-initiator.json. Note getAgentDir
|
|
13703
14504
|
// returns the agent root (NOT the /provision subdir `agentDir` points at),
|
|
13704
14505
|
// so it byte-matches the broker readers' path.
|
|
13705
|
-
AGT_TURN_INITIATOR_FILE:
|
|
14506
|
+
AGT_TURN_INITIATOR_FILE: join33(
|
|
13706
14507
|
frameworkAdapter.getAgentDir(agent.code_name),
|
|
13707
14508
|
".current-turn-initiator.json"
|
|
13708
14509
|
)
|
|
@@ -13722,8 +14523,8 @@ async function processAgent(agent, agentStates) {
|
|
|
13722
14523
|
log(`Channel credentials written for '${agent.code_name}/direct-chat'`);
|
|
13723
14524
|
}
|
|
13724
14525
|
}
|
|
13725
|
-
const staleChannelsPath =
|
|
13726
|
-
if (
|
|
14526
|
+
const staleChannelsPath = join33(projectDir, ".mcp-channels.json");
|
|
14527
|
+
if (existsSync14(staleChannelsPath)) {
|
|
13727
14528
|
try {
|
|
13728
14529
|
rmSync5(staleChannelsPath, { force: true });
|
|
13729
14530
|
} catch {
|
|
@@ -13733,7 +14534,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13733
14534
|
log(`Failed to provision direct-chat channel for '${agent.code_name}': ${err.message}`);
|
|
13734
14535
|
}
|
|
13735
14536
|
}
|
|
13736
|
-
let lastSecretsProvisionAt =
|
|
14537
|
+
let lastSecretsProvisionAt = state7.agents.find((a) => a.agentId === agent.agent_id)?.lastSecretsProvisionAt ?? null;
|
|
13737
14538
|
let secretsHash = agentState.knownSecretsHashes.get(agent.agent_id) ?? null;
|
|
13738
14539
|
try {
|
|
13739
14540
|
const secretsData = await api.post("/host/secrets", { agent_id: agent.agent_id });
|
|
@@ -13812,7 +14613,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13812
14613
|
}
|
|
13813
14614
|
if (hostFlagStore().getBoolean("connectivity-probe")) {
|
|
13814
14615
|
try {
|
|
13815
|
-
const probeProjectDir =
|
|
14616
|
+
const probeProjectDir = join33(homedir15(), ".augmented", agent.code_name, "project");
|
|
13816
14617
|
let probeSet = integrations;
|
|
13817
14618
|
try {
|
|
13818
14619
|
const quarantined = await api.post("/host/agent-integrations/quarantined", { agent_id: agent.agent_id });
|
|
@@ -13858,7 +14659,7 @@ async function processAgent(agent, agentStates) {
|
|
|
13858
14659
|
const forceDue = attemptsLeft > 0;
|
|
13859
14660
|
let probeRan = false;
|
|
13860
14661
|
try {
|
|
13861
|
-
const probeProjectDir =
|
|
14662
|
+
const probeProjectDir = join33(homedir15(), ".augmented", agent.code_name, "project");
|
|
13862
14663
|
probeRan = await runAgentSessionToolBindProbes(agent, integrations, probeProjectDir, { forceDue });
|
|
13863
14664
|
} catch (err) {
|
|
13864
14665
|
log(`Session-tool-bind probe failed for '${agent.code_name}': ${err.message}`);
|
|
@@ -13935,11 +14736,11 @@ async function processAgent(agent, agentStates) {
|
|
|
13935
14736
|
const intHash = computeIntegrationsHash(integrations);
|
|
13936
14737
|
const prevIntHash = agentState.knownIntegrationHashes.get(agent.agent_id);
|
|
13937
14738
|
if (intHash !== prevIntHash) {
|
|
13938
|
-
const projectDir =
|
|
13939
|
-
const envIntPath =
|
|
14739
|
+
const projectDir = join33(homedir15(), ".augmented", agent.code_name, "project");
|
|
14740
|
+
const envIntPath = join33(projectDir, ".env.integrations");
|
|
13940
14741
|
let preWriteEnv;
|
|
13941
14742
|
try {
|
|
13942
|
-
preWriteEnv =
|
|
14743
|
+
preWriteEnv = readFileSync26(envIntPath, "utf-8");
|
|
13943
14744
|
} catch {
|
|
13944
14745
|
preWriteEnv = void 0;
|
|
13945
14746
|
}
|
|
@@ -13958,9 +14759,9 @@ async function processAgent(agent, agentStates) {
|
|
|
13958
14759
|
}
|
|
13959
14760
|
if (fw === "claude-code" && isSessionHealthy(agent.code_name)) {
|
|
13960
14761
|
try {
|
|
13961
|
-
const projectMcpPath =
|
|
13962
|
-
const postWriteEnv =
|
|
13963
|
-
const mcpContent =
|
|
14762
|
+
const projectMcpPath = join33(projectDir, ".mcp.json");
|
|
14763
|
+
const postWriteEnv = readFileSync26(envIntPath, "utf-8");
|
|
14764
|
+
const mcpContent = readFileSync26(projectMcpPath, "utf-8");
|
|
13964
14765
|
const changedVars = diffEnvIntegrations(preWriteEnv, postWriteEnv);
|
|
13965
14766
|
const mcpJsonForReap = JSON.parse(mcpContent);
|
|
13966
14767
|
const affectedServerKeys = findMcpServersUsingVars(mcpJsonForReap, changedVars);
|
|
@@ -14051,10 +14852,10 @@ async function processAgent(agent, agentStates) {
|
|
|
14051
14852
|
desiredEntries.push({ serverId, url, headers: mcpHeaders, name: tk.toolkit_name });
|
|
14052
14853
|
}
|
|
14053
14854
|
const hashBasis = desiredEntries.slice().sort((a, b) => a.serverId.localeCompare(b.serverId)).map((e) => {
|
|
14054
|
-
const headersHash =
|
|
14855
|
+
const headersHash = createHash17("sha256").update(canonicalJson(e.headers ?? {})).digest("hex").slice(0, 16);
|
|
14055
14856
|
return `${e.serverId}|${e.url}|${headersHash}`;
|
|
14056
14857
|
}).join("\n");
|
|
14057
|
-
const mcpHash =
|
|
14858
|
+
const mcpHash = createHash17("sha256").update(hashBasis).digest("hex").slice(0, 16);
|
|
14058
14859
|
const prevMcpHash = agentState.knownManagedMcpHashes.get(agent.agent_id);
|
|
14059
14860
|
const structureHash = managedMcpStructureHash(desiredEntries);
|
|
14060
14861
|
const prevStructureHash = agentState.knownManagedMcpStructure.get(agent.agent_id);
|
|
@@ -14069,7 +14870,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14069
14870
|
if (mcpHash !== prevMcpHash) {
|
|
14070
14871
|
for (const e of desiredEntries) {
|
|
14071
14872
|
frameworkAdapter.writeMcpServer(agent.code_name, e.serverId, { url: e.url, headers: e.headers });
|
|
14072
|
-
const urlHash =
|
|
14873
|
+
const urlHash = createHash17("sha256").update(e.url).digest("hex").slice(0, 12);
|
|
14073
14874
|
log(`[managed-toolkit] ${agent.code_name}: wrote '${e.name}' (serverId=${e.serverId}, url_hash=${urlHash})`);
|
|
14074
14875
|
}
|
|
14075
14876
|
if (frameworkAdapter.removeMcpServer && frameworkAdapter.readMcpServers) {
|
|
@@ -14171,7 +14972,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14171
14972
|
if (frameworkAdapter.installSkillFiles) {
|
|
14172
14973
|
const currentIntegrationSkillIds = /* @__PURE__ */ new Set();
|
|
14173
14974
|
const installedIntegrationSkills = [];
|
|
14174
|
-
const { createHash:
|
|
14975
|
+
const { createHash: createHash18 } = await import("crypto");
|
|
14175
14976
|
const refreshAny = refreshData;
|
|
14176
14977
|
const contexts = refreshAny.integration_contexts ?? refreshAny.plugin_contexts ?? [];
|
|
14177
14978
|
const contextBySlug = /* @__PURE__ */ new Map();
|
|
@@ -14200,7 +15001,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14200
15001
|
)
|
|
14201
15002
|
}));
|
|
14202
15003
|
const bundle = buildIntegrationBundle(renderedScopes);
|
|
14203
|
-
const contentHash =
|
|
15004
|
+
const contentHash = createHash18("sha256").update(bundleFingerprint(bundle.files)).digest("hex").slice(0, 12);
|
|
14204
15005
|
if (!shouldWriteIntegrationSkill(
|
|
14205
15006
|
agentState.knownSkillHashes,
|
|
14206
15007
|
agent.agent_id,
|
|
@@ -14223,23 +15024,23 @@ async function processAgent(agent, agentStates) {
|
|
|
14223
15024
|
}
|
|
14224
15025
|
}
|
|
14225
15026
|
try {
|
|
14226
|
-
const { readdirSync:
|
|
14227
|
-
const { homedir:
|
|
15027
|
+
const { readdirSync: readdirSync10, rmSync: rmSync6 } = await import("fs");
|
|
15028
|
+
const { homedir: homedir16 } = await import("os");
|
|
14228
15029
|
const frameworkId2 = frameworkAdapter.id;
|
|
14229
15030
|
const candidateSkillDirs = [
|
|
14230
15031
|
// Claude Code — framework runtime tree
|
|
14231
|
-
|
|
15032
|
+
join33(homedir16(), ".augmented", agent.code_name, "skills"),
|
|
14232
15033
|
// Claude Code — project tree
|
|
14233
|
-
|
|
15034
|
+
join33(homedir16(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
14234
15035
|
// Defensive: legacy provision-side path, not currently an
|
|
14235
15036
|
// install target but cheap to sweep.
|
|
14236
|
-
|
|
15037
|
+
join33(agentDir, ".claude", "skills")
|
|
14237
15038
|
];
|
|
14238
|
-
const existingDirs = candidateSkillDirs.filter((d) =>
|
|
15039
|
+
const existingDirs = candidateSkillDirs.filter((d) => existsSync14(d));
|
|
14239
15040
|
const discoveredEntries = /* @__PURE__ */ new Set();
|
|
14240
15041
|
for (const dir of existingDirs) {
|
|
14241
15042
|
try {
|
|
14242
|
-
for (const entry of
|
|
15043
|
+
for (const entry of readdirSync10(dir)) {
|
|
14243
15044
|
if (entry.startsWith("plugin-") || entry.startsWith("integration-")) {
|
|
14244
15045
|
discoveredEntries.add(entry);
|
|
14245
15046
|
}
|
|
@@ -14254,7 +15055,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14254
15055
|
entry,
|
|
14255
15056
|
dirs: existingDirs,
|
|
14256
15057
|
removeDir: (p) => {
|
|
14257
|
-
if (
|
|
15058
|
+
if (existsSync14(p)) {
|
|
14258
15059
|
rmSync6(p, { recursive: true, force: true });
|
|
14259
15060
|
}
|
|
14260
15061
|
}
|
|
@@ -14274,7 +15075,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14274
15075
|
const sharedSkillsPayload = refreshAny.shared_skills;
|
|
14275
15076
|
const desiredResolved = globalSkillsPayload !== void 0 || sharedSkillsPayload !== void 0;
|
|
14276
15077
|
const manifestPath = managedSkillManifestPath(
|
|
14277
|
-
|
|
15078
|
+
join33(homedir15(), ".augmented", agent.code_name)
|
|
14278
15079
|
);
|
|
14279
15080
|
const prevIds = /* @__PURE__ */ new Set([
|
|
14280
15081
|
...agentState.knownGlobalSkillIds.get(agent.agent_id) ?? /* @__PURE__ */ new Set(),
|
|
@@ -14283,7 +15084,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14283
15084
|
const plan = planGlobalSkillSync(
|
|
14284
15085
|
[...globalSkillsPayload ?? [], ...sharedSkillsPayload ?? []],
|
|
14285
15086
|
prevIds,
|
|
14286
|
-
(content) =>
|
|
15087
|
+
(content) => createHash18("sha256").update(content).digest("hex").slice(0, 12),
|
|
14287
15088
|
(skillId) => agentState.knownSkillHashes.get(`global-skill:${agent.agent_id}:${skillId}`),
|
|
14288
15089
|
{ desiredResolved }
|
|
14289
15090
|
);
|
|
@@ -14294,15 +15095,15 @@ async function processAgent(agent, agentStates) {
|
|
|
14294
15095
|
}
|
|
14295
15096
|
if (plan.removes.length) {
|
|
14296
15097
|
const globalSkillDirs = [
|
|
14297
|
-
|
|
14298
|
-
|
|
14299
|
-
|
|
15098
|
+
join33(homedir15(), ".augmented", agent.code_name, "skills"),
|
|
15099
|
+
join33(homedir15(), ".augmented", agent.code_name, "project", ".claude", "skills"),
|
|
15100
|
+
join33(agentDir, ".claude", "skills")
|
|
14300
15101
|
];
|
|
14301
15102
|
for (const id of plan.removes) {
|
|
14302
15103
|
let prunedAny = false;
|
|
14303
15104
|
for (const dir of globalSkillDirs) {
|
|
14304
|
-
const p =
|
|
14305
|
-
if (
|
|
15105
|
+
const p = join33(dir, id);
|
|
15106
|
+
if (existsSync14(p) && existsSync14(join33(p, "SKILL.md"))) {
|
|
14306
15107
|
rmSync5(p, { recursive: true, force: true });
|
|
14307
15108
|
prunedAny = true;
|
|
14308
15109
|
}
|
|
@@ -14334,7 +15135,7 @@ async function processAgent(agent, agentStates) {
|
|
|
14334
15135
|
const slug = hook.integration_slug ?? hook.plugin_slug;
|
|
14335
15136
|
if (!slug) continue;
|
|
14336
15137
|
try {
|
|
14337
|
-
const scriptHash =
|
|
15138
|
+
const scriptHash = createHash18("sha256").update(hook.script).digest("hex").slice(0, 12);
|
|
14338
15139
|
const hookKey = `${agent.agent_id}:${frameworkAdapter.id}:plugin-hook:${slug}:on_install`;
|
|
14339
15140
|
if (agentState.knownSkillHashes.get(hookKey) === scriptHash) continue;
|
|
14340
15141
|
const result = await frameworkAdapter.executePluginHook({
|
|
@@ -14349,9 +15150,9 @@ async function processAgent(agent, agentStates) {
|
|
|
14349
15150
|
} else if (result.timedOut) {
|
|
14350
15151
|
log(`Integration hook on_install '${slug}' TIMED OUT for '${agent.code_name}' after ${result.durationMs}ms`);
|
|
14351
15152
|
} else {
|
|
14352
|
-
const stderrHash =
|
|
15153
|
+
const stderrHash = createHash18("sha256").update(result.stderr).digest("hex").slice(0, 12);
|
|
14353
15154
|
const missingCmd = result.exitCode === 127 ? extractCommandNotFound(result.stderr) : null;
|
|
14354
|
-
const missingCmdHash = missingCmd ?
|
|
15155
|
+
const missingCmdHash = missingCmd ? createHash18("sha256").update(missingCmd).digest("hex").slice(0, 8) : null;
|
|
14355
15156
|
log(
|
|
14356
15157
|
`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}]`
|
|
14357
15158
|
);
|
|
@@ -14537,8 +15338,8 @@ async function processAgent(agent, agentStates) {
|
|
|
14537
15338
|
const sess = getSessionState(agent.code_name);
|
|
14538
15339
|
let mcpJsonParsed = null;
|
|
14539
15340
|
try {
|
|
14540
|
-
const mcpPath =
|
|
14541
|
-
mcpJsonParsed = JSON.parse(
|
|
15341
|
+
const mcpPath = join33(getProjectDir(agent.code_name), ".mcp.json");
|
|
15342
|
+
mcpJsonParsed = JSON.parse(readFileSync26(mcpPath, "utf-8"));
|
|
14542
15343
|
} catch {
|
|
14543
15344
|
}
|
|
14544
15345
|
reapMissingMcpSessions({
|
|
@@ -14968,10 +15769,10 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
14968
15769
|
}
|
|
14969
15770
|
}
|
|
14970
15771
|
const trackedFiles = frameworkAdapter.driftTrackedFiles();
|
|
14971
|
-
if (trackedFiles.length > 0 &&
|
|
15772
|
+
if (trackedFiles.length > 0 && existsSync14(agentDir)) {
|
|
14972
15773
|
const hashes = /* @__PURE__ */ new Map();
|
|
14973
15774
|
for (const file of trackedFiles) {
|
|
14974
|
-
const h = hashFile(
|
|
15775
|
+
const h = hashFile(join33(agentDir, file));
|
|
14975
15776
|
if (h) hashes.set(file, h);
|
|
14976
15777
|
}
|
|
14977
15778
|
agentState.writtenHashes.set(agent.agent_id, hashes);
|
|
@@ -14986,7 +15787,7 @@ Retried to the limit after repeated stalls \u2014 needs a look.`).catch(() => {
|
|
|
14986
15787
|
refreshData.agent.onboarding_state
|
|
14987
15788
|
);
|
|
14988
15789
|
const obStep = obState.step;
|
|
14989
|
-
const markerPath =
|
|
15790
|
+
const markerPath = join33(homedir15(), ".augmented", agent.code_name, "onboarding-drive.json");
|
|
14990
15791
|
const marker = readOnboardingDriveMarker(markerPath);
|
|
14991
15792
|
const decision = decideOnboardingDrive(obStep, marker, Date.now(), obState.generation ?? 0);
|
|
14992
15793
|
if (decision.clearMarker) {
|
|
@@ -15074,7 +15875,7 @@ async function ensureOpencodeRuntime(agent, refreshData, agentTimezone) {
|
|
|
15074
15875
|
}
|
|
15075
15876
|
stopOpencodeSlackIngest(codeName, log);
|
|
15076
15877
|
stopOpencodeTelegramIngest(codeName, log);
|
|
15077
|
-
const opencodeProjectDir =
|
|
15878
|
+
const opencodeProjectDir = join33(getFramework("opencode").getAgentDir(codeName), "provision");
|
|
15078
15879
|
const serveEnv = {
|
|
15079
15880
|
AGT_HOST: requireHost(),
|
|
15080
15881
|
AGT_API_KEY: getApiKey() ?? void 0,
|
|
@@ -15129,8 +15930,8 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
15129
15930
|
});
|
|
15130
15931
|
}
|
|
15131
15932
|
const projectDir = getProjectDir(codeName);
|
|
15132
|
-
const mcpConfigPath =
|
|
15133
|
-
const claudeMdPath =
|
|
15933
|
+
const mcpConfigPath = join33(projectDir, ".mcp.json");
|
|
15934
|
+
const claudeMdPath = join33(projectDir, "CLAUDE.md");
|
|
15134
15935
|
if (restartBreaker.isTripped(codeName)) {
|
|
15135
15936
|
const trip = restartBreaker.getTrip(codeName);
|
|
15136
15937
|
return {
|
|
@@ -15341,7 +16142,7 @@ async function ensurePersistentSession(agent, tasks, boardItems, refreshData) {
|
|
|
15341
16142
|
const ctx = getLastFailureContext(codeName);
|
|
15342
16143
|
const recovery = prepareForRespawn(codeName);
|
|
15343
16144
|
const tailSummary = !ctx.tail ? "" : KNOWN_SAFE_TAIL_SIGNATURES.has(ctx.signature) ? `; last pane output (${PANE_TAIL_PREVIEW_LINES} of ~20 lines):
|
|
15344
|
-
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${
|
|
16145
|
+
${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash17("sha256").update(ctx.tail).digest("hex").slice(0, 12)} (raw at ~/.augmented/${codeName}/pane.log)`;
|
|
15345
16146
|
const sigSummary = ctx.signature !== "unknown" ? `; signature=${ctx.signature}` : "";
|
|
15346
16147
|
const recoverySummary = recovery ? `; recovery=${recovery}` : "";
|
|
15347
16148
|
log(
|
|
@@ -15355,7 +16156,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash16("sha256")
|
|
|
15355
16156
|
);
|
|
15356
16157
|
getHostId().then((hostId) => {
|
|
15357
16158
|
if (!hostId) return;
|
|
15358
|
-
const paneTailHash = zombie.paneTail ? `sha256:${
|
|
16159
|
+
const paneTailHash = zombie.paneTail ? `sha256:${createHash17("sha256").update(zombie.paneTail).digest("hex").slice(0, 12)}` : null;
|
|
15359
16160
|
return api.post("/host/events", {
|
|
15360
16161
|
host_id: hostId,
|
|
15361
16162
|
agent_code_name: codeName,
|
|
@@ -15510,7 +16311,7 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash16("sha256")
|
|
|
15510
16311
|
if (!claudeAuthTupleBySession.has(codeName)) {
|
|
15511
16312
|
claudeAuthTupleBySession.set(codeName, currentAuthTuple);
|
|
15512
16313
|
}
|
|
15513
|
-
const stableTasksHash =
|
|
16314
|
+
const stableTasksHash = createHash17("sha256").update(JSON.stringify(tasks)).digest("hex").slice(0, 16);
|
|
15514
16315
|
const prevHash = agentState.knownTasksHashes.get(agent.agent_id);
|
|
15515
16316
|
if (stableTasksHash !== prevHash) {
|
|
15516
16317
|
const taskInputs = tasks.map((t) => buildSchedulerTaskInput(t));
|
|
@@ -15521,9 +16322,9 @@ ${truncateForLog(ctx.tail)}` : `; pane_tail_hash=sha256:${createHash16("sha256")
|
|
|
15521
16322
|
} else if (!claudeSchedulerStates.has(codeName)) {
|
|
15522
16323
|
claudeSchedulerStates.set(codeName, loadSchedulerState(codeName));
|
|
15523
16324
|
}
|
|
15524
|
-
const
|
|
15525
|
-
if (
|
|
15526
|
-
const ready = getReadyTasks(
|
|
16325
|
+
const state8 = claudeSchedulerStates.get(codeName);
|
|
16326
|
+
if (state8) {
|
|
16327
|
+
const ready = getReadyTasks(state8, inFlightClaudeTasks);
|
|
15527
16328
|
if (ready.length > 0) {
|
|
15528
16329
|
log(`[persistent-session] ${ready.length} ready task(s) for '${codeName}': ${ready.map((t) => `${t.name}(next=${t.nextFireAt ? new Date(t.nextFireAt).toISOString() : "null"})`).join(", ")}`);
|
|
15529
16330
|
}
|
|
@@ -15711,7 +16512,7 @@ function restartReasonStampsTiming(reason) {
|
|
|
15711
16512
|
return reason === "integration-change";
|
|
15712
16513
|
}
|
|
15713
16514
|
async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
15714
|
-
const prev =
|
|
16515
|
+
const prev = state7.agents.find((a) => a.agentId === agentId);
|
|
15715
16516
|
const codeName = prev?.codeName;
|
|
15716
16517
|
if (!codeName) return;
|
|
15717
16518
|
const lastProcessed = prev?.lastRestartProcessedAt ?? null;
|
|
@@ -15761,7 +16562,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
15761
16562
|
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}`));
|
|
15762
16563
|
void (async () => {
|
|
15763
16564
|
try {
|
|
15764
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
16565
|
+
const { collectDiagnostics } = await import("../persistent-session-CI37H442.js");
|
|
15765
16566
|
await api.post("/host/heartbeat", {
|
|
15766
16567
|
host_id: hostId,
|
|
15767
16568
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -15772,7 +16573,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
15772
16573
|
})();
|
|
15773
16574
|
prev.lastRestartProcessedAt = requestedAt;
|
|
15774
16575
|
try {
|
|
15775
|
-
atomicWriteFileSync(getStateFile(), JSON.stringify(
|
|
16576
|
+
atomicWriteFileSync(getStateFile(), JSON.stringify(state7, null, 2));
|
|
15776
16577
|
} catch (err) {
|
|
15777
16578
|
log(`[restart-lane] failed to persist ack for '${codeName}': ${err.message}`);
|
|
15778
16579
|
}
|
|
@@ -15782,7 +16583,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
|
|
|
15782
16583
|
}
|
|
15783
16584
|
}
|
|
15784
16585
|
async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
15785
|
-
const prev =
|
|
16586
|
+
const prev = state7.agents.find((a) => a.codeName === codeName);
|
|
15786
16587
|
if (!prev) return;
|
|
15787
16588
|
const agentId = prev.agentId;
|
|
15788
16589
|
if (restartInFlight.has(agentId)) return;
|
|
@@ -15811,7 +16612,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
|
|
|
15811
16612
|
}
|
|
15812
16613
|
try {
|
|
15813
16614
|
const hostId = await getHostId();
|
|
15814
|
-
const { collectDiagnostics } = await import("../persistent-session-
|
|
16615
|
+
const { collectDiagnostics } = await import("../persistent-session-CI37H442.js");
|
|
15815
16616
|
await api.post("/host/heartbeat", {
|
|
15816
16617
|
host_id: hostId,
|
|
15817
16618
|
agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor)
|
|
@@ -15919,7 +16720,7 @@ function ensureRealtimeKanbanStarted(agentStates) {
|
|
|
15919
16720
|
);
|
|
15920
16721
|
}
|
|
15921
16722
|
}).catch((err) => {
|
|
15922
|
-
const errId =
|
|
16723
|
+
const errId = createHash17("sha256").update(err instanceof Error ? err.message : String(err)).digest("hex").slice(0, 12);
|
|
15923
16724
|
log(
|
|
15924
16725
|
`[realtime] Work trigger enqueue threw for '${agent.codeName}' item_id=${item.id} error_id=${errId} \u2014 hybrid nudge will retry`
|
|
15925
16726
|
);
|
|
@@ -16073,7 +16874,7 @@ async function processDirectChatMessageOpencode(agent, msg) {
|
|
|
16073
16874
|
body
|
|
16074
16875
|
});
|
|
16075
16876
|
recordCursorAdvanceOutcome(
|
|
16076
|
-
|
|
16877
|
+
dirname9(paneLogPath(agent.codeName)),
|
|
16077
16878
|
"direct-chat-manager",
|
|
16078
16879
|
"reply",
|
|
16079
16880
|
verdict
|
|
@@ -16103,7 +16904,7 @@ async function processDirectChatMessageOpencode(agent, msg) {
|
|
|
16103
16904
|
} catch (err) {
|
|
16104
16905
|
if (!recorded) {
|
|
16105
16906
|
recordCursorAdvanceOutcome(
|
|
16106
|
-
|
|
16907
|
+
dirname9(paneLogPath(agent.codeName)),
|
|
16107
16908
|
"direct-chat-manager",
|
|
16108
16909
|
"reply",
|
|
16109
16910
|
{ outcome: "failed", error: err.message, expected: 1 }
|
|
@@ -16154,9 +16955,9 @@ async function processDirectChatMessage(agent, msg) {
|
|
|
16154
16955
|
const useDoorbell = hostFlagStore().getBoolean("direct-chat-doorbell") || isolationMode(agent.codeName) === "docker";
|
|
16155
16956
|
if (useDoorbell) {
|
|
16156
16957
|
try {
|
|
16157
|
-
const doorbell = directChatDoorbellPath(agent.agentId,
|
|
16158
|
-
|
|
16159
|
-
|
|
16958
|
+
const doorbell = directChatDoorbellPath(agent.agentId, homedir15());
|
|
16959
|
+
mkdirSync11(dirname9(doorbell), { recursive: true });
|
|
16960
|
+
writeFileSync14(doorbell, String(Date.now()));
|
|
16160
16961
|
log(`[direct-chat] Doorbell rung for '${agent.codeName}' (msg=${msg.id}) \u2014 in-session MCP will pull via the cursor`);
|
|
16161
16962
|
return;
|
|
16162
16963
|
} catch (err) {
|
|
@@ -16205,10 +17006,10 @@ function getKanbanNudgeState(codeName) {
|
|
|
16205
17006
|
function loadKanbanNudgeStateFromDisk() {
|
|
16206
17007
|
loadKanbanNudgeState(kanbanNudgeStateByCode, channelHashCacheDir());
|
|
16207
17008
|
}
|
|
16208
|
-
function setKanbanNudgeState(codeName,
|
|
17009
|
+
function setKanbanNudgeState(codeName, state8) {
|
|
16209
17010
|
const key = kanbanNudgeStateKey(codeName);
|
|
16210
17011
|
if (key !== codeName) kanbanNudgeStateByCode.delete(codeName);
|
|
16211
|
-
kanbanNudgeStateByCode.set(key,
|
|
17012
|
+
kanbanNudgeStateByCode.set(key, state8);
|
|
16212
17013
|
saveKanbanNudgeState(kanbanNudgeStateByCode, channelHashCacheDir());
|
|
16213
17014
|
}
|
|
16214
17015
|
function clearKanbanNudgeState(codeName) {
|
|
@@ -16283,9 +17084,9 @@ ${formatRunMarker(run_id)}` : KANBAN_CHECK_COMMAND;
|
|
|
16283
17084
|
}
|
|
16284
17085
|
if (run_id) openInjectedRunByCode.set(codeName, run_id);
|
|
16285
17086
|
try {
|
|
16286
|
-
const doorbell = directChatDoorbellPath(agentId,
|
|
16287
|
-
|
|
16288
|
-
|
|
17087
|
+
const doorbell = directChatDoorbellPath(agentId, homedir15());
|
|
17088
|
+
mkdirSync11(dirname9(doorbell), { recursive: true });
|
|
17089
|
+
writeFileSync14(doorbell, String(Date.now()));
|
|
16289
17090
|
} catch (err) {
|
|
16290
17091
|
log(`[kanban] doorbell ring failed for '${codeName}': ${err.message} (notice still queued)`);
|
|
16291
17092
|
}
|
|
@@ -16389,7 +17190,7 @@ async function processClaudePairSessions(agents) {
|
|
|
16389
17190
|
killPairSession,
|
|
16390
17191
|
pairTmuxSession,
|
|
16391
17192
|
finalizeClaudePairOnboarding
|
|
16392
|
-
} = await import("../claude-pair-runtime-
|
|
17193
|
+
} = await import("../claude-pair-runtime-6AOWAYHH.js");
|
|
16393
17194
|
for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
|
|
16394
17195
|
log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
|
|
16395
17196
|
const killed = await killPairSession(pairTmuxSession(pairId));
|
|
@@ -16643,8 +17444,8 @@ function parseMemoryFile(raw, fallbackName) {
|
|
|
16643
17444
|
};
|
|
16644
17445
|
}
|
|
16645
17446
|
async function syncMemories(agent, configDir, log2) {
|
|
16646
|
-
const projectDir =
|
|
16647
|
-
const memoryDir =
|
|
17447
|
+
const projectDir = join33(configDir, agent.code_name, "project");
|
|
17448
|
+
const memoryDir = join33(projectDir, "memory");
|
|
16648
17449
|
const isFreshSync = pendingFreshMemorySync.has(agent.agent_id);
|
|
16649
17450
|
if (isFreshSync) {
|
|
16650
17451
|
log2(`[memory-sync] Fresh-sync requested for '${agent.code_name}' \u2014 pulling DB first`);
|
|
@@ -16655,15 +17456,15 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
16655
17456
|
}
|
|
16656
17457
|
pendingFreshMemorySync.delete(agent.agent_id);
|
|
16657
17458
|
}
|
|
16658
|
-
if (
|
|
17459
|
+
if (existsSync14(memoryDir)) {
|
|
16659
17460
|
const prevHashes = memoryFileHashes.get(agent.agent_id) ?? /* @__PURE__ */ new Map();
|
|
16660
17461
|
const currentHashes = /* @__PURE__ */ new Map();
|
|
16661
17462
|
const changedMemories = [];
|
|
16662
|
-
for (const file of
|
|
17463
|
+
for (const file of readdirSync9(memoryDir)) {
|
|
16663
17464
|
if (!file.endsWith(".md")) continue;
|
|
16664
17465
|
try {
|
|
16665
|
-
const raw =
|
|
16666
|
-
const fileHash =
|
|
17466
|
+
const raw = readFileSync26(join33(memoryDir, file), "utf-8");
|
|
17467
|
+
const fileHash = createHash17("sha256").update(raw).digest("hex").slice(0, 16);
|
|
16667
17468
|
currentHashes.set(file, fileHash);
|
|
16668
17469
|
if (prevHashes.get(file) === fileHash) continue;
|
|
16669
17470
|
const parsed = parseMemoryFile(raw, file.replace(/\.md$/, ""));
|
|
@@ -16687,7 +17488,7 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
16687
17488
|
} catch (err) {
|
|
16688
17489
|
for (const mem of changedMemories) {
|
|
16689
17490
|
for (const [file] of currentHashes) {
|
|
16690
|
-
const parsed = parseMemoryFile(
|
|
17491
|
+
const parsed = parseMemoryFile(readFileSync26(join33(memoryDir, file), "utf-8"), file.replace(/\.md$/, ""));
|
|
16691
17492
|
if (parsed?.name === mem.name) currentHashes.delete(file);
|
|
16692
17493
|
}
|
|
16693
17494
|
}
|
|
@@ -16700,29 +17501,29 @@ async function syncMemories(agent, configDir, log2) {
|
|
|
16700
17501
|
}
|
|
16701
17502
|
}
|
|
16702
17503
|
async function downloadMemories(agent, memoryDir, log2, { force }) {
|
|
16703
|
-
const localFiles =
|
|
16704
|
-
const localListHash =
|
|
17504
|
+
const localFiles = existsSync14(memoryDir) ? readdirSync9(memoryDir).filter((f) => f.endsWith(".md")).sort() : [];
|
|
17505
|
+
const localListHash = createHash17("sha256").update(localFiles.join(",")).digest("hex").slice(0, 16);
|
|
16705
17506
|
const prevLocalHash = lastLocalFileHash.get(agent.agent_id);
|
|
16706
17507
|
const prevDownload = lastDownloadHash.get(agent.agent_id);
|
|
16707
17508
|
try {
|
|
16708
17509
|
const dbMemories = await api.post("/host/memories", {
|
|
16709
17510
|
agent_id: agent.agent_id
|
|
16710
17511
|
});
|
|
16711
|
-
const responseHash =
|
|
17512
|
+
const responseHash = createHash17("sha256").update(JSON.stringify(dbMemories.memories ?? [])).digest("hex").slice(0, 16);
|
|
16712
17513
|
if (!force && prevDownload && prevLocalHash === localListHash && lastDownloadHash.get(agent.agent_id) === responseHash) {
|
|
16713
17514
|
return true;
|
|
16714
17515
|
}
|
|
16715
17516
|
lastDownloadHash.set(agent.agent_id, responseHash);
|
|
16716
17517
|
lastLocalFileHash.set(agent.agent_id, localListHash);
|
|
16717
17518
|
if (dbMemories.memories?.length) {
|
|
16718
|
-
|
|
17519
|
+
mkdirSync11(memoryDir, { recursive: true });
|
|
16719
17520
|
let written = 0;
|
|
16720
17521
|
let overwritten = 0;
|
|
16721
17522
|
for (let i = 0; i < dbMemories.memories.length; i++) {
|
|
16722
17523
|
const mem = dbMemories.memories[i];
|
|
16723
17524
|
const rawSlug = mem.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "").slice(0, 60);
|
|
16724
17525
|
const slug = rawSlug || `memory-${i}`;
|
|
16725
|
-
const filePath =
|
|
17526
|
+
const filePath = join33(memoryDir, `${slug}.md`);
|
|
16726
17527
|
const desired = `---
|
|
16727
17528
|
name: ${JSON.stringify(mem.name)}
|
|
16728
17529
|
type: ${mem.type}
|
|
@@ -16731,23 +17532,23 @@ description: ${JSON.stringify(mem.content.slice(0, 200))}
|
|
|
16731
17532
|
|
|
16732
17533
|
${mem.content}
|
|
16733
17534
|
`;
|
|
16734
|
-
if (
|
|
17535
|
+
if (existsSync14(filePath)) {
|
|
16735
17536
|
let existing = "";
|
|
16736
17537
|
try {
|
|
16737
|
-
existing =
|
|
17538
|
+
existing = readFileSync26(filePath, "utf-8");
|
|
16738
17539
|
} catch {
|
|
16739
17540
|
}
|
|
16740
17541
|
if (existing === desired) continue;
|
|
16741
|
-
|
|
17542
|
+
writeFileSync14(filePath, desired);
|
|
16742
17543
|
overwritten++;
|
|
16743
17544
|
} else {
|
|
16744
|
-
|
|
17545
|
+
writeFileSync14(filePath, desired);
|
|
16745
17546
|
written++;
|
|
16746
17547
|
}
|
|
16747
17548
|
}
|
|
16748
17549
|
if (written > 0 || overwritten > 0) {
|
|
16749
|
-
const updatedFiles =
|
|
16750
|
-
lastLocalFileHash.set(agent.agent_id,
|
|
17550
|
+
const updatedFiles = readdirSync9(memoryDir).filter((f) => f.endsWith(".md")).sort();
|
|
17551
|
+
lastLocalFileHash.set(agent.agent_id, createHash17("sha256").update(updatedFiles.join(",")).digest("hex").slice(0, 16));
|
|
16751
17552
|
log2(`Memory download for '${agent.code_name}': wrote ${written} new, overwrote ${overwritten} stale`);
|
|
16752
17553
|
}
|
|
16753
17554
|
}
|
|
@@ -16758,7 +17559,7 @@ ${mem.content}
|
|
|
16758
17559
|
}
|
|
16759
17560
|
}
|
|
16760
17561
|
async function cleanupAgentFiles(codeName, agentDir) {
|
|
16761
|
-
if (
|
|
17562
|
+
if (existsSync14(agentDir)) {
|
|
16762
17563
|
try {
|
|
16763
17564
|
rmSync5(agentDir, { recursive: true, force: true });
|
|
16764
17565
|
log(`Removed provision directory for '${codeName}'`);
|
|
@@ -16821,7 +17622,7 @@ async function driveArtifactStreaming() {
|
|
|
16821
17622
|
}
|
|
16822
17623
|
async function driveArtifactStreamingInner() {
|
|
16823
17624
|
const liveIds = /* @__PURE__ */ new Set();
|
|
16824
|
-
for (const agent of
|
|
17625
|
+
for (const agent of state7.agents) {
|
|
16825
17626
|
if (!agent.agentId || !agent.codeName || agent.status !== "active") continue;
|
|
16826
17627
|
liveIds.add(agent.agentId);
|
|
16827
17628
|
let scanner = artifactScanners.get(agent.agentId);
|
|
@@ -16997,12 +17798,12 @@ function startManager(opts) {
|
|
|
16997
17798
|
config = opts;
|
|
16998
17799
|
try {
|
|
16999
17800
|
const stateFile = getStateFile();
|
|
17000
|
-
if (
|
|
17001
|
-
const raw =
|
|
17801
|
+
if (existsSync14(stateFile)) {
|
|
17802
|
+
const raw = readFileSync26(stateFile, "utf-8");
|
|
17002
17803
|
const parsed = JSON.parse(raw);
|
|
17003
17804
|
if (Array.isArray(parsed.agents)) {
|
|
17004
|
-
|
|
17005
|
-
log(`[startup] rehydrated ${
|
|
17805
|
+
state7.agents = parsed.agents;
|
|
17806
|
+
log(`[startup] rehydrated ${state7.agents.length} agent state(s) from ${stateFile}`);
|
|
17006
17807
|
}
|
|
17007
17808
|
if (parsed.circuitBreakerTrips && typeof parsed.circuitBreakerTrips === "object") {
|
|
17008
17809
|
restartBreaker.hydrate(parsed.circuitBreakerTrips);
|
|
@@ -17015,9 +17816,9 @@ function startManager(opts) {
|
|
|
17015
17816
|
if (n > 0) log(`[startup] rehydrated ${n} auto-resume marker(s) (ENG-6088)`);
|
|
17016
17817
|
}
|
|
17017
17818
|
if (typeof parsed.lastUpdateRequestProcessedAt === "string" || parsed.lastUpdateRequestProcessedAt === null) {
|
|
17018
|
-
|
|
17019
|
-
if (
|
|
17020
|
-
log(`[startup] rehydrated update-request ack at ${
|
|
17819
|
+
state7.lastUpdateRequestProcessedAt = parsed.lastUpdateRequestProcessedAt;
|
|
17820
|
+
if (state7.lastUpdateRequestProcessedAt) {
|
|
17821
|
+
log(`[startup] rehydrated update-request ack at ${state7.lastUpdateRequestProcessedAt} (ENG-6692)`);
|
|
17021
17822
|
}
|
|
17022
17823
|
}
|
|
17023
17824
|
}
|
|
@@ -17025,7 +17826,7 @@ function startManager(opts) {
|
|
|
17025
17826
|
log(`[startup] state rehydration failed (continuing with empty state): ${err.message}`);
|
|
17026
17827
|
}
|
|
17027
17828
|
log(
|
|
17028
|
-
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${
|
|
17829
|
+
`[startup] worker pid=${process.pid} ppid=${process.ppid} node=${process.version} log=${join33(homedir15(), ".augmented", "manager.log")}`
|
|
17029
17830
|
);
|
|
17030
17831
|
deployMcpAssets();
|
|
17031
17832
|
reapOrphanChannelMcps({ log });
|
|
@@ -17037,7 +17838,7 @@ function startManager(opts) {
|
|
|
17037
17838
|
}
|
|
17038
17839
|
try {
|
|
17039
17840
|
refreshSlackRestartContextHints(
|
|
17040
|
-
|
|
17841
|
+
state7.agents.map((a) => a.codeName),
|
|
17041
17842
|
{ log }
|
|
17042
17843
|
);
|
|
17043
17844
|
} catch (err) {
|
|
@@ -17054,7 +17855,7 @@ async function reapOrphanedClaudePids() {
|
|
|
17054
17855
|
const looksLikeClaude = (pid) => {
|
|
17055
17856
|
if (process.platform !== "linux") return true;
|
|
17056
17857
|
try {
|
|
17057
|
-
const comm =
|
|
17858
|
+
const comm = readFileSync26(`/proc/${pid}/comm`, "utf-8").trim().toLowerCase();
|
|
17058
17859
|
return comm.includes("claude");
|
|
17059
17860
|
} catch {
|
|
17060
17861
|
return false;
|
|
@@ -17151,18 +17952,18 @@ function restartRunningChannelMcps(basenames) {
|
|
|
17151
17952
|
}
|
|
17152
17953
|
}
|
|
17153
17954
|
function deployMcpAssets() {
|
|
17154
|
-
const targetDir =
|
|
17155
|
-
|
|
17156
|
-
const moduleDir =
|
|
17955
|
+
const targetDir = join33(homedir15(), ".augmented", "_mcp");
|
|
17956
|
+
mkdirSync11(targetDir, { recursive: true });
|
|
17957
|
+
const moduleDir = dirname9(fileURLToPath(import.meta.url));
|
|
17157
17958
|
let mcpSourceDir = "";
|
|
17158
17959
|
let dir = moduleDir;
|
|
17159
17960
|
for (let i = 0; i < 6; i++) {
|
|
17160
|
-
const candidate =
|
|
17161
|
-
if (
|
|
17961
|
+
const candidate = join33(dir, "dist", "mcp");
|
|
17962
|
+
if (existsSync14(join33(candidate, "index.js"))) {
|
|
17162
17963
|
mcpSourceDir = candidate;
|
|
17163
17964
|
break;
|
|
17164
17965
|
}
|
|
17165
|
-
const parent =
|
|
17966
|
+
const parent = dirname9(dir);
|
|
17166
17967
|
if (parent === dir) break;
|
|
17167
17968
|
dir = parent;
|
|
17168
17969
|
}
|
|
@@ -17173,8 +17974,8 @@ function deployMcpAssets() {
|
|
|
17173
17974
|
const changedBasenames = [];
|
|
17174
17975
|
const fileHash = (p) => {
|
|
17175
17976
|
try {
|
|
17176
|
-
if (!
|
|
17177
|
-
return
|
|
17977
|
+
if (!existsSync14(p)) return null;
|
|
17978
|
+
return createHash17("sha256").update(readFileSync26(p)).digest("hex");
|
|
17178
17979
|
} catch {
|
|
17179
17980
|
return null;
|
|
17180
17981
|
}
|
|
@@ -17245,9 +18046,9 @@ function deployMcpAssets() {
|
|
|
17245
18046
|
// needs restarting to pick up a token rotation.
|
|
17246
18047
|
"xero.js"
|
|
17247
18048
|
]) {
|
|
17248
|
-
const src =
|
|
17249
|
-
const dst =
|
|
17250
|
-
if (!
|
|
18049
|
+
const src = join33(mcpSourceDir, file);
|
|
18050
|
+
const dst = join33(targetDir, file);
|
|
18051
|
+
if (!existsSync14(src)) continue;
|
|
17251
18052
|
const before = fileHash(dst);
|
|
17252
18053
|
try {
|
|
17253
18054
|
copyFileSync(src, dst);
|
|
@@ -17264,23 +18065,23 @@ function deployMcpAssets() {
|
|
|
17264
18065
|
log(`[manager] Bundle(s) updated: ${changedBasenames.join(", ")} \u2014 signalling running instances to restart`);
|
|
17265
18066
|
restartRunningChannelMcps(changedBasenames);
|
|
17266
18067
|
}
|
|
17267
|
-
const localMcpPath =
|
|
18068
|
+
const localMcpPath = join33(targetDir, "index.js");
|
|
17268
18069
|
try {
|
|
17269
|
-
const agentsDir =
|
|
17270
|
-
if (
|
|
17271
|
-
for (const entry of
|
|
18070
|
+
const agentsDir = join33(homedir15(), ".augmented", "agents");
|
|
18071
|
+
if (existsSync14(agentsDir)) {
|
|
18072
|
+
for (const entry of readdirSync9(agentsDir, { withFileTypes: true })) {
|
|
17272
18073
|
if (!entry.isDirectory()) continue;
|
|
17273
18074
|
for (const subdir of ["provision", "project"]) {
|
|
17274
|
-
const mcpJsonPath =
|
|
18075
|
+
const mcpJsonPath = join33(agentsDir, entry.name, subdir, ".mcp.json");
|
|
17275
18076
|
try {
|
|
17276
|
-
const raw =
|
|
18077
|
+
const raw = readFileSync26(mcpJsonPath, "utf-8");
|
|
17277
18078
|
if (!raw.includes("@integrity-labs/augmented-mcp")) continue;
|
|
17278
18079
|
const mcpConfig = JSON.parse(raw);
|
|
17279
18080
|
const augServer = mcpConfig.mcpServers?.["augmented"];
|
|
17280
18081
|
if (!augServer) continue;
|
|
17281
18082
|
augServer.command = "node";
|
|
17282
18083
|
augServer.args = [localMcpPath];
|
|
17283
|
-
|
|
18084
|
+
writeFileSync14(mcpJsonPath, JSON.stringify(mcpConfig, null, 2));
|
|
17284
18085
|
log(`[manager] Patched ${entry.name}/${subdir}/.mcp.json: npx \u2192 node`);
|
|
17285
18086
|
} catch {
|
|
17286
18087
|
}
|