@integrity-labs/agt-cli 0.28.575 → 0.28.577
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.
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
safeWriteJsonAtomic,
|
|
54
54
|
setConfigHash,
|
|
55
55
|
tripClass
|
|
56
|
-
} from "../chunk-
|
|
56
|
+
} from "../chunk-FOMHYTFV.js";
|
|
57
57
|
import {
|
|
58
58
|
getProjectDir as getProjectDir2,
|
|
59
59
|
getReadyTasks,
|
|
@@ -11596,7 +11596,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
|
|
|
11596
11596
|
var lastVersionCheckAt = 0;
|
|
11597
11597
|
var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
|
|
11598
11598
|
var lastResponsivenessProbeAt = 0;
|
|
11599
|
-
var agtCliVersion = true ? "0.28.
|
|
11599
|
+
var agtCliVersion = true ? "0.28.577" : "dev";
|
|
11600
11600
|
function resolveBrewPath(execFileSync2) {
|
|
11601
11601
|
try {
|
|
11602
11602
|
const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
|
package/dist/mcp/index.js
CHANGED
|
@@ -6802,6 +6802,15 @@ var require_dist = __commonJS({
|
|
|
6802
6802
|
|
|
6803
6803
|
// src/index.ts
|
|
6804
6804
|
import { mkdtemp, writeFile } from "fs/promises";
|
|
6805
|
+
import {
|
|
6806
|
+
closeSync,
|
|
6807
|
+
constants as fsConstants,
|
|
6808
|
+
fstatSync,
|
|
6809
|
+
lstatSync,
|
|
6810
|
+
openSync,
|
|
6811
|
+
readSync,
|
|
6812
|
+
realpathSync
|
|
6813
|
+
} from "fs";
|
|
6805
6814
|
|
|
6806
6815
|
// src/kanban-add-result.ts
|
|
6807
6816
|
function kanbanAddResultText(data, submittedTitle, status) {
|
|
@@ -6818,8 +6827,134 @@ ${first.duplicate_hint}` : added;
|
|
|
6818
6827
|
}
|
|
6819
6828
|
|
|
6820
6829
|
// src/index.ts
|
|
6821
|
-
import { tmpdir } from "os";
|
|
6822
|
-
import { basename, join as join3 } from "path";
|
|
6830
|
+
import { homedir as homedir3, tmpdir } from "os";
|
|
6831
|
+
import { basename, isAbsolute, join as join3, resolve as pathResolve, sep } from "path";
|
|
6832
|
+
|
|
6833
|
+
// src/skill-body-path.ts
|
|
6834
|
+
var SKILL_BODY_PATH_TOOLS = /* @__PURE__ */ new Set(["skill_create", "skill_update"]);
|
|
6835
|
+
var SKILL_BODY_PATH_MAX_BYTES = 512 * 1024;
|
|
6836
|
+
function allowedSkillBodyRoots(agentDir, path) {
|
|
6837
|
+
return [path.join(agentDir, "project"), path.join(agentDir, "scratch")];
|
|
6838
|
+
}
|
|
6839
|
+
function assertNoSymlinkComponents(target, roots, path, fs) {
|
|
6840
|
+
const root = roots.find((r) => isContained(target, r, path.sep));
|
|
6841
|
+
if (!root) return "resolved outside the allowed roots";
|
|
6842
|
+
const rest = target.slice(root.length).split(path.sep).filter(Boolean);
|
|
6843
|
+
let current = root;
|
|
6844
|
+
for (const segment of rest) {
|
|
6845
|
+
current = path.join(current, segment);
|
|
6846
|
+
try {
|
|
6847
|
+
if (fs.lstatSync(current).isSymbolicLink()) {
|
|
6848
|
+
return `'${current}' is a symlink, so the path changed after it was resolved`;
|
|
6849
|
+
}
|
|
6850
|
+
} catch {
|
|
6851
|
+
return `'${current}' could not be inspected`;
|
|
6852
|
+
}
|
|
6853
|
+
}
|
|
6854
|
+
return null;
|
|
6855
|
+
}
|
|
6856
|
+
function isContained(candidate, root, sep2) {
|
|
6857
|
+
if (candidate === root) return true;
|
|
6858
|
+
const rootWithSep = root.endsWith(sep2) ? root : root + sep2;
|
|
6859
|
+
return candidate.startsWith(rootWithSep);
|
|
6860
|
+
}
|
|
6861
|
+
function resolveSkillBodyPath(toolName, args, opts) {
|
|
6862
|
+
if (!SKILL_BODY_PATH_TOOLS.has(toolName)) return { ok: true, args };
|
|
6863
|
+
const raw = args["body_path"];
|
|
6864
|
+
if (raw === void 0 || raw === null) return { ok: true, args };
|
|
6865
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
6866
|
+
return { ok: false, error: "body_path must be a non-empty string." };
|
|
6867
|
+
}
|
|
6868
|
+
if (typeof args["body"] === "string") {
|
|
6869
|
+
return {
|
|
6870
|
+
ok: false,
|
|
6871
|
+
error: "Pass either body or body_path, not both \u2014 they are two ways to say the same thing and I will not guess which one you meant."
|
|
6872
|
+
};
|
|
6873
|
+
}
|
|
6874
|
+
const { agentDir, path, fs } = opts;
|
|
6875
|
+
const maxBytes = opts.maxBytes ?? SKILL_BODY_PATH_MAX_BYTES;
|
|
6876
|
+
const roots = allowedSkillBodyRoots(agentDir, path);
|
|
6877
|
+
const absolute = path.isAbsolute(raw) ? path.resolve(raw) : path.resolve(path.join(agentDir, "project"), raw);
|
|
6878
|
+
if (!roots.some((r) => isContained(absolute, r, path.sep))) {
|
|
6879
|
+
return {
|
|
6880
|
+
ok: false,
|
|
6881
|
+
error: `body_path must be inside your own project or scratch directory. Got '${raw}'. Allowed: ${roots.join(", ")}`
|
|
6882
|
+
};
|
|
6883
|
+
}
|
|
6884
|
+
let real;
|
|
6885
|
+
try {
|
|
6886
|
+
real = fs.realpathSync(absolute);
|
|
6887
|
+
} catch {
|
|
6888
|
+
return { ok: false, error: `body_path does not exist or is unreadable: '${raw}'` };
|
|
6889
|
+
}
|
|
6890
|
+
if (!roots.some((r) => isContained(real, r, path.sep))) {
|
|
6891
|
+
return {
|
|
6892
|
+
ok: false,
|
|
6893
|
+
error: `body_path resolves outside your own directories (symlink?). '${raw}' \u2192 '${real}'. Allowed: ${roots.join(", ")}`
|
|
6894
|
+
};
|
|
6895
|
+
}
|
|
6896
|
+
const walkError = assertNoSymlinkComponents(real, roots, path, fs);
|
|
6897
|
+
if (walkError) return { ok: false, error: `body_path: ${walkError}` };
|
|
6898
|
+
let vetted = null;
|
|
6899
|
+
try {
|
|
6900
|
+
vetted = fs.lstatSync(real);
|
|
6901
|
+
} catch {
|
|
6902
|
+
return { ok: false, error: `body_path vanished before it could be read: '${raw}'` };
|
|
6903
|
+
}
|
|
6904
|
+
let fd;
|
|
6905
|
+
try {
|
|
6906
|
+
fd = fs.openSync(real, opts.openFlags);
|
|
6907
|
+
} catch (err) {
|
|
6908
|
+
const msg = err.message;
|
|
6909
|
+
return {
|
|
6910
|
+
ok: false,
|
|
6911
|
+
error: `body_path could not be opened (it may have been replaced mid-check): '${raw}' \u2014 ${msg}`
|
|
6912
|
+
};
|
|
6913
|
+
}
|
|
6914
|
+
try {
|
|
6915
|
+
let stat;
|
|
6916
|
+
try {
|
|
6917
|
+
stat = fs.fstatSync(fd);
|
|
6918
|
+
} catch {
|
|
6919
|
+
return { ok: false, error: `body_path could not be inspected: '${raw}'` };
|
|
6920
|
+
}
|
|
6921
|
+
if (!stat.isFile()) {
|
|
6922
|
+
return { ok: false, error: `body_path is not a regular file: '${raw}'` };
|
|
6923
|
+
}
|
|
6924
|
+
if (stat.ino !== void 0 && vetted.ino !== void 0) {
|
|
6925
|
+
if (stat.ino !== vetted.ino || stat.dev !== vetted.dev) {
|
|
6926
|
+
return {
|
|
6927
|
+
ok: false,
|
|
6928
|
+
error: `body_path changed identity between the check and the open (it was replaced): '${raw}'`
|
|
6929
|
+
};
|
|
6930
|
+
}
|
|
6931
|
+
}
|
|
6932
|
+
if (stat.size > maxBytes) {
|
|
6933
|
+
return {
|
|
6934
|
+
ok: false,
|
|
6935
|
+
error: `body_path is ${stat.size} bytes, over the ${maxBytes}-byte limit for a skill body.`
|
|
6936
|
+
};
|
|
6937
|
+
}
|
|
6938
|
+
let bytes;
|
|
6939
|
+
try {
|
|
6940
|
+
bytes = fs.readFdSync(fd, maxBytes);
|
|
6941
|
+
} catch (err) {
|
|
6942
|
+
return { ok: false, error: `body_path could not be read: ${err.message}` };
|
|
6943
|
+
}
|
|
6944
|
+
if (bytes.byteLength > maxBytes) {
|
|
6945
|
+
return { ok: false, error: `body_path grew past the ${maxBytes}-byte limit while being read.` };
|
|
6946
|
+
}
|
|
6947
|
+
const body = fs.decodeUtf8(bytes);
|
|
6948
|
+
const next = { ...args, body };
|
|
6949
|
+
delete next["body_path"];
|
|
6950
|
+
return { ok: true, args: next };
|
|
6951
|
+
} finally {
|
|
6952
|
+
try {
|
|
6953
|
+
fs.closeSync(fd);
|
|
6954
|
+
} catch {
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6823
6958
|
|
|
6824
6959
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
6825
6960
|
var external_exports = {};
|
|
@@ -24345,16 +24480,72 @@ async function discoverApiTools() {
|
|
|
24345
24480
|
return [];
|
|
24346
24481
|
}
|
|
24347
24482
|
}
|
|
24483
|
+
function canonicalAgentDir(dir) {
|
|
24484
|
+
try {
|
|
24485
|
+
return realpathSync(dir);
|
|
24486
|
+
} catch {
|
|
24487
|
+
return dir;
|
|
24488
|
+
}
|
|
24489
|
+
}
|
|
24348
24490
|
async function forwardToolCall(toolName, args) {
|
|
24349
24491
|
if (!AGT_AGENT_ID) {
|
|
24350
24492
|
throw new Error("Cannot forward tool call: AGT_AGENT_ID is not set");
|
|
24351
24493
|
}
|
|
24494
|
+
let outgoing = args;
|
|
24495
|
+
if (SKILL_BODY_PATH_TOOLS.has(toolName) && args["body_path"] !== void 0) {
|
|
24496
|
+
if (!AGT_AGENT_CODE_NAME) {
|
|
24497
|
+
throw new Error("Cannot resolve body_path: AGT_AGENT_CODE_NAME is not set");
|
|
24498
|
+
}
|
|
24499
|
+
const resolved = resolveSkillBodyPath(toolName, args, {
|
|
24500
|
+
// ADR-0049 / ENG-7891: the per-agent host dir is agent_id-keyed and the
|
|
24501
|
+
// codename path is only a compatibility SYMLINK. That is not a style
|
|
24502
|
+
// point here, it is a correctness one: the containment check below
|
|
24503
|
+
// realpath-resolves the caller's path, which resolves THROUGH the symlink
|
|
24504
|
+
// to the agent_id-keyed tree - so a codename-based root would fail
|
|
24505
|
+
// containment for every legitimate file and refuse them all. Resolve the
|
|
24506
|
+
// root to its canonical form so both sides of the comparison are real
|
|
24507
|
+
// paths. Falls back to the unresolved join if the dir is missing, which
|
|
24508
|
+
// then simply refuses (there is nothing to read anyway).
|
|
24509
|
+
agentDir: canonicalAgentDir(join3(homedir3(), ".augmented", AGT_AGENT_CODE_NAME)),
|
|
24510
|
+
// agent-dir-allow: immediately realpath-resolved to the ADR-0049 agent_id-keyed dir before use as a containment root
|
|
24511
|
+
path: { isAbsolute, resolve: pathResolve, join: join3, sep },
|
|
24512
|
+
// O_NOFOLLOW is the whole point of opening by flags: the path is already
|
|
24513
|
+
// symlink-resolved, so a symlink at the final component means it was
|
|
24514
|
+
// swapped after the check, and this turns that into an ELOOP.
|
|
24515
|
+
openFlags: fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW,
|
|
24516
|
+
fs: {
|
|
24517
|
+
realpathSync,
|
|
24518
|
+
openSync,
|
|
24519
|
+
fstatSync,
|
|
24520
|
+
closeSync,
|
|
24521
|
+
// Bounded read straight off the descriptor: at most maxBytes + 1 bytes,
|
|
24522
|
+
// so an oversize file is detected rather than buffered whole.
|
|
24523
|
+
lstatSync,
|
|
24524
|
+
// Raw bytes, decoded only after the byte-length check — see
|
|
24525
|
+
// readFdSync's docblock in skill-body-path.ts.
|
|
24526
|
+
readFdSync: (fd, maxBytes) => {
|
|
24527
|
+
const buf = Buffer.alloc(maxBytes + 1);
|
|
24528
|
+
let read = 0;
|
|
24529
|
+
for (; ; ) {
|
|
24530
|
+
const n = readSync(fd, buf, read, buf.length - read, null);
|
|
24531
|
+
if (n <= 0) break;
|
|
24532
|
+
read += n;
|
|
24533
|
+
if (read >= buf.length) break;
|
|
24534
|
+
}
|
|
24535
|
+
return buf.subarray(0, read);
|
|
24536
|
+
},
|
|
24537
|
+
decodeUtf8: (bytes) => Buffer.from(bytes).toString("utf8")
|
|
24538
|
+
}
|
|
24539
|
+
});
|
|
24540
|
+
if (!resolved.ok) throw new Error(resolved.error);
|
|
24541
|
+
outgoing = resolved.args;
|
|
24542
|
+
}
|
|
24352
24543
|
return apiPost(
|
|
24353
24544
|
"/host/mcp/tools/call",
|
|
24354
24545
|
{
|
|
24355
24546
|
agent_id: AGT_AGENT_ID,
|
|
24356
24547
|
name: toolName,
|
|
24357
|
-
arguments:
|
|
24548
|
+
arguments: outgoing,
|
|
24358
24549
|
run_id: AGT_RUN_ID ?? null
|
|
24359
24550
|
},
|
|
24360
24551
|
false,
|