mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
c5cefd622b
Record genuine Code Mode use and full model usage rather than response-only estimates. Describe return shapes, permit byte-budgeted internal C reads, separate guest CPU from analysis deadlines, and move subsequent model profiles outside the source tree. Verify lease, admission, cleanup-retry and cancellation semantics. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
387 lines
12 KiB
JavaScript
387 lines
12 KiB
JavaScript
// Native protocols stay here. API and execution adapters consume parsed facts.
|
|
import { createHash, randomUUID } from "node:crypto";
|
|
import { writeFile, readFile, mkdir } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { parseBinaryTarget } from "../../dist/application/BinaryTargetResolver.js";
|
|
import { inspectKunaInstallation } from "../../dist/kuna/KunaInstallation.js";
|
|
import { KunaTargetCommands } from "../../dist/kuna/KunaTargetCommands.js";
|
|
import { parseKunaStringInventory } from "../../dist/kuna/KunaStringInventory.js";
|
|
import { parseKunaIncomingReferenceFacts } from "../../dist/kuna/KunaReferenceFiles.js";
|
|
import { PrivateRuntimeRoot } from "../../dist/process/PrivateRuntimeRoot.js";
|
|
import {
|
|
parseKunaSsaDump,
|
|
backwardSlice,
|
|
} from "../atomic-re-tools/kuna-ssa.mjs";
|
|
|
|
export class FactFailure extends Error {
|
|
constructor(code, message, details = {}) {
|
|
super(message);
|
|
this.code = code;
|
|
this.details = details;
|
|
}
|
|
}
|
|
const requireResult = (result, code = "provider_failure") => {
|
|
if (!result.ok)
|
|
throw new FactFailure(code, String(result.error?.message ?? result.error));
|
|
return result.value;
|
|
};
|
|
const sha256 = (value) => createHash("sha256").update(value).digest("hex");
|
|
|
|
export class KunaFactOwner {
|
|
#lifetime = new AbortController();
|
|
#sources = new Map();
|
|
#snapshots = new Map();
|
|
#work = new Map();
|
|
#loaded = new Map();
|
|
#root;
|
|
#commands;
|
|
#target;
|
|
#installation;
|
|
#retainedBytes = 0;
|
|
#strings;
|
|
#captureDirectory;
|
|
context;
|
|
static async open(path, environment = process.env) {
|
|
const owner = new KunaFactOwner();
|
|
owner.#target = requireResult(
|
|
await parseBinaryTarget(path),
|
|
"invalid_target",
|
|
);
|
|
owner.#installation = requireResult(
|
|
await inspectKunaInstallation(environment),
|
|
"provider_unavailable",
|
|
);
|
|
if (owner.#installation.version !== "1.833")
|
|
throw new FactFailure(
|
|
"unsupported_version",
|
|
"SSA diagnostic decoder is verified only for Kuna 1.833",
|
|
);
|
|
owner.#root = await PrivateRuntimeRoot.create({
|
|
prefix: "rea-atomic-facts-",
|
|
});
|
|
owner.#commands = new KunaTargetCommands(owner.#target, {
|
|
signal: owner.#lifetime.signal,
|
|
});
|
|
owner.#captureDirectory = environment.REA_ATOMIC_EVIDENCE_DIR;
|
|
owner.context = Object.freeze({
|
|
lease: randomUUID(),
|
|
artifact_sha256: owner.#target.sha256,
|
|
path: owner.#target.path,
|
|
selected_path: path,
|
|
provider: "kuna",
|
|
engine_sha256: owner.#installation.sha256,
|
|
engine_version: owner.#installation.version,
|
|
address_space: "native-default",
|
|
profile:
|
|
"native CLI defaults, no annotations; per-command arguments retained",
|
|
});
|
|
return owner;
|
|
}
|
|
async #native(key, arguments_, signal) {
|
|
if (this.#work.has(key)) return this.#work.get(key);
|
|
if (this.#sources.size >= 128)
|
|
throw new FactFailure(
|
|
"capacity",
|
|
"Session source capacity reached; close and reopen the target",
|
|
);
|
|
const work = this.#capture(arguments_, signal);
|
|
this.#work.set(key, work);
|
|
try {
|
|
return await work;
|
|
} catch (error) {
|
|
this.#work.delete(key);
|
|
throw error;
|
|
}
|
|
}
|
|
async #capture(arguments_, signal) {
|
|
const start = performance.now();
|
|
const snapshot = requireResult(
|
|
await this.#commands.run({
|
|
installation: this.#installation,
|
|
operation: "experiment:atomic-facts",
|
|
arguments: arguments_,
|
|
outputBytes: 8 * 1024 * 1024,
|
|
timeoutMs: 120000,
|
|
signal: AbortSignal.any([signal, this.#lifetime.signal]),
|
|
}),
|
|
);
|
|
const id = `ev_${sha256(this.context.lease + snapshot.stdout.text)}`;
|
|
const bytes = Buffer.byteLength(snapshot.stdout.text);
|
|
if (this.#retainedBytes + bytes > 64 * 1024 * 1024)
|
|
throw new FactFailure(
|
|
"capacity",
|
|
"64 MiB retained-source budget reached; close and reopen",
|
|
);
|
|
const file = join(this.#root.path, id);
|
|
await writeFile(file, snapshot.stdout.text, { flag: "wx" });
|
|
const source = {
|
|
evidence_id: id,
|
|
sha256: sha256(snapshot.stdout.text),
|
|
bytes: Buffer.byteLength(snapshot.stdout.text),
|
|
arguments: arguments_(snapshot.input).map((s) =>
|
|
s === snapshot.input ? this.#target.path : s,
|
|
),
|
|
elapsed_ms: performance.now() - start,
|
|
};
|
|
this.#sources.set(id, { source, file });
|
|
this.#retainedBytes += bytes;
|
|
if (this.#captureDirectory) {
|
|
await mkdir(this.#captureDirectory, { recursive: true, mode: 0o700 });
|
|
await writeFile(
|
|
join(this.#captureDirectory, `${id}.txt`),
|
|
snapshot.stdout.text,
|
|
{ flag: "wx", mode: 0o600 },
|
|
);
|
|
await writeFile(
|
|
join(this.#captureDirectory, `${id}.json`),
|
|
JSON.stringify({ context: this.context, source }),
|
|
{ flag: "wx", mode: 0o600 },
|
|
);
|
|
}
|
|
return { source, input: snapshot.input };
|
|
}
|
|
async #sourceText(id) {
|
|
const record = this.#sources.get(id);
|
|
if (!record)
|
|
throw new FactFailure(
|
|
"unknown_evidence",
|
|
"Evidence is not owned by this target lease",
|
|
);
|
|
const text = await readFile(record.file, "utf8");
|
|
if (sha256(text) !== record.source.sha256)
|
|
throw new FactFailure("evidence_changed", "Retained source changed");
|
|
return text;
|
|
}
|
|
async strings(input, signal) {
|
|
const capture = await this.#native(
|
|
"strings",
|
|
(path) => [
|
|
"strings",
|
|
path,
|
|
"--json",
|
|
"--encoding",
|
|
"all",
|
|
"--min-length",
|
|
"5",
|
|
"--termination",
|
|
"any",
|
|
"--no-xrefs",
|
|
],
|
|
signal,
|
|
);
|
|
this.#strings ??= requireResult(
|
|
parseKunaStringInventory(
|
|
await this.#sourceText(capture.source.evidence_id),
|
|
capture.input,
|
|
),
|
|
"invalid_provider_output",
|
|
);
|
|
const inventory = this.#strings;
|
|
const rows = inventory.items.filter((row) =>
|
|
row.value.includes(input.query),
|
|
);
|
|
return {
|
|
source: capture.source,
|
|
items: rows.slice(input.offset, input.offset + input.limit),
|
|
total: rows.length,
|
|
next_offset:
|
|
input.offset + input.limit < rows.length
|
|
? input.offset + input.limit
|
|
: null,
|
|
coverage: {
|
|
scope: "native string inventory, minimum 5",
|
|
artifact_discovery: "unknown",
|
|
reference_attribution: "not-computed",
|
|
},
|
|
};
|
|
}
|
|
async references(input, signal) {
|
|
const results = [];
|
|
for (const address of input.addresses) {
|
|
try {
|
|
const capture = await this.#native(
|
|
`xref:${address}`,
|
|
(path) => ["xrefs", path, "--to", address, "--json"],
|
|
signal,
|
|
);
|
|
const parsed = requireResult(
|
|
parseKunaIncomingReferenceFacts(
|
|
await this.#sourceText(capture.source.evidence_id),
|
|
capture.input,
|
|
address,
|
|
),
|
|
"invalid_provider_output",
|
|
);
|
|
results.push({
|
|
ok: true,
|
|
address,
|
|
source: capture.source,
|
|
items: parsed.facts.slice(input.offset, input.offset + input.limit),
|
|
total: parsed.facts.length,
|
|
next_offset:
|
|
input.offset + input.limit < parsed.facts.length
|
|
? input.offset + input.limit
|
|
: null,
|
|
coverage: {
|
|
scope: "exact target address; not an object extent",
|
|
discovery: "unknown",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
results.push({
|
|
ok: false,
|
|
address,
|
|
error: {
|
|
code: error.code ?? "provider_failure",
|
|
message: error.message,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
return { results };
|
|
}
|
|
async snapshot(input, signal) {
|
|
const capture = await this.#native(
|
|
`ssa:${input.entry}`,
|
|
(path) => [
|
|
"decompile",
|
|
path,
|
|
input.entry,
|
|
"--addr",
|
|
"--raw",
|
|
"--max-fn-memory",
|
|
"1024",
|
|
],
|
|
signal,
|
|
);
|
|
const id = `snap_${sha256(this.context.lease + input.entry + capture.source.sha256)}`;
|
|
if (!this.#snapshots.has(id))
|
|
this.#snapshots.set(id, {
|
|
entry: input.entry,
|
|
evidence_id: capture.source.evidence_id,
|
|
});
|
|
const parsed = await this.#load(id);
|
|
if (parsed.block(0).start !== input.entry)
|
|
throw new FactFailure(
|
|
"subject_mismatch",
|
|
"Native SSA entry does not match the requested function entry",
|
|
);
|
|
return {
|
|
snapshot: id,
|
|
source: capture.source,
|
|
entry: input.entry,
|
|
coverage: parsed.coverage,
|
|
};
|
|
}
|
|
async #load(id) {
|
|
const descriptor = this.#snapshots.get(id);
|
|
if (!descriptor)
|
|
throw new FactFailure(
|
|
"unknown_snapshot",
|
|
"Snapshot is not owned by the current target lease",
|
|
);
|
|
if (!this.#loaded.has(id)) {
|
|
const { file, source } = this.#sources.get(descriptor.evidence_id);
|
|
if (source.bytes > 2 * 1024 * 1024)
|
|
throw new FactFailure(
|
|
"capacity",
|
|
"SSA indexing budget is 2 MiB per source with two resident models; full source remains readable",
|
|
{ evidence_id: descriptor.evidence_id },
|
|
);
|
|
const text = await readFile(file, "utf8");
|
|
if (sha256(text) !== source.sha256)
|
|
throw new FactFailure(
|
|
"evidence_changed",
|
|
"Retained native source changed",
|
|
);
|
|
const parsed = parseKunaSsaDump(text);
|
|
if (this.#loaded.size >= 2)
|
|
this.#loaded.delete(this.#loaded.keys().next().value);
|
|
this.#loaded.set(id, parsed);
|
|
}
|
|
return this.#loaded.get(id);
|
|
}
|
|
async operations(input) {
|
|
const model = await this.#load(input.snapshot);
|
|
return {
|
|
...model.select(input),
|
|
evidence_id: this.#snapshots.get(input.snapshot).evidence_id,
|
|
coverage: model.coverage,
|
|
};
|
|
}
|
|
async relations(input) {
|
|
const model = await this.#load(input.snapshot);
|
|
return {
|
|
...model.edges(input),
|
|
evidence_id: this.#snapshots.get(input.snapshot).evidence_id,
|
|
coverage: model.coverage,
|
|
};
|
|
}
|
|
async slice(input) {
|
|
const model = await this.#load(input.snapshot);
|
|
return {
|
|
...backwardSlice(model, input.seeds, {
|
|
maxOperations: input.max_operations,
|
|
stopAt: input.stop_at,
|
|
stopKinds: input.stop_kinds,
|
|
}),
|
|
evidence_id: this.#snapshots.get(input.snapshot).evidence_id,
|
|
};
|
|
}
|
|
async code(input) {
|
|
const descriptor = this.#snapshots.get(input.snapshot);
|
|
if (!descriptor)
|
|
throw new FactFailure(
|
|
"unknown_snapshot",
|
|
"Snapshot is not owned by the current lease",
|
|
);
|
|
const text = await this.#sourceText(descriptor.evidence_id);
|
|
const code = text.split("\n0\nBasic Block", 1)[0];
|
|
const lines = code.split("\n");
|
|
return {
|
|
evidence_id: descriptor.evidence_id,
|
|
start_line: input.offset + 1,
|
|
text: lines.slice(input.offset, input.offset + input.limit).join("\n"),
|
|
total_lines: lines.length,
|
|
next_offset:
|
|
input.offset + input.limit < lines.length
|
|
? input.offset + input.limit
|
|
: null,
|
|
coverage: {
|
|
authority: "reconstructed C, not original source",
|
|
scope: "selected display lines",
|
|
},
|
|
};
|
|
}
|
|
async evidence(input) {
|
|
const record = this.#sources.get(input.evidence_id);
|
|
if (!record)
|
|
throw new FactFailure(
|
|
"unknown_evidence",
|
|
"Evidence is not retained by the current lease",
|
|
);
|
|
const text = await this.#sourceText(input.evidence_id);
|
|
return {
|
|
source: record.source,
|
|
text: text.slice(input.offset, input.offset + input.limit),
|
|
total_code_units: text.length,
|
|
next_offset:
|
|
input.offset + input.limit < text.length
|
|
? input.offset + input.limit
|
|
: null,
|
|
};
|
|
}
|
|
async close() {
|
|
this.#lifetime.abort();
|
|
await this.#commands.close();
|
|
await this.#root.close();
|
|
this.#loaded.clear();
|
|
this.#sources.clear();
|
|
this.#work.clear();
|
|
this.#snapshots.clear();
|
|
}
|
|
cancel() {
|
|
this.#lifetime.abort();
|
|
}
|
|
}
|