fix(context-bill): --exact reads only the file each walk approved

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-26 02:28:28 +00:00
1 parent c7ddca62db
commit fd4809e0e1
2 files changed
+86 -5

No files matched your search

+35 -5
View File
@@ -815,9 +815,15 @@ async function countTokens(text: string, { model, apiKey, fetchImpl }: CountToke
return json.input_tokens;
}
export interface ExactFile extends WalkedFile {
/** The walked file's identity; --exact reads only that same file. */
dev: number;
ino: number;
}
export interface ExactSendSet {
/** Each real file once, under the first lexical path a skill's walk reaches it by. */
files: WalkedFile[];
files: ExactFile[];
/** Realpaths of the SKILL.md files whose frontmatter the bill prices. */
frontmatter: Set<string>;
}
@@ -833,7 +839,7 @@ export interface ExactSendSet {
export function exactSendSet(root: string): ExactSendSet {
const resolved = path.resolve(root);
if (!fs.existsSync(resolved)) throw new Error(`No such tree: ${resolved}`);
const files: WalkedFile[] = [];
const files: ExactFile[] = [];
const frontmatter = new Set<string>();
const seen = new Set<string>();
for (const dir of findSkillDirs(resolved)) {
@@ -841,12 +847,36 @@ export function exactSendSet(root: string): ExactSendSet {
for (const file of walkMdFiles(dir, { billedRoot: resolved, skipNestedSkills: true })) {
if (seen.has(file.real)) continue;
seen.add(file.real);
files.push(file);
const { dev, ino } = fs.statSync(file.real);
files.push({ ...file, dev, ino });
}
}
return { files, frontmatter };
}
/**
* Reads the exact file the walk approved: its realpath, opened without
* following a final link, and only if it is still the same regular file. A
* link retargeted or a file swapped for a link after the walk is refused, so
* nothing the bill did not walk can be sent.
*/
function readWalkedFile(real: string, dev: number, ino: number, file: string, root: string): string {
const changed = () => new Error(`${file} changed after it was walked; nothing from ${root} was sent. Run again.`);
let fd: number;
try {
fd = fs.openSync(real, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
} catch {
throw changed();
}
try {
const st = fs.fstatSync(fd);
if (!st.isFile() || st.dev !== dev || st.ino !== ino) throw changed();
return fs.readFileSync(fd, "utf8");
} finally {
fs.closeSync(fd);
}
}
export interface ExactMeasurement {
tokenSource: string;
tokensOf: TokensOf;
@@ -894,8 +924,8 @@ export async function measureExactTokens(
// real file back to the one key that was measured.
const lexicalOf = new Map<string, string>();
const { files, frontmatter } = sendSet ?? exactSendSet(root);
for (const { path: file, real } of files) {
const text = fs.readFileSync(real, "utf8");
for (const { path: file, real, dev, ino } of files) {
const text = readWalkedFile(real, dev, ino, file, root);
texts.set(file, text);
lexicalOf.set(real, file);
if (!frontmatter.has(real)) continue;
+51
View File
@@ -1059,6 +1059,57 @@ describe("--exact (opt-in measurement; offline here via an injected fetch)", ()
expect(calls.length).toBe(4);
});
it("sends only the files the walk approved, even when links or files change during --diff", async () => {
if (process.platform === "win32") return;
const run = async (swap: (store: string, qa: string) => void) => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-swap-"));
for (const tree of ["a", "b"]) {
fs.mkdirSync(path.join(tmp, tree, "qa"), { recursive: true });
fs.writeFileSync(path.join(tmp, tree, "qa", "SKILL.md"), "---\nname: qa\ndescription: d\n---\n# qa\n");
}
const store = path.join(tmp, "store");
fs.mkdirSync(store);
fs.writeFileSync(path.join(store, "safe.md"), "safe");
fs.writeFileSync(path.join(store, "secret.env"), "SYNTHETIC_PRIVATE_NONMARKDOWN");
const qa = path.join(tmp, "b", "qa");
fs.symlinkSync(path.join(store, "safe.md"), path.join(qa, "ref.md"));
const calls: { body: string }[] = [];
const inner = fakeFetch(calls) as unknown as (u: string, i: { body: string }) => Promise<unknown>;
let swapped = false;
const fetchImpl = (async (u: string, i: { body: string }) => {
if (!swapped) {
swapped = true;
swap(store, qa);
}
return inner(u, i);
}) as never;
const err = capture();
const code = await contextBillMain(["--diff", path.join(tmp, "a"), path.join(tmp, "b"), "--exact", "--json"], {
stdout: capture().stream,
stderr: err.stream,
apiKey: "sk-test",
fetchImpl,
egressHome,
});
fs.rmSync(tmp, { recursive: true, force: true });
return { code, calls, err: err.text() };
};
const retargeted = await run((store, qa) => {
fs.unlinkSync(path.join(qa, "ref.md"));
fs.symlinkSync(path.join(store, "secret.env"), path.join(qa, "ref.md"));
});
expect(retargeted.calls.some((c) => c.body.includes("SYNTHETIC_PRIVATE"))).toBe(false);
const replaced = await run((store) => {
fs.unlinkSync(path.join(store, "safe.md"));
fs.symlinkSync(path.join(store, "secret.env"), path.join(store, "safe.md"));
});
expect(replaced.calls.some((c) => c.body.includes("SYNTHETIC_PRIVATE"))).toBe(false);
expect(replaced.code).toBe(1);
expect(replaced.err).toContain("changed after it was walked");
});
it("names a missing tree, without a raw ENOENT or any request", async () => {
const parent = fs.mkdtempSync(path.join(os.tmpdir(), "context-bill-missing-"));
const out = capture();