agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
165 lines • 6.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.trustBoundaryRecorder = void 0;
const project_js_1 = require("./project.js");
let recorderSequence = 0;
let captureSequence = 0;
function newEpoch(capacity) {
return {
id: `trust-capture-${Date.now()}-${++captureSequence}`,
ring: new Array(capacity),
head: 0,
size: 0,
observed: 0,
evicted: 0,
invalid: 0,
oversized: 0,
pending: 0,
};
}
function counters(epoch) {
return Object.freeze({
observed: epoch.observed,
retained: epoch.size,
evicted: epoch.evicted,
invalid: epoch.invalid,
oversized: epoch.oversized,
pending: epoch.pending,
});
}
/** O(1) ordinary append; bounded owned-row insertion for reentrant completion. */
function retain(epoch, fact) {
const capacity = epoch.ring.length;
const last = epoch.ring[(epoch.head + epoch.size - 1 + capacity) % capacity];
if (epoch.size === 0 || (last !== undefined && last.seq < fact.seq)) {
if (epoch.size === capacity) {
epoch.ring[epoch.head] = fact;
epoch.head = (epoch.head + 1) % capacity;
epoch.evicted++;
}
else {
epoch.ring[(epoch.head + epoch.size) % capacity] = fact;
epoch.size++;
}
return;
}
// A metadata Proxy can synchronously dispatch a later observation before
// this one completes. Retain by reserved ordinal, never completion order.
if (epoch.size === capacity) {
const first = epoch.ring[epoch.head];
epoch.evicted++;
if (first !== undefined && fact.seq < first.seq)
return;
epoch.head = (epoch.head + 1) % capacity;
epoch.size--;
}
let index = epoch.size;
while (index > 0) {
const previous = epoch.ring[(epoch.head + index - 1) % capacity];
if (previous === undefined || previous.seq < fact.seq)
break;
epoch.ring[(epoch.head + index) % capacity] = previous;
index--;
}
epoch.ring[(epoch.head + index) % capacity] = fact;
epoch.size++;
}
/**
* Capture actual typed security facts in a bounded, metadata-only tail.
* Subscribe before execution; this observer cannot reconstruct missed facts.
*/
function trustBoundaryRecorder(options = {}) {
const maxFacts = options.maxFacts ?? 1000;
if (!Number.isSafeInteger(maxFacts) || maxFacts < 1 || maxFacts > 10000) {
throw new RangeError('trustBoundaryRecorder: maxFacts must be an integer from 1 to 10000');
}
const id = options.id ?? `trust-boundaries-${++recorderSequence}`;
if (typeof id !== 'string' || id.length === 0 || id.length > 512) {
throw new TypeError('trustBoundaryRecorder: id must contain 1 to 512 characters');
}
let epoch = newEpoch(maxFacts);
let active;
return {
id,
get counters() {
return counters(epoch);
},
subscribe(source) {
if (active !== undefined)
throw new Error('trustBoundaryRecorder: already subscribed');
const token = {};
active = token;
let off;
try {
off = source.on('*', (event) => {
if (active !== token)
return;
const owner = epoch;
let reserved = false;
const result = (0, project_js_1.projectTrustBoundary)(event, () => {
if (epoch !== owner || active !== token)
return undefined;
reserved = true;
owner.pending++;
return ++owner.observed;
});
if (!reserved)
return;
owner.pending--;
// Reset creates a new owner; an old observation cannot enter it.
if (epoch !== owner)
return;
if (result.status === 'invalid')
owner.invalid++;
else if (result.status === 'oversized')
owner.oversized++;
else if (result.status === 'fact')
retain(owner, result.fact);
});
if (typeof off !== 'function')
throw new TypeError('trustBoundaryRecorder: source.on must return unsubscribe');
}
catch (error) {
if (active === token)
active = undefined;
throw error;
}
let stopped = false;
return () => {
if (stopped)
return;
stopped = true;
if (active === token)
active = undefined;
off();
};
},
toSnapshot() {
const facts = [];
for (let i = 0; i < epoch.size; i++) {
const fact = epoch.ring[(epoch.head + i) % maxFacts];
if (fact !== undefined)
facts.push(fact);
}
return Object.freeze({
name: 'TrustBoundaries',
description: 'Observed middleware, permission and credential metadata; not proof of execution or complete coverage.',
meta: Object.freeze({ version: 1 }),
data: Object.freeze({
captureId: epoch.id,
facts: Object.freeze(facts),
counters: counters(epoch),
firstObservedSeq: epoch.observed ? 1 : null,
lastObservedSeq: epoch.observed || null,
firstRetainedSeq: facts[0]?.seq ?? null,
lastRetainedSeq: facts[facts.length - 1]?.seq ?? null,
}),
});
},
resetCapture() {
epoch = newEpoch(maxFacts);
},
};
}
exports.trustBoundaryRecorder = trustBoundaryRecorder;
//# sourceMappingURL=capture.js.map