agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
174 lines • 6.71 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.projectTrustBoundary = void 0;
const moments_js_1 = require("../lib/moments.js");
const STRING_LIMIT = 512;
const PATH_LIMIT = 32;
const FACT_BYTES_LIMIT = 8192;
// Exhaustiveness belongs at the producer vocabulary boundary: additions must
// be explicitly admitted here rather than silently disappearing from capture.
const capabilities = Object.keys({
tool_call: true,
skill_read: true,
memory_read: true,
memory_write: true,
external_net: true,
user_data: true,
});
const permissionResults = Object.keys({
allow: true,
deny: true,
halt: true,
gate_open: true,
});
const middlewareOutcomes = Object.keys({ allow: true, deny: true, ask: true });
const credentialModes = Object.keys({ machine: true, user: true });
const names = new Set([
'agentfootprint.middleware.decision',
'agentfootprint.permission.check',
'agentfootprint.permission.halt',
'agentfootprint.credential.requested',
'agentfootprint.credential.acquired',
'agentfootprint.credential.authorization_required',
'agentfootprint.credential.failed',
]);
const invalid = Symbol('invalid');
const oversized = Symbol('oversized');
function record(value) {
if (typeof value !== 'object' || value === null || Array.isArray(value))
throw invalid;
return value;
}
function field(value, key) {
const descriptor = Object.getOwnPropertyDescriptor(value, key);
if (descriptor && !('value' in descriptor))
throw invalid;
return descriptor?.value;
}
function text(value) {
if (typeof value !== 'string' || value.length === 0)
throw invalid;
if (value.length > STRING_LIMIT)
throw oversized;
return value;
}
function integer(value, minimum = 0) {
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum)
throw invalid;
return value;
}
function choice(value, choices) {
const result = text(value);
if (!choices.includes(result))
throw invalid;
return result;
}
function position(value) {
const source = record(value);
const engineRunId = text(field(source, 'engineRunId'));
const logRunId = text(field(source, 'logRunId'));
const rawPath = field(source, 'drillPath');
if (!Array.isArray(rawPath))
throw invalid;
const length = integer(field(rawPath, 'length'));
if (length > PATH_LIMIT)
throw oversized;
const drillPath = [];
for (let i = 0; i < length; i++)
drillPath.push(text(field(rawPath, String(i))));
return Object.freeze({
engineRunId,
logRunId,
drillPath: Object.freeze(drillPath),
committedThroughIdx: integer(field(source, 'committedThroughIdx'), -1),
});
}
/** Single own-data whitelist admission boundary. Never copies/enumerates the source. */
function projectTrustBoundary(event, reserve) {
let selected = false;
try {
const input = record(event);
const eventType = field(input, 'type');
if (typeof eventType !== 'string')
throw invalid;
if (!names.has(eventType))
return { status: 'ignored' };
selected = true;
// Reserve after membership, before user-controlled metadata inspection.
const seq = reserve();
if (seq === undefined)
return { status: 'ignored' };
const meta = record(field(input, 'meta'));
const payload = record(field(input, 'payload'));
const stamp = field(meta, 'wallClockMs');
if (typeof stamp !== 'number' || !Number.isFinite(stamp))
throw invalid;
const fact = {
seq,
eventType,
runId: text(field(meta, 'runId')),
runtimeStageId: text(field(meta, 'runtimeStageId')),
wallClockMs: stamp,
};
const source = field(meta, 'sourcePosition');
if (source !== undefined)
fact.sourcePosition = position(source);
const callId = field(payload, 'toolCallId');
if (callId !== undefined)
fact.toolCallId = text(callId);
const iteration = field(payload, 'iteration');
if (iteration !== undefined)
fact.iteration = integer(iteration);
const optionalText = (key) => {
const value = field(payload, key);
if (value !== undefined)
fact[key] = text(value);
};
switch (eventType) {
case 'agentfootprint.middleware.decision': {
fact.middleware = text(field(payload, 'middleware'));
fact.moment = choice(field(payload, 'moment'), moments_js_1.LOOP_MOMENTS);
fact.outcome = choice(field(payload, 'outcome'), middlewareOutcomes);
const changed = field(payload, 'changed');
if (typeof changed !== 'boolean')
throw invalid;
fact.changed = changed;
fact.iteration = integer(iteration);
break;
}
case 'agentfootprint.permission.check':
fact.capability = choice(field(payload, 'capability'), capabilities);
fact.result = choice(field(payload, 'result'), permissionResults);
optionalText('target');
optionalText('policyRuleId');
break;
case 'agentfootprint.permission.halt':
fact.target = text(field(payload, 'target'));
fact.iteration = integer(iteration);
optionalText('checkerId');
break;
default:
fact.service = text(field(payload, 'service'));
if (eventType === 'agentfootprint.credential.requested') {
const mode = field(payload, 'mode');
if (mode !== undefined)
fact.mode = choice(mode, credentialModes);
}
else if (eventType === 'agentfootprint.credential.acquired') {
fact.kind = text(field(payload, 'kind'));
}
else if (eventType === 'agentfootprint.credential.failed')
optionalText('errorClass');
}
if (new TextEncoder().encode(JSON.stringify(fact)).length > FACT_BYTES_LIMIT)
throw oversized;
return { status: 'fact', fact: Object.freeze(fact) };
}
catch (error) {
if (!selected)
return { status: 'ignored' };
return { status: error === oversized ? 'oversized' : 'invalid' };
}
}
exports.projectTrustBoundary = projectTrustBoundary;
//# sourceMappingURL=project.js.map