agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
99 lines • 4.59 kB
JavaScript
;
/**
* identityKey — the conversation a pooled client belongs to, as one key.
*
* Pattern: the library's one identity encoding, never a second.
* Role: The client key is `identityNamespace` of the identity the CALLER
* named — each field through `encodeIdentityField`, the encoding the
* memory layer calls the tenant boundary. It is injective: tenant
* `acme/hr` with principal `alice` and tenant `acme` with principal
* `hr/alice` are two keys, and an absent field and a field named `_`
* are two keys. A pool is one server, so the key needs no server
* segment, and two pools never share a client.
*
* Two doors only: the run's caller identity (`ToolDispatchContext.callerIdentity`,
* `ToolExecutionContext.identity`) and the identity app code passes to `acquire`
* and `terminate`. Never tool arguments, model output, a message, a request
* body, or a `sessionId` alone. No identity value is ever written to an error,
* an event or a report: the wire carries `keyHash`, the tool-session digest.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.scopeMatcher = exports.poolKeyOf = void 0;
const encode_js_1 = require("../../memory/identity/encode.js");
const types_js_1 = require("../../memory/identity/types.js");
const toolSessions_js_1 = require("../../tools/toolSessions.js");
/**
* The key for `identity`, or why there is none: `'no-identity'` when nothing was
* named (a derived identity counts as none), `'incomplete-identity'` when it
* lacks a `conversationId`, a required `principal` or a required `tenant`, or a
* field is not a string.
*/
function poolKeyOf(identity, rules) {
if (identity === undefined || identity === null)
return 'no-identity';
if (typeof identity !== 'object' || Array.isArray(identity))
return 'incomplete-identity';
const raw = identity;
const fields = {};
for (const name of ['tenant', 'principal', 'conversationId']) {
let value;
try {
value = raw[name];
}
catch {
return 'incomplete-identity';
}
if (value === undefined || value === null || value === '')
continue;
if (typeof value !== 'string')
return 'incomplete-identity';
fields[name] = value;
}
if (fields.conversationId === undefined)
return 'incomplete-identity';
if (rules.requirePrincipal && fields.principal === undefined)
return 'incomplete-identity';
if (rules.requireTenant && fields.tenant === undefined)
return 'incomplete-identity';
const named = Object.freeze({
conversationId: fields.conversationId,
...(fields.tenant !== undefined && { tenant: fields.tenant }),
...(fields.principal !== undefined && { principal: fields.principal }),
});
const key = (0, types_js_1.identityNamespace)(named);
const tenant = (0, encode_js_1.encodeIdentityField)(named.tenant);
return {
key,
principal: `${tenant}/${(0, encode_js_1.encodeIdentityField)(named.principal)}`,
tenant,
keyHash: (0, toolSessions_js_1.hashSessionKey)(key),
identity: named,
};
}
exports.poolKeyOf = poolKeyOf;
/**
* Which keys a scope names. The tenant is matched exactly, always — an absent
* tenant is the absent segment, never a wildcard, because the tenant is the
* boundary. A scope that names a `conversationId` names exactly that one
* conversation (an absent principal is then the absent segment, as in the key).
* A scope without one names every conversation of its principal, and a scope
* without a principal either names every conversation of its tenant.
*/
function scopeMatcher(scope) {
const tenant = (0, encode_js_1.encodeIdentityField)(scope.tenant);
if (scope.conversationId !== undefined && scope.conversationId !== '') {
const exact = (0, types_js_1.identityNamespace)({
conversationId: scope.conversationId,
...(scope.tenant !== undefined && { tenant: scope.tenant }),
...(scope.principal !== undefined && { principal: scope.principal }),
});
return (key) => key.key === exact;
}
if (scope.principal !== undefined && scope.principal !== '') {
const principal = `${tenant}/${(0, encode_js_1.encodeIdentityField)(scope.principal)}`;
return (key) => key.principal === principal;
}
return (key) => key.tenant === tenant;
}
exports.scopeMatcher = scopeMatcher;
//# sourceMappingURL=identityKey.js.map