UNPKG

agentfootprint

Version:

The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.

81 lines • 3.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizeHostTurn = exports.resolveTurnNumber = exports.maxStoredTurn = void 0; /** * How many pages the scan will read before it stops asking. 100 pages of * 1000 is 100k entries in ONE conversation's namespace — far past any real * transcript, and a bound rather than a hang if an adapter's cursor never * terminates. Stopping early can only UNDER-count, which the `+1` and the * host's own floor then correct upward. */ const MAX_SCAN_PAGES = 100; /** Page size requested per `list()` call. */ const SCAN_PAGE_SIZE = 1000; /** A turn is a positive integer. Anything else is not a turn. */ function turnOf(entry) { const raw = entry.source?.turn; if (typeof raw !== 'number' || !Number.isFinite(raw)) return 0; const turn = Math.floor(raw); return turn > 0 ? turn : 0; } /** * The highest turn already stored in this identity's namespace — 0 when the * conversation has never been written to. * * Only LIVE entries are seen: `store.list` drops TTL-expired ones, the same * as every read path, so an expired turn 4 does not hold the numbering open. */ async function maxStoredTurn(store, identity, options = {}) { const read = options.turnFor ?? turnOf; let max = 0; let cursor; let seen; for (let page = 0; page < MAX_SCAN_PAGES; page++) { const result = await store.list(identity, { limit: SCAN_PAGE_SIZE, ...(cursor !== undefined && { cursor }), }); for (const entry of result.entries) { const turn = read(entry); if (turn > max) max = turn; } if (result.cursor === undefined) break; // An adapter that hands back the cursor it was given would page forever. if (result.cursor === seen) break; seen = result.cursor; cursor = result.cursor; } return max; } exports.maxStoredTurn = maxStoredTurn; /** * Resolve the turn number this run should stamp on everything it writes. * * Returns at least 1, always. A store that throws is NOT swallowed: a memory * whose store is unreachable is going to fail on the next read anyway, and a * turn number quietly guessed here is how entries overwrite each other. */ async function resolveTurnNumber(options) { const hostTurn = normalizeHostTurn(options.hostTurn); const stores = Array.from(new Set(options.stores)); let stored = 0; for (const store of stores) { const max = await maxStoredTurn(store, options.identity); if (max > stored) stored = max; } return Math.max(hostTurn, stored + 1); } exports.resolveTurnNumber = resolveTurnNumber; /** A host turn below 1 (or not a number at all) means "I do not know" → 1. */ function normalizeHostTurn(hostTurn) { if (typeof hostTurn !== 'number' || !Number.isFinite(hostTurn)) return 1; return Math.max(1, Math.floor(hostTurn)); } exports.normalizeHostTurn = normalizeHostTurn; //# sourceMappingURL=resolveTurnNumber.js.map