agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
109 lines • 4.7 kB
JavaScript
/**
* time/readingAsk — the choices an OPEN reading offers (time design
* § 6.1, § 6.3): the one field a `time-reading` row whose choice is `open`
* needs.
*
* Pattern: a pure builder over the row — the candidates as labelled choices,
* a zone asked as `format: 'zone'`, a `model` reading offered to
* confirm. The judge of the answer stays `ask.ts` ·
* `checkTimeAnswer`.
* Role: L0 leaf (the time layer). Split from `ask.ts` because its
* labels render through `present.ts` (`presentRange`), and `ask.ts`
* is on the resume door's SYNCHRONOUS path (`tools/inputRequest.ts`)
* while the renderer loads only under `.time()` — the
* optional-family law of docs-next's site budget.
* Emits: N/A.
*
* @example
* ```ts
* const ask = timeAskOf(row, defaultTimeAskMessages);
* ask?.field.format; // 'time-range' — its `enum` the candidates left
* ```
*/
import { fillMessage, quoted } from './ask.js';
import { spellRange } from './range.js';
import { presentRange } from './present.js';
import { shownGrain } from './resolveRecord.js';
/** The zone token the person wrote in the mention's first parse that has one. */
function saidZoneToken(row) {
for (const parts of row.parses ?? []) {
const token = parts.zoneToken ?? parts.rangeOf?.[0].zoneToken ?? parts.rangeOf?.[1].zoneToken;
if (token !== undefined)
return token;
}
return '';
}
/**
* The ask a `time-reading` row needs, or `undefined` when its reading is
* settled (`only`, `policy`) or is no person's words (a refused mention).
* Words the library holds NO reading of (`none`: unreadable, no candidate)
* are asked too — which time the person meant, free entry, nothing
* pre-filled (`ask.which`, `format: 'time-range'`): the person wrote a time,
* and a tool's default never stands in for it. `messages` is the whole
* catalog (the caller composes the app's overrides over
* `defaultTimeAskMessages`); `id` names the field.
*/
export function timeAskOf(row, messages, id = 'time') {
const choice = row.choice;
if (row.quote === undefined || row.refused !== undefined)
return undefined;
if (choice?.by === 'none') {
return {
question: fillMessage(messages['ask.which'], { quote: quoted(row.quote) }),
field: { id, type: 'string', required: true, format: 'time-range' },
};
}
if (choice?.by !== 'open')
return undefined;
const quote = quoted(row.quote);
if (choice.open.includes('zone')) {
// A mention that names no zone waits on the PERSON's zone (the clock's is unknown, G15).
const token = saidZoneToken(row);
return {
question: token === ''
? fillMessage(messages['ask.zone-unknown'], { quote })
: fillMessage(messages['ask.zone'], { quote, token: quoted(token) }),
field: { id, type: 'string', required: true, format: 'zone' },
};
}
const candidates = row.candidates ?? [];
const seen = new Set();
const offered = [];
for (const index of choice.remaining) {
const candidate = candidates[index];
if (candidate === undefined)
continue;
const value = spellRange(candidate.range);
if (seen.has(value))
continue;
seen.add(value);
offered.push({ value, candidate });
}
if (offered.length === 0)
return undefined;
// The row owns "a reading needs confirming" (`rowsBuild.ts` · `timeReadingRows` puts 'confirm'
// on every reading's open choice); this only reads it.
const confirm = choice.open.includes('confirm');
const labels = offered.map(({ candidate }) => {
// The end as said when it was widened, a look-back's at now: "8 AM to 9 AM" is shown
// 8:00 – 9:00, not 8:59; "last 40 minutes" 8:00 – 8:40, not 8:40:00.001.
const window = presentRange(candidate.range, { zone: candidate.zone, locale: row.reader.locale }, shownGrain(candidate));
if (!confirm)
return window;
// The zone is named: the reading leaned on it, and the person may have meant another.
return fillMessage(messages['choice.confirm'], { quote, window, zone: candidate.zone });
});
const question = fillMessage(messages[confirm ? 'ask.confirm' : 'ask.which'], { quote });
return {
question,
field: {
id,
type: 'string',
required: true,
format: 'time-range',
enum: offered.map((o) => o.value),
labels,
},
};
}
//# sourceMappingURL=readingAsk.js.map