UNPKG

agentfootprint

Version:

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

148 lines • 6.47 kB
"use strict"; /** * time/textForms — the spellings of the times written in TEXT (time * design § 9.5, step T7): the text rule of `forms.ts` · `timeFormsOf`, and * the small spelling parts both halves share. * * Pattern: pure regular expressions over the text — no reading, no resolver. * Role: split from `forms.ts` BY FILE for the synchronous doors: the * evidence gate's exempt corpus (`evidence/evidenceIndex.ts` · * `addExempt`) runs on every agent with a gate, and a bundler places * a whole file on the synchronous graph — so the window half, which * re-resolves a recorded reading (`resolve.ts`), stays behind * `import()` (the optional-family law, * `test/trace-toolpack/browserGraph.test.ts`). Imports nothing. * Emits: N/A. * * @example * ```ts * timeFormsOfText('2026-10-09').said; // ['2026', '10', '9'] * timeFormsOfText('took 2h'); // { said: [], derived: [] } * ``` */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Spellings = exports.pad2 = exports.clockSpellings = exports.timeFormsOfText = void 0; // ─── The person's text ───────────────────────────────────────────────────── /** An ISO calendar date in text — `2026-10-09`, also the date half of `2026-10-09T08:00`. */ const ISO_DATE = /(?<![\d-])(\d{4})-(\d{2})-(\d{2})(?![\d])/g; /** * A 12-hour clock reading in text — `8 Am`, `8:40 AM`, `8pm`, `8:40 p.m.`. * Read off the TEXT, not tokens, because the suffix is its own token. */ const TWELVE_HOUR = /(?<![\d:.])(\d{1,2})(?::(\d{2}))?\s*([ap])\.?m(?![a-z])/gi; /** * The evidence tokenizer's boundary (`lib/normalize.ts` · `tokenize`), * restated for this leaf so a 24-hour reading is recognised as the same * whole token the gate compares — pinned equal by `test/time/forms.test.ts`. */ const NOT_IN_TOKEN = /[^A-Za-z0-9:_\-/.,%+@#$]+/; const LIST_COMMA = /,(?!\d)|(?<!\d),/; const LEADING = /^[$#@+'"`([{<]+/; /** The trailing decoration `normalize.ts` drops, as character codes. */ const TRAILING = new Set(Array.from('.,;:!?%\'"`)]}>', (char) => char.charCodeAt(0))); const TWENTY_FOUR_HOUR = /^(\d{1,2}):(\d{2})$/; /** * `piece.replace(/[.,;:!?%'"`)\]}>]+$/, '')`, walked in from the end once — * `normalize.ts` · `normalizeToken` drops the same run the same way. The * regex retried from every position of a long dot or comma run, which a token * can be. It is the walk of `lib/linearText.ts` · `trimTrailing`, restated * because this folder imports nothing; its counted work is pinned with the * other doors in `test/security/linear-text.test.ts`. */ function withoutTrailingDecoration(piece) { let end = piece.length; while (end > 0 && TRAILING.has(piece.charCodeAt(end - 1))) end--; return end === piece.length ? piece : piece.slice(0, end); } /** The whole tokens of `text` that are a 24-hour clock reading, lower-cased as the gate reads them. */ function clockTokens(text) { const out = []; for (const rough of text.split(NOT_IN_TOKEN)) { for (const piece of rough.split(LIST_COMMA)) { const token = withoutTrailingDecoration(piece.toLowerCase().trim().replace(LEADING, '')); if (TWENTY_FOUR_HOUR.test(token)) out.push(token); } } return out; } /** * The spellings of the times written in `text` — `said` only (the text rule of * `forms.ts` · `timeFormsOf`, which delegates here): an ISO date → its year, * month and day; a 12-hour or 24-hour clock reading → its spellings. Slash * dates and durations are never read. * * @example * ```ts * timeFormsOfText('what connected 8 Am to 8:40 AM PST').said; * // ['8:00', '08:00', '8:00am', '8:40', '08:40', '8:40am'] * ``` */ function timeFormsOfText(text) { return { said: textForms(text), derived: [] }; } exports.timeFormsOfText = timeFormsOfText; /** The said spellings of every date and clock time written in `text` (the file header's text rule). */ function textForms(text) { const out = new Spellings(); for (const [, year = '', mm, dd] of text.matchAll(ISO_DATE)) { const month = Number(mm); const day = Number(dd); if (month < 1 || month > 12 || day < 1 || day > 31) continue; out.push(year, String(month), String(day)); } for (const [, hh, mm, half = ''] of text.matchAll(TWELVE_HOUR)) { const hour = Number(hh); const minutes = mm ?? '00'; if (hour < 1 || hour > 12 || Number(minutes) > 59) continue; const h24 = (hour % 12) + (half.toLowerCase() === 'p' ? 12 : 0); out.push(...clockSpellings(h24, minutes)); } // A reading the 12-hour pass already took is not read again as 24-hour: // `8:40 p.m.` is 20:40, never also 08:40. for (const token of clockTokens(text.replace(TWELVE_HOUR, ' '))) { const [, hh = '', minutes = ''] = TWENTY_FOUR_HOUR.exec(token) ?? []; const h24 = Number(hh); if (h24 > 23 || Number(minutes) > 59) continue; out.push(...clockSpellings(h24, minutes)); } return out.list(); } /** * The spellings of ONE clock time: the 24-hour form bare and padded * (`8:00`, `08:00`, `20:00`), the 12-hour colon form (`8:00`) and its glued * suffix form (`8:00pm`). After tokenizing, `8:00 PM` and `8:00 AM` both read * `8:00` — the same hour on a 12-hour dial. */ function clockSpellings(h24, minutes) { const h12 = h24 % 12 === 0 ? 12 : h24 % 12; const suffix = h24 < 12 ? 'am' : 'pm'; return [ `${h24}:${minutes}`, `${(0, exports.pad2)(h24)}:${minutes}`, `${h12}:${minutes}`, `${h12}:${minutes}${suffix}`, ]; } exports.clockSpellings = clockSpellings; // ─── Small parts ───────────────────────────────────────────────────────────── const pad2 = (n) => String(n).padStart(2, '0'); exports.pad2 = pad2; /** An insertion-ordered set of spellings. */ class Spellings { seen = new Set(); push(...forms) { for (const form of forms) if (form !== '') this.seen.add(form); } list() { return [...this.seen]; } } exports.Spellings = Spellings; //# sourceMappingURL=textForms.js.map