agentfootprint 9.131.0 → 9.132.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CAPABILITIES.md +3 -2
- package/ai-instructions/claude-code/SKILL.md +2 -2
- package/dist/core/agent/arguments/ask.js +225 -48
- package/dist/core/agent/arguments/ask.js.map +1 -1
- package/dist/core/agent/arguments/serve.js +169 -56
- package/dist/core/agent/arguments/serve.js.map +1 -1
- package/dist/core/agent/coverage/period.js +47 -2
- package/dist/core/agent/coverage/period.js.map +1 -1
- package/dist/core/agent/stages/argumentAsk.js +2 -0
- package/dist/core/agent/stages/argumentAsk.js.map +1 -1
- package/dist/core/slots/buildToolsSlot.js +3 -1
- package/dist/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/core/time/bind.js +41 -40
- package/dist/core/time/bind.js.map +1 -1
- package/dist/core/time/convert.js +74 -14
- package/dist/core/time/convert.js.map +1 -1
- package/dist/core/time/forms.js +7 -5
- package/dist/core/time/forms.js.map +1 -1
- package/dist/core/time/periodForm.js +1 -0
- package/dist/core/time/periodForm.js.map +1 -1
- package/dist/core/time/reader.js +3 -2
- package/dist/core/time/reader.js.map +1 -1
- package/dist/core/time/readers/english.js +46 -23
- package/dist/core/time/readers/english.js.map +1 -1
- package/dist/core/time/readingAsk.js +4 -1
- package/dist/core/time/readingAsk.js.map +1 -1
- package/dist/core/time/resolve.js +197 -63
- package/dist/core/time/resolve.js.map +1 -1
- package/dist/core/time/resolveRecord.js +103 -6
- package/dist/core/time/resolveRecord.js.map +1 -1
- package/dist/core/time/rows.js +7 -2
- package/dist/core/time/rows.js.map +1 -1
- package/dist/core/time/rowsBuild.js +1 -1
- package/dist/core/time/rowsBuild.js.map +1 -1
- package/dist/core/time/windows.js +50 -0
- package/dist/core/time/windows.js.map +1 -1
- package/dist/core/time/zone.js +105 -1
- package/dist/core/time/zone.js.map +1 -1
- package/dist/doors/time.js +62 -0
- package/dist/doors/time.js.map +1 -0
- package/dist/esm/core/agent/arguments/ask.d.ts +11 -5
- package/dist/esm/core/agent/arguments/ask.js +227 -50
- package/dist/esm/core/agent/arguments/ask.js.map +1 -1
- package/dist/esm/core/agent/arguments/serve.d.ts +48 -19
- package/dist/esm/core/agent/arguments/serve.js +166 -54
- package/dist/esm/core/agent/arguments/serve.js.map +1 -1
- package/dist/esm/core/agent/coverage/period.d.ts +6 -1
- package/dist/esm/core/agent/coverage/period.js +47 -2
- package/dist/esm/core/agent/coverage/period.js.map +1 -1
- package/dist/esm/core/agent/stages/argumentAsk.js +2 -0
- package/dist/esm/core/agent/stages/argumentAsk.js.map +1 -1
- package/dist/esm/core/slots/buildToolsSlot.js +3 -1
- package/dist/esm/core/slots/buildToolsSlot.js.map +1 -1
- package/dist/esm/core/time/bind.d.ts +10 -6
- package/dist/esm/core/time/bind.js +42 -41
- package/dist/esm/core/time/bind.js.map +1 -1
- package/dist/esm/core/time/convert.d.ts +44 -1
- package/dist/esm/core/time/convert.js +72 -14
- package/dist/esm/core/time/convert.js.map +1 -1
- package/dist/esm/core/time/forms.js +8 -6
- package/dist/esm/core/time/forms.js.map +1 -1
- package/dist/esm/core/time/periodForm.d.ts +7 -2
- package/dist/esm/core/time/periodForm.js +1 -0
- package/dist/esm/core/time/periodForm.js.map +1 -1
- package/dist/esm/core/time/reader.d.ts +7 -0
- package/dist/esm/core/time/reader.js +3 -2
- package/dist/esm/core/time/reader.js.map +1 -1
- package/dist/esm/core/time/readers/english.d.ts +8 -6
- package/dist/esm/core/time/readers/english.js +46 -23
- package/dist/esm/core/time/readers/english.js.map +1 -1
- package/dist/esm/core/time/readingAsk.js +4 -1
- package/dist/esm/core/time/readingAsk.js.map +1 -1
- package/dist/esm/core/time/resolve.d.ts +31 -17
- package/dist/esm/core/time/resolve.js +197 -65
- package/dist/esm/core/time/resolve.js.map +1 -1
- package/dist/esm/core/time/resolveRecord.d.ts +90 -5
- package/dist/esm/core/time/resolveRecord.js +99 -5
- package/dist/esm/core/time/resolveRecord.js.map +1 -1
- package/dist/esm/core/time/rows.d.ts +9 -3
- package/dist/esm/core/time/rows.js +7 -2
- package/dist/esm/core/time/rows.js.map +1 -1
- package/dist/esm/core/time/rowsBuild.js +1 -1
- package/dist/esm/core/time/rowsBuild.js.map +1 -1
- package/dist/esm/core/time/windows.d.ts +28 -0
- package/dist/esm/core/time/windows.js +50 -0
- package/dist/esm/core/time/windows.js.map +1 -1
- package/dist/esm/core/time/zone.d.ts +32 -0
- package/dist/esm/core/time/zone.js +102 -0
- package/dist/esm/core/time/zone.js.map +1 -1
- package/dist/esm/doors/time.d.ts +51 -0
- package/dist/esm/doors/time.js +52 -0
- package/dist/esm/doors/time.js.map +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/core/agent/arguments/ask.d.ts +11 -5
- package/dist/types/core/agent/arguments/ask.d.ts.map +1 -1
- package/dist/types/core/agent/arguments/serve.d.ts +48 -19
- package/dist/types/core/agent/arguments/serve.d.ts.map +1 -1
- package/dist/types/core/agent/coverage/period.d.ts +6 -1
- package/dist/types/core/agent/coverage/period.d.ts.map +1 -1
- package/dist/types/core/agent/stages/argumentAsk.d.ts.map +1 -1
- package/dist/types/core/slots/buildToolsSlot.d.ts.map +1 -1
- package/dist/types/core/time/bind.d.ts +10 -6
- package/dist/types/core/time/bind.d.ts.map +1 -1
- package/dist/types/core/time/convert.d.ts +44 -1
- package/dist/types/core/time/convert.d.ts.map +1 -1
- package/dist/types/core/time/forms.d.ts.map +1 -1
- package/dist/types/core/time/periodForm.d.ts +7 -2
- package/dist/types/core/time/periodForm.d.ts.map +1 -1
- package/dist/types/core/time/reader.d.ts +7 -0
- package/dist/types/core/time/reader.d.ts.map +1 -1
- package/dist/types/core/time/readers/english.d.ts +8 -6
- package/dist/types/core/time/readers/english.d.ts.map +1 -1
- package/dist/types/core/time/readingAsk.d.ts.map +1 -1
- package/dist/types/core/time/resolve.d.ts +31 -17
- package/dist/types/core/time/resolve.d.ts.map +1 -1
- package/dist/types/core/time/resolveRecord.d.ts +90 -5
- package/dist/types/core/time/resolveRecord.d.ts.map +1 -1
- package/dist/types/core/time/rows.d.ts +9 -3
- package/dist/types/core/time/rows.d.ts.map +1 -1
- package/dist/types/core/time/rowsBuild.d.ts.map +1 -1
- package/dist/types/core/time/windows.d.ts +28 -0
- package/dist/types/core/time/windows.d.ts.map +1 -1
- package/dist/types/core/time/zone.d.ts +32 -0
- package/dist/types/core/time/zone.d.ts.map +1 -1
- package/dist/types/doors/time.d.ts +52 -0
- package/dist/types/doors/time.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +14 -1
package/AGENTS.md
CHANGED
|
@@ -35,9 +35,9 @@ only *labels* a spinning run; `maxIterations` is the hard stop), and there is **
|
|
|
35
35
|
automatic re-delivery of an ageing skill body** (`refreshPolicy` is stored and never
|
|
36
36
|
read on any version — use `surfaceMode: 'both'`).
|
|
37
37
|
|
|
38
|
-
## Subpath map —
|
|
38
|
+
## Subpath map — 18 doors
|
|
39
39
|
|
|
40
|
-
`agentfootprint` (main barrel: `Agent`, `LLMCall`, `defineTool`, control flow, patterns, `defineRAG`, pause/resume) · `/providers` (`mock`, `anthropic`, `openai`, `bedrock`, `ollama`, `mcpClient`, embedders — every provider, so bundlers never walk the vendor SDKs from the main barrel) · `/context` (`defineSkill`, `defineFact`, `defineSteering`, `defineInstruction`, `skillGraph`, `skillsFromDir`, the scorers) · `/memory` (`defineMemory`, `InMemoryStore`, `mockEmbedder`, the stores) · `/rag` (stores + loaders; `defineRAG` itself is on the main barrel) · `/observe` (recorders, tracing, `RunStep`) · `/resilience` (provider decorators) · `/reliability` (the rules-based fail-fast gate) · `/cache` (prefix-cache strategies; importing it registers them) · `/security` · `/hosting` · `/events` · `/skill-graph` (the routing layer with no framework attached, for a host that is not this agent) · `/recipes` (`defineAgentRecipe` — one named, versioned agent setup, applied with `.recipe()`) · `/maps` (`Claim<T>` and the map-engagement vocabulary behind `.maps()`) · `/classify` (the calibrated-classifier port: `Classifier`, `mockClassifier`) · `/ontology` (`defineOntology` — a declared map of the domain, mounted with `.ontology()`). `package.json` `exports` is the complete list; a path not on it does not resolve.
|
|
40
|
+
`agentfootprint` (main barrel: `Agent`, `LLMCall`, `defineTool`, control flow, patterns, `defineRAG`, pause/resume) · `/providers` (`mock`, `anthropic`, `openai`, `bedrock`, `ollama`, `mcpClient`, embedders — every provider, so bundlers never walk the vendor SDKs from the main barrel) · `/context` (`defineSkill`, `defineFact`, `defineSteering`, `defineInstruction`, `skillGraph`, `skillsFromDir`, the scorers) · `/memory` (`defineMemory`, `InMemoryStore`, `mockEmbedder`, the stores) · `/rag` (stores + loaders; `defineRAG` itself is on the main barrel) · `/observe` (recorders, tracing, `RunStep`) · `/resilience` (provider decorators) · `/reliability` (the rules-based fail-fast gate) · `/cache` (prefix-cache strategies; importing it registers them) · `/security` · `/hosting` · `/events` · `/skill-graph` (the routing layer with no framework attached, for a host that is not this agent) · `/recipes` (`defineAgentRecipe` — one named, versioned agent setup, applied with `.recipe()`) · `/maps` (`Claim<T>` and the map-engagement vocabulary behind `.maps()`) · `/classify` (the calibrated-classifier port: `Classifier`, `mockClassifier`) · `/ontology` (`defineOntology` — a declared map of the domain, mounted with `.ontology()`) · `/time` (`convertForTool` + `sugarForms` + `granularityMsOf` — the time layer's own answer to whether and how a tool reads a window, for an app outside a run; the steps `convertExact`, `convertWidened`, `periodFactProblem` too). `package.json` `exports` is the complete list; a path not on it does not resolve.
|
|
41
41
|
|
|
42
42
|
## Core Concepts
|
|
43
43
|
|
package/CAPABILITIES.md
CHANGED
|
@@ -55,13 +55,14 @@ not in this table, search `src/index.ts` for the nearest noun before writing cod
|
|
|
55
55
|
| a chart or check comparing a dataset's time values as spellings — epoch seconds beside ISO strings, `+02:00` beside `Z`, a wall-clock string with no offset silently read as UTC, two `01:30` rows in the hour the clocks go back; a READ-SIDE view (the stored rows are never rewritten): UTC instants at one precision, sorted, each with its row; an offset-less value under a zone-less axis is counted (`status: 'naive-values'`) or refused (`naive: 'refuse'`), never read as UTC; a fall-back wall time placed by row order and noted, else counted `dstAmbiguous`; a skipped one `dstGap` (the axis is re-exported from `src/artifacts/`) | `normaliseInstants` + `NormalisedAxis` | `src/core/time/axis.ts` | 9.129.0 |
|
|
56
56
|
| a second parser for an instant, a look-back like `30m`/`24h`, a time zone or a `from..to` range inside the library — three separately written grammars used to disagree (an `iso-range` argument took 30 February, a declared period took a lower-case `t`); ONE owner now, with two named instant profiles (`lenient` for a period a result declares, `strict` for a value sent to a tool), a duration grammar with the unit set named per use (look-back `mhdw`, no digit cap; `s` only by opt-in), IANA zones through `Intl` with abbreviations and bare offsets refused, Temporal's four DST words, and one conversion per boundary a half-open range crosses; internal until the run clock ships | `instantOf` + `durationMs` / `spellDuration` + `isZoneName` / `wallToInstant` + `parseRange` / `spellRange` + `fromInclusive` / `toInclusive` | `src/core/time/` | 9.129.0 |
|
|
57
57
|
| a run that reads "now" and the person's time zone from the SERVER — "since 8" answered in the host's zone, a default nobody chose, a resume half an hour later silently re-anchoring the question — the clock as a declared, recorded input: the zone per run (`run({ time: { zone } })`), `.time({ zone })` an optional fallback, a run with neither REFUSED before the turn starts; `now` the app's (the message's time) else the turn's start, recorded `nowSource: 'default'`; one `clock` row per turn on the ledger (with a UI-set `time.window`, `source: 'control'`), one `call` row per dispatched call with `dispatchedAt`, a resume's differing `time` kept out and recorded as `clock-on-resume`; the limits block's `Period:` lines rendered in the run's zone with the zone named (`2026-10-09 08:00–08:40 America/Los_Angeles (UTC-07:00)`) while the typed record keeps the instants; nothing served to the model | `AgentBuilder.time()` + `AgentInput.time` / `AgentRunOptions.time` (`RunTime`) + `TimeClock` + `ClockRow` / `ClockOnResumeRow` / `CallRow` | `src/core/time/clock.ts` | 9.129.0 |
|
|
58
|
-
| an app's date parser silently choosing MDY, reading `8:40` as morning, guessing a year, taking `PST` as a zone, or re-reading a message differently on a resume — the person's words read ONLY through a reader the app arms (`.time({ reader })`, no default), which returns zone-less PARTS and a verbatim quote (checked: an out-of-text quote is refused and keeps no text); the library resolves every candidate window against the run clock (three date orders, am and pm, both instants of a DST overlap or gap, a day word, a look-back, a range read to the end of its grain; an abbreviation
|
|
58
|
+
| an app's date parser silently choosing MDY, reading `8:40` as morning, guessing a year, taking `PST` as a zone, or re-reading a message differently on a resume — the person's words read ONLY through a reader the app arms (`.time({ reader })`, no default), which returns zone-less PARTS and a verbatim quote (checked: an out-of-text quote is refused and keeps no text); the library resolves every candidate window against the run clock (three date orders, am and pm, both instants of a DST overlap or gap, a day word, a look-back, a range read to the end of its grain — an o'clock end ends AT the hour, "8 AM to 9 AM" is one hour; a place named with `time` read as the one zone the tz database names for it; an abbreviation asked unless the app maps it with `policy.abbreviations`, and then offered as its zone AND its letters' offset when they disagree); a v1 policy (`dateOrder`, `year`, both `'ask'` by default) removes readings and the removal is recorded; one `time-reading` row per mention (or one `mentions: 0` row), once per message — only a message a person wrote, never a composed run's — with the reader's id, version, kind and locale and the tz database version; a resume or retry reads the row, never the reader; a `kind: 'model'` reader's window is never `said` and waits for the person's confirmation; nothing served to the model | `TimeReader` + `TimeParts` + `TimePolicy` + `TimeCandidate` / `ReadingChoice` + `TimeReadingRow` (`.time({ reader, policy })`) | `src/core/time/resolve.ts` | 9.129.0 |
|
|
59
59
|
| every app re-checking a time answer the person typed into an ask — a window written backwards, a date-time with no offset, `PST` for a zone, a wall time the clocks skip — and re-asking in silence; a `requestInput` field declares a `format` — `'instant'`, `'time-range'` or `'zone'` — (refused unless `type: 'string'`; a malformed choice or supplied value refused at definition) and the resume door judges the answer with the time layer's one judge: a refused one is NOT taken — the same ask returns with `refused: { answer, reason }` and `repeat: { count }`, the field missing again, nothing run; the reason is a catalog sentence the app can reword (`.time({ messages })`), and only a check the app armed produces one (the DST check needs `.time()`'s zone); `labels` per choice, free entry open unless `strict`; over MCP a range travels as two `date-time` elicitation properties | `InputField.format` / `labels` / `strict` + `defaultTimeAskMessages` + `elicitationOf` / `answerFromElicitation` | `src/core/time/ask.ts` | 9.129.0 |
|
|
60
60
|
| a tool whose period is two epoch-millisecond arguments (or a joined range, a date, a look-back in seconds) being sent the person's window in the wrong shape, rounded silently, or overwritten — and every host tool re-parsing its own argument to say what its read covered; the tool DECLARES every shape its period takes (`forms`: `bounds` · `joined` · `object` · `day` · `lookback` with `units`; today's `{ argument, spelling }` is shorthand, plus `accepts`, `wall-range` + `zoneArgument`) and facts about its source (`direction`, `retention`, `maxRange`, `granularity`, `filtersToAsked`, `wallZone`), judged at definition, dispatch and MCP ingest (`_meta.agentfootprint.period`); under `.time()` a period the model left out is filled from the turn's ONE window of the person's (a settled reading, or a UI `time.window`) into the first form that holds it exactly, filed as theirs (`said` + `matched: 'mention'`; a model reader's as a reading; a UI window as `app`); two mentions fill nothing; a sent window is bound by quote or value, or runs as sent and is recorded `model-chosen` (standing "not sure"); one `call-window` row per call; the tool is handed `ctx.time` (`asked`, `zone`, `now`, `dispatchedAt`; over MCP in the call's `_meta.agentfootprint.time`); the tool's facts join the time answer's re-check | `ToolPeriod.forms` / `PeriodForm` / `PeriodFacts` + `ToolExecutionContext.time` (`TimeContext`) + `CallWindowRow` | `src/core/time/convert.ts` | 9.129.0 |
|
|
61
61
|
| a tool whose period cannot hold the person's window exactly being sent a narrower window, a silently wider one, or a window its source cannot hold — a look-back tool asked for "yesterday", a `day` tool asked for two days, a log store asked for tomorrow, a look-back re-read half an hour later after a pause; under `.time()` the fill takes the first exact form, else the first that holds MORE (a whole day; the covering look-back from now, never over `maxRange`) and records it (`call-window` `sent` + `differs.extra`, or `trimmedByTool` under `filtersToAsked`; the note says wider); a window outside `direction`, wholly beyond `retention`, over `maxRange`, spanning days for a `day`-only tool, or a sent wall time the zone skips is REFUSED before dispatch with a past-tense reason naming the declared fact (`how: 'refused'`); a window partly beyond `retention` runs, marked; after drift past the tool's step the library's own look-back fill is redrawn into an absolute form and the model's runs as sent (`call` row `drift`: `redrawn` or `shifted`) | `PeriodFacts` + `convertWidened` + `driftAtDispatch` + `CallWindowRow` + `CallRow.drift` | `src/core/time/` | 9.129.0 |
|
|
62
|
-
| reading a person's time words with a hand-rolled parser that guesses — `10/09/26` silently MDY, `PST` taken as a zone, "yesterday" read out of "yesterday morning" — then making the model re-derive the window from words, and asking about dates nobody needed; the library's careful English reader (no dependency, `kind: 'rule'`) TOKENIZES a small, closed set (ISO dates and instants, numeric dates with the order left open, clock times, a range of two, a zone written after them, today / yesterday / tomorrow, "last 40 minutes") and says "unreadable" — one mention, the whole phrase — for every other time phrase it recognises; a reading only PROPOSES (owner decision "Always confirm": nothing typed in chat is filed as the person's words) — when a tool that declares a period is about to be called, it is offered as a pre-filled, editable one-click confirmation naming its window AND its zone (the zone asked first for an abbreviation, the readings the tool's facts allow as labelled choices), written into the tool's own arguments and filed `answered` with a `time-answer` row (`confirmed` click or `edited`); from then on the turn's later calls are filled from it and it is served on every tool that declares a period, in that tool's own form, with its source; when every reading breaks the tool's facts the call is refused, nothing asked | `englishTimeReader` + the window field of the batch ask + the `time-answer` row + the served time sentence | `src/core/time/readers/english.ts` | 9.131.0 |
|
|
62
|
+
| reading a person's time words with a hand-rolled parser that guesses — `10/09/26` silently MDY, `PST` taken as a zone, "yesterday" read out of "yesterday morning" — then making the model re-derive the window from words, and asking about dates nobody needed; the library's careful English reader (no dependency, `kind: 'rule'`) TOKENIZES a small, closed set (ISO dates and instants, numeric dates with the order left open, clock times, a range of two, a zone written after them or after a day word — `yesterday London time` included, today / yesterday / tomorrow, "last 40 minutes") and says "unreadable" — one mention, the whole phrase — for every other time phrase it recognises; a reading only PROPOSES (owner decision "Always confirm": nothing typed in chat is filed as the person's words) — when a tool that declares a period is about to be called, it is offered as a pre-filled, editable one-click confirmation naming its window AND its zone (the zone asked first for an abbreviation, the readings the tool's facts allow as labelled choices), written into the tool's own arguments and filed `answered` with a `time-answer` row (`confirmed` click or `edited`); from then on the turn's later calls are filled from it and it is served on every tool that declares a period, in that tool's own form, with its source; when every reading breaks the tool's facts the call is refused, nothing asked | `englishTimeReader` + the window field of the batch ask + the `time-answer` row + the served time sentence | `src/core/time/readers/english.ts` | 9.131.0 |
|
|
63
63
|
| an answer about the person's time window marked "not traced" for `08:41`, `-07:00`, `PDT` or the epoch the call ran with — or, the other way, the model's echo of the library's served time line passing as the person's words; under `.time()` beside the evidence gate, ONE owner says which spelling of a time is whose: the parts the person wrote, at the grain they wrote them ("8 AM" is also `8:00`/`08:00`; a confirmed `10/09/26` is `2026-10-09`), and every part of a window they typed or set in a UI, are theirs (exempt); what the library produced from a reading (an implied year, the abbreviation in effect, offsets, UTC and epoch spellings, the end-of-grain minute, a look-back's duration, the values it filled, the served time line, any part of a `model` reading) is filed as ONE `time-derived` row and folds `derived-from-reading` — "not sure", never "known", never called invented; a time no reading produced is still flagged; the gate's private clock table is retired into the same owner | `timeFormsOf` + `TimeDerivedRow` + `derived-from-reading` + `EvidenceVerdict.derived` | `src/core/time/forms.ts` | 9.131.0 |
|
|
64
64
|
| an answer standing on a read that is not the window the person asked about — a store that clamped 30 days to 7, a covering look-back that read two days for one, a look-back re-read half an hour late after a pause, a model's drill-down reported as the person's window, a window older than the source keeps — while the answer reads as if it covered the ask; under `.time()` the results layer compares each call's READ (the result's declared `period.queried`, read back with the tool's `granularity`; else the widened fill; else the look-back shifted by its drift) with what it ASKED (for a window the model chose, with the person's): `missing` (asked, not read) and `extra` (read, not asked) on the call's `period` row (`differs`, plus `shifted`, `beyondRetention`, `partlyBeyondRetention`), the event names the checks (never a range), the standing folds `period-differs-from-asked` (TQ8: a wider read too, unless the result declares it read exactly what was asked) and `period-beyond-retention` — "not sure" — and the limits block says each in the person's zone; a dataset whose declared axis names a zone files a `source-clock` row and a `Clocks` line (two zones: the clocks differ — a label only); a period's offset is never read as a clock | `PeriodRow.differs` / `shifted` / `beyondRetention` + `period-differs-from-asked` + `period-beyond-retention` + `SourceClockRow` | `src/core/time/check.ts` | 9.131.0 |
|
|
65
|
+
| an app keeping its own copy of the library's period conversion — to preview what a tool would read for the person's window, or to build a call itself — and drifting from what the fill, the time ask and the served line send; the same question the time layer asks of each tool, over the tool's own `period` and a clock the app passes: a fact the window breaks, else the first form that holds it EXACTLY, else the first that reads MORE within `maxRange` (with the range it reads and what it adds, marked when partly older than `retention`), else `multi-day` / `no-form-holds` (`convertForTool`, the forms and step read the run's way by `sugarForms` / `granularityMsOf`); the steps alone too (`convertExact`, `convertWidened`, `periodFactProblem`) — no run, no record, no `.time()` | `convertForTool` + `sugarForms` + `granularityMsOf` (+ `convertExact` / `convertWidened` / `widestMsOf` / `periodFactProblem`) (`agentfootprint/time`) | `src/doors/time.ts` | 9.132.0 |
|
|
65
66
|
| letting the UI draw an artifact without the model naming a component | `registerArtifactComponent` (in the `agentfootprint-lens` package) | — | — |
|
|
66
67
|
| finding which tools the model keeps writing by hand | `agentfootprint.tools.code_run` + `codeShape` | `src/core/codeRunnerTool.ts` | 9.46.0 |
|
|
67
68
|
| a skill wrongly activated by a keyword staying loaded for the whole turn — suspending/parking a map's prompt + tools when its contribution goes unused, WITHOUT touching its cursor, and re-engaging it on evidence | `.maps({ renewalGrace })` (default 3) + `MountedMap.nonParkable` + `advanceEngagement` + `agentfootprint.map.engaged/parked` + skip reason `'parked'` | `src/maps/` | 9.58.0 |
|
|
@@ -35,9 +35,9 @@ only *labels* a spinning run; `maxIterations` is the hard stop), and there is **
|
|
|
35
35
|
automatic re-delivery of an ageing skill body** (`refreshPolicy` is stored and never
|
|
36
36
|
read on any version — use `surfaceMode: 'both'`).
|
|
37
37
|
|
|
38
|
-
## Subpath map —
|
|
38
|
+
## Subpath map — 18 doors
|
|
39
39
|
|
|
40
|
-
`agentfootprint` (main barrel: `Agent`, `LLMCall`, `defineTool`, control flow, patterns, `defineRAG`, pause/resume) · `/providers` (`mock`, `anthropic`, `openai`, `bedrock`, `ollama`, `mcpClient`, embedders — every provider, so bundlers never walk the vendor SDKs from the main barrel) · `/context` (`defineSkill`, `defineFact`, `defineSteering`, `defineInstruction`, `skillGraph`, `skillsFromDir`, the scorers) · `/memory` (`defineMemory`, `InMemoryStore`, `mockEmbedder`, the stores) · `/rag` (stores + loaders; `defineRAG` itself is on the main barrel) · `/observe` (recorders, tracing, `RunStep`) · `/resilience` (provider decorators) · `/reliability` (the rules-based fail-fast gate) · `/cache` (prefix-cache strategies; importing it registers them) · `/security` · `/hosting` · `/events` · `/skill-graph` (the routing layer with no framework attached, for a host that is not this agent) · `/recipes` (`defineAgentRecipe` — one named, versioned agent setup, applied with `.recipe()`) · `/maps` (`Claim<T>` and the map-engagement vocabulary behind `.maps()`) · `/classify` (the calibrated-classifier port: `Classifier`, `mockClassifier`) · `/ontology` (`defineOntology` — a declared map of the domain, mounted with `.ontology()`). `package.json` `exports` is the complete list; a path not on it does not resolve.
|
|
40
|
+
`agentfootprint` (main barrel: `Agent`, `LLMCall`, `defineTool`, control flow, patterns, `defineRAG`, pause/resume) · `/providers` (`mock`, `anthropic`, `openai`, `bedrock`, `ollama`, `mcpClient`, embedders — every provider, so bundlers never walk the vendor SDKs from the main barrel) · `/context` (`defineSkill`, `defineFact`, `defineSteering`, `defineInstruction`, `skillGraph`, `skillsFromDir`, the scorers) · `/memory` (`defineMemory`, `InMemoryStore`, `mockEmbedder`, the stores) · `/rag` (stores + loaders; `defineRAG` itself is on the main barrel) · `/observe` (recorders, tracing, `RunStep`) · `/resilience` (provider decorators) · `/reliability` (the rules-based fail-fast gate) · `/cache` (prefix-cache strategies; importing it registers them) · `/security` · `/hosting` · `/events` · `/skill-graph` (the routing layer with no framework attached, for a host that is not this agent) · `/recipes` (`defineAgentRecipe` — one named, versioned agent setup, applied with `.recipe()`) · `/maps` (`Claim<T>` and the map-engagement vocabulary behind `.maps()`) · `/classify` (the calibrated-classifier port: `Classifier`, `mockClassifier`) · `/ontology` (`defineOntology` — a declared map of the domain, mounted with `.ontology()`) · `/time` (`convertForTool` + `sugarForms` + `granularityMsOf` — the time layer's own answer to whether and how a tool reads a window, for an app outside a run; the steps `convertExact`, `convertWidened`, `periodFactProblem` too). `package.json` `exports` is the complete list; a path not on it does not resolve.
|
|
41
41
|
|
|
42
42
|
## Core Concepts
|
|
43
43
|
|
|
@@ -56,10 +56,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
56
56
|
exports.settledCalls = exports.WINDOW_ZONE_EXPECTATION = exports.WINDOW_FORM_EXPECTATION = exports.bindAnswer = exports.factExpectation = exports.checkAnswer = exports.memberValue = exports.readAskAnswer = exports.isArgumentAsk = exports.withWaiting = exports.argumentAskDeclaration = exports.judgeAskContextHook = exports.nextAskRound = exports.initialAskState = exports.planAskFields = exports.periodsShareField = exports.convertSpelling = exports.ARGUMENT_REASK_QUESTION = exports.ARGUMENT_ASK_QUESTION = exports.ARGUMENT_ASK_ID = exports.ASK_CONTEXT_CHARS = exports.MAX_ASK_FIELDS = exports.MAX_ASK_ROUNDS = exports.ASK_CONTEXT_KEY = exports.ARGUMENT_ASK_KIND = void 0;
|
|
57
57
|
const inputRequest_js_1 = require("../../inputRequest.js");
|
|
58
58
|
const convert_js_1 = require("../../time/convert.js");
|
|
59
|
+
const rowsBuild_js_1 = require("../../time/rowsBuild.js");
|
|
60
|
+
const instant_js_1 = require("../../time/instant.js");
|
|
59
61
|
const range_js_1 = require("../../time/range.js");
|
|
60
62
|
const resolve_js_1 = require("../../time/resolve.js");
|
|
61
63
|
const readingAsk_js_1 = require("../../time/readingAsk.js");
|
|
62
64
|
const rows_js_1 = require("../../time/rows.js");
|
|
65
|
+
const zone_js_1 = require("../../time/zone.js");
|
|
63
66
|
const toolShownArgs_js_1 = require("../../toolShownArgs.js");
|
|
64
67
|
const toolArgsValidation_js_1 = require("../toolArgsValidation.js");
|
|
65
68
|
const declare_js_1 = require("./declare.js");
|
|
@@ -89,21 +92,46 @@ exports.ARGUMENT_ASK_QUESTION = 'Before the next step can run, a few values are
|
|
|
89
92
|
/** The question of a re-ask, after an answer that did not fit the tool's own schema. */
|
|
90
93
|
exports.ARGUMENT_REASK_QUESTION = 'One answer did not fit what the tool accepts. Please answer that field again.';
|
|
91
94
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
95
|
+
* Why one member tool cannot read a window the ask would put into it — the
|
|
96
|
+
* one owner's answer (`core/time/convert.ts` · `convertForTool`: a fact it
|
|
97
|
+
* breaks, `multi-day`, or `no-form-holds`) — or `undefined` when it can. A
|
|
98
|
+
* tool whose rules cannot be read has nothing to say here.
|
|
96
99
|
*/
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
return
|
|
100
|
+
function unreadableBy(toolName, range, zone, toolOf, time) {
|
|
101
|
+
const rules = (0, declare_js_1.rulesOf)(toolOf(toolName));
|
|
102
|
+
if (rules === undefined || (0, declare_js_1.isRefused)(rules))
|
|
103
|
+
return undefined;
|
|
104
|
+
const facts = (0, declare_js_1.periodFactsOf)(rules.period);
|
|
105
|
+
const at = zone ?? time.zone;
|
|
106
|
+
if (at === undefined) {
|
|
107
|
+
const problem = (0, convert_js_1.periodFactProblem)(range, facts, time.now);
|
|
108
|
+
return problem === undefined ? undefined : { refused: problem, facts };
|
|
109
|
+
}
|
|
110
|
+
const read = (0, convert_js_1.convertForTool)({ range }, (0, declare_js_1.periodFormsOf)(rules.period), facts, {
|
|
111
|
+
now: time.now,
|
|
112
|
+
zone: at,
|
|
113
|
+
...(time.appZone !== undefined && { appZone: time.appZone }),
|
|
114
|
+
granularityMs: (0, convert_js_1.granularityMsOf)(facts),
|
|
101
115
|
});
|
|
116
|
+
return 'refused' in read ? { refused: read.refused, facts } : undefined;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The indexes of the choices EVERY member tool can read (§ 6.3: "some
|
|
120
|
+
* readings inside the tool's `direction` → the ones inside as choices" — and,
|
|
121
|
+
* by the same law, a reading no form of the tool can hold is not offered) —
|
|
122
|
+
* `undefined` when none is left (several tools that disagree: every choice
|
|
123
|
+
* is offered, and the answer is judged against each tool).
|
|
124
|
+
*/
|
|
125
|
+
function allowedChoices(choices, zones, members, toolOf, time) {
|
|
126
|
+
const names = [...new Set(members.map((m) => m.toolName))];
|
|
102
127
|
const kept = choices.flatMap((value, i) => {
|
|
103
128
|
const range = (0, range_js_1.parseRange)(value);
|
|
104
129
|
if (range === undefined)
|
|
105
130
|
return [];
|
|
106
|
-
|
|
131
|
+
const zone = zones?.[i];
|
|
132
|
+
return names.every((name) => unreadableBy(name, range, zone, toolOf, time) === undefined)
|
|
133
|
+
? [i]
|
|
134
|
+
: [];
|
|
107
135
|
});
|
|
108
136
|
return kept.length > 0 ? kept : undefined;
|
|
109
137
|
}
|
|
@@ -269,7 +297,7 @@ function planAskFields(asks, calls, toolOf, windowPlan) {
|
|
|
269
297
|
const window = windowFieldOf(windowPlan, windowMembers);
|
|
270
298
|
const kept = window.choices === undefined
|
|
271
299
|
? undefined
|
|
272
|
-
: allowedChoices(window.choices, window.members, toolOf, windowPlan
|
|
300
|
+
: allowedChoices(window.choices, windowPlan.zones, window.members, toolOf, windowPlan);
|
|
273
301
|
return [narrowed(window, kept), ...fields];
|
|
274
302
|
}
|
|
275
303
|
exports.planAskFields = planAskFields;
|
|
@@ -559,6 +587,96 @@ function factBroken(toolOf, member, value, time) {
|
|
|
559
587
|
}
|
|
560
588
|
return undefined;
|
|
561
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* The period fields whose answers break a tool's fact only TOGETHER — one
|
|
592
|
+
* bound of two (`start_time`, `end_time`) is no window, so {@link checkAnswer}
|
|
593
|
+
* cannot judge it alone (`factBroken` reads single-argument forms only). Per
|
|
594
|
+
* call, the answers this pass carries and the ones an earlier round bound are
|
|
595
|
+
* written over the call's own arguments; every `bounds` / `object` form whose
|
|
596
|
+
* arguments are then all present is read back (`convert.ts` · `readBack`) and
|
|
597
|
+
* judged against `direction`, `retention` and `maxRange` at the turn's clock
|
|
598
|
+
* (`periodFactProblem`). A pair that breaks one sends each of its fields
|
|
599
|
+
* asked in this pass back to be asked again, naming the fact; a pair whose
|
|
600
|
+
* bounds are all present yet name no window (the end given before the start)
|
|
601
|
+
* goes back the same way, naming the window form — the same
|
|
602
|
+
* bounded re-ask a single argument gets, so a future start and end the
|
|
603
|
+
* person gave never reach a tool whose source holds only the past, with or
|
|
604
|
+
* without the reader.
|
|
605
|
+
*/
|
|
606
|
+
function pairsBroken(state, values, byId, toolOf, time) {
|
|
607
|
+
const broken = new Map();
|
|
608
|
+
const waiting = state.waiting;
|
|
609
|
+
if (time === undefined || waiting === undefined)
|
|
610
|
+
return broken;
|
|
611
|
+
const now = new Map(waiting.fieldIndexes.map((index, k) => [index, values[`f${k + 1}`]]));
|
|
612
|
+
// Per call: its tool, and each period argument's answer with the field it came from.
|
|
613
|
+
const perCall = new Map();
|
|
614
|
+
state.fields.forEach((field, index) => {
|
|
615
|
+
if (field.window !== undefined)
|
|
616
|
+
return;
|
|
617
|
+
const answer = now.has(index) ? now.get(index) : state.progress[index]?.answer;
|
|
618
|
+
if (answer === undefined)
|
|
619
|
+
return;
|
|
620
|
+
for (const member of field.members) {
|
|
621
|
+
if (member.period !== true)
|
|
622
|
+
continue;
|
|
623
|
+
const value = memberValue(field, member, answer);
|
|
624
|
+
if (value === undefined)
|
|
625
|
+
continue;
|
|
626
|
+
for (const id of member.toolCallIds) {
|
|
627
|
+
const entry = perCall.get(id) ?? { toolName: member.toolName, answers: new Map() };
|
|
628
|
+
entry.answers.set(member.argument, { value, field: index });
|
|
629
|
+
perCall.set(id, entry);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
for (const [id, { toolName, answers }] of perCall) {
|
|
634
|
+
const call = byId.get(id);
|
|
635
|
+
const rules = (0, declare_js_1.rulesOf)(toolOf(toolName));
|
|
636
|
+
if (call === undefined || rules === undefined || (0, declare_js_1.isRefused)(rules))
|
|
637
|
+
continue;
|
|
638
|
+
const facts = (0, declare_js_1.periodFactsOf)(rules.period);
|
|
639
|
+
if (facts.direction === undefined &&
|
|
640
|
+
facts.retention === undefined &&
|
|
641
|
+
facts.maxRange === undefined) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
const args = { ...call.args };
|
|
645
|
+
for (const [argument, { value }] of answers)
|
|
646
|
+
args[argument] = value;
|
|
647
|
+
for (const form of (0, declare_js_1.periodFormsOf)(rules.period)) {
|
|
648
|
+
if (form.kind !== 'bounds' && form.kind !== 'object')
|
|
649
|
+
continue;
|
|
650
|
+
// A zone argument the rule assumes is not in the call yet; `readBack` answers without one.
|
|
651
|
+
const names = (0, convert_js_1.formArguments)(form).map((a) => a.argument);
|
|
652
|
+
const bounds = (0, convert_js_1.formArguments)(form).filter((a) => a.role !== 'zone');
|
|
653
|
+
const asked = names.filter((a) => answers.has(a));
|
|
654
|
+
if (asked.length === 0 || bounds.some((a) => args[a.argument] === undefined))
|
|
655
|
+
continue;
|
|
656
|
+
const range = (0, convert_js_1.readBack)(args, form, time);
|
|
657
|
+
// Every bound is present: no range means the pair names no window (a start not before its
|
|
658
|
+
// end, a misspelled bound) — never "no problem", or it reaches the tool unjudged. Only a
|
|
659
|
+
// zone still to come leaves the pair for the zone's own ask.
|
|
660
|
+
const expectation = range !== undefined
|
|
661
|
+
? pairExpectation((0, convert_js_1.periodFactProblem)(range, facts, time.now), facts)
|
|
662
|
+
: (0, convert_js_1.readBackLacksZone)(args, form, time)
|
|
663
|
+
? undefined
|
|
664
|
+
: exports.WINDOW_FORM_EXPECTATION;
|
|
665
|
+
if (expectation === undefined)
|
|
666
|
+
continue;
|
|
667
|
+
for (const a of asked) {
|
|
668
|
+
const field = answers.get(a).field;
|
|
669
|
+
if (now.has(field) && !broken.has(field))
|
|
670
|
+
broken.set(field, expectation);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return broken;
|
|
675
|
+
}
|
|
676
|
+
/** A pair's fact problem in the refusal's words — `undefined` when the pair breaks none. */
|
|
677
|
+
function pairExpectation(problem, facts) {
|
|
678
|
+
return problem === undefined ? undefined : factExpectation(problem, facts);
|
|
679
|
+
}
|
|
562
680
|
// LENS · tool-result · persistent-history (through `serve.ts` · `unansweredRefusal`)
|
|
563
681
|
// reads: the tool's declared fact the person's answers kept breaking, and its declared value
|
|
564
682
|
// law: names the rule, never the answer; a fact about the source, in the present tense of the source.
|
|
@@ -595,6 +713,7 @@ function bindAnswer(state, values, calls, toolOf, stamp, time) {
|
|
|
595
713
|
const byId = new Map(calls.map((c) => [c.id, c]));
|
|
596
714
|
const progress = [...state.progress];
|
|
597
715
|
const rows = [];
|
|
716
|
+
const pairs = pairsBroken(state, values, byId, toolOf, time);
|
|
598
717
|
const fields = [...state.fields];
|
|
599
718
|
waiting.fieldIndexes.forEach((index, k) => {
|
|
600
719
|
const field = state.fields[index];
|
|
@@ -612,6 +731,12 @@ function bindAnswer(state, values, calls, toolOf, stamp, time) {
|
|
|
612
731
|
rows.push(...windowAnsweredRows(field, bound.fills, byId, toolOf, stamp));
|
|
613
732
|
// The person's window for the mention — the only door a window of words becomes theirs.
|
|
614
733
|
rows.push((0, rows_js_1.timeAnswerRow)({ mention: field.window.mention, ...bound.answered }, stamp));
|
|
734
|
+
// Each filled call's window, as it will run: the person's answer, exactly or WIDER (the
|
|
735
|
+
// covering look-back's `sent` and `extra`) — the call's latest `call-window` row, so the
|
|
736
|
+
// clock at dispatch, `ctx.time` and the result checks read the window the call carries.
|
|
737
|
+
for (const [toolCallId, { toolName, decision }] of Object.entries(bound.filled)) {
|
|
738
|
+
rows.push((0, rowsBuild_js_1.callWindowRow)({ toolCallId, toolName }, decision, stamp));
|
|
739
|
+
}
|
|
615
740
|
return;
|
|
616
741
|
}
|
|
617
742
|
if ('outside' in bound) {
|
|
@@ -626,7 +751,10 @@ function bindAnswer(state, values, calls, toolOf, stamp, time) {
|
|
|
626
751
|
rows.push(...memberCalls(field).map((who) => (0, rows_js_2.askedRowOf)(who, stamp, 'invalid-answer')));
|
|
627
752
|
return;
|
|
628
753
|
}
|
|
629
|
-
const
|
|
754
|
+
const alone = answer === undefined ? { fits: false } : checkAnswer(field, answer, toolOf, time);
|
|
755
|
+
// An answer that fits alone can still break a fact with the other bound of its window.
|
|
756
|
+
const pair = pairs.get(index);
|
|
757
|
+
const check = alone.fits && pair !== undefined ? { fits: false, expected: pair } : alone;
|
|
630
758
|
if (answer !== undefined && check.fits) {
|
|
631
759
|
progress[index] = { rounds: was.rounds, answer };
|
|
632
760
|
rows.push(...answeredRows(field, answer, byId, toolOf, stamp));
|
|
@@ -672,7 +800,7 @@ function bindWindowAnswer(field, answer, byId, toolOf, time) {
|
|
|
672
800
|
}
|
|
673
801
|
const row = reading.row;
|
|
674
802
|
// The re-read stays a PROPOSAL: the zone was the person's, the window is still words.
|
|
675
|
-
const resolution = (0, resolve_js_1.resolveMention)((0, resolve_js_1.withZoneAnswered)(row.parses ?? [], answer), { now: time.now, zone: time.zone }, row.reader, true);
|
|
803
|
+
const resolution = (0, resolve_js_1.resolveMention)((0, resolve_js_1.withZoneAnswered)(row.parses ?? [], answer, reading.policy), { now: time.now, zone: time.zone }, row.reader, true, reading.policy);
|
|
676
804
|
const choice = (0, resolve_js_1.chooseReading)(resolution, reading.policy, row.reader.kind, undefined, true);
|
|
677
805
|
if (choice.by === 'open') {
|
|
678
806
|
const ask = (0, readingAsk_js_1.timeAskOf)({ ...row, candidates: resolution.candidates, choice }, reading.messages);
|
|
@@ -692,10 +820,10 @@ function bindWindowAnswer(field, answer, byId, toolOf, time) {
|
|
|
692
820
|
},
|
|
693
821
|
};
|
|
694
822
|
const choices = (ask.field.enum ?? []);
|
|
695
|
-
const kept = allowedChoices(choices, field.members, toolOf, time
|
|
823
|
+
const kept = allowedChoices(choices, zones, field.members, toolOf, time);
|
|
696
824
|
if (kept === undefined && field.members.length > 0) {
|
|
697
|
-
//
|
|
698
|
-
const outside =
|
|
825
|
+
// A member tool can read no reading: nothing is asked (§ 6.3) — the reason.
|
|
826
|
+
const outside = readingsOutside(choices, zones, field.members, toolOf, time);
|
|
699
827
|
if (outside !== undefined)
|
|
700
828
|
return { outside };
|
|
701
829
|
}
|
|
@@ -707,57 +835,101 @@ function bindWindowAnswer(field, answer, byId, toolOf, time) {
|
|
|
707
835
|
if (range === undefined)
|
|
708
836
|
return { expected: exports.WINDOW_FORM_EXPECTATION };
|
|
709
837
|
const at = field.choices?.indexOf(answer) ?? -1;
|
|
710
|
-
const zone =
|
|
838
|
+
const zone = at >= 0 ? window.zones?.[at] ?? time.zone : editedZone(range, window.zones, time.zone);
|
|
711
839
|
if (zone === undefined)
|
|
712
840
|
return { expected: exports.WINDOW_FORM_EXPECTATION };
|
|
713
841
|
const bound = windowFills(field, range, zone, byId, toolOf, time);
|
|
714
842
|
if (!('fills' in bound))
|
|
715
843
|
return bound;
|
|
716
|
-
return {
|
|
844
|
+
return {
|
|
845
|
+
fills: bound.fills,
|
|
846
|
+
filled: bound.filled,
|
|
847
|
+
answered: { range, zone, how: at >= 0 ? 'confirmed' : 'edited' },
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* The zone a window the person TYPED is recorded in: the first of the
|
|
852
|
+
* offered readings' zones — then the app's — whose offsets at both ends are
|
|
853
|
+
* the ones typed; else the fixed-offset zone the typed offsets spell
|
|
854
|
+
* (`Etc/GMT-1` for `+01:00`, `zone.ts` · `fixedOffsetZone`); else the app's.
|
|
855
|
+
* A London day typed with `+01:00` under an app in Los Angeles is recorded
|
|
856
|
+
* in Europe/London (the zone the mention named), never in the app's zone.
|
|
857
|
+
*/
|
|
858
|
+
function editedZone(range, offered, appZone) {
|
|
859
|
+
const from = (0, instant_js_1.instantOf)(range.from, 'lenient');
|
|
860
|
+
const to = (0, instant_js_1.instantOf)(range.to, 'lenient');
|
|
861
|
+
if (from === undefined || to === undefined)
|
|
862
|
+
return appZone;
|
|
863
|
+
const shows = (zone) => (0, zone_js_1.offsetAt)(zone, from.ms) === from.offsetMinutes && (0, zone_js_1.offsetAt)(zone, to.ms) === to.offsetMinutes;
|
|
864
|
+
const zones = [...(offered ?? []), ...(appZone !== undefined ? [appZone] : [])];
|
|
865
|
+
const match = zones.find(shows);
|
|
866
|
+
if (match !== undefined)
|
|
867
|
+
return match;
|
|
868
|
+
const fixed = from.offsetMinutes === to.offsetMinutes ? (0, zone_js_1.fixedOffsetZone)(from.offsetMinutes) : undefined;
|
|
869
|
+
return fixed ?? appZone;
|
|
717
870
|
}
|
|
718
|
-
/**
|
|
719
|
-
function
|
|
871
|
+
/** Why the first reading cannot be read, when ONE member tool can read no reading at all — the refusal's words. */
|
|
872
|
+
function readingsOutside(choices, zones, members, toolOf, time) {
|
|
720
873
|
for (const name of new Set(members.map((m) => m.toolName))) {
|
|
721
|
-
const
|
|
722
|
-
if (rules === undefined || (0, declare_js_1.isRefused)(rules))
|
|
723
|
-
continue;
|
|
724
|
-
const facts = (0, declare_js_1.periodFactsOf)(rules.period);
|
|
725
|
-
const problems = choices.map((value) => {
|
|
874
|
+
const reasons = choices.map((value, i) => {
|
|
726
875
|
const range = (0, range_js_1.parseRange)(value);
|
|
727
|
-
return range === undefined ? undefined : (
|
|
876
|
+
return range === undefined ? undefined : unreadableBy(name, range, zones[i], toolOf, time);
|
|
728
877
|
});
|
|
729
|
-
const first =
|
|
730
|
-
if (first !== undefined &&
|
|
731
|
-
return
|
|
878
|
+
const first = reasons[0];
|
|
879
|
+
if (first !== undefined && reasons.every((r) => r !== undefined)) {
|
|
880
|
+
return refusalExpectation(first.refused, first.facts);
|
|
732
881
|
}
|
|
733
882
|
}
|
|
734
883
|
return undefined;
|
|
735
884
|
}
|
|
736
|
-
/**
|
|
885
|
+
/** What an answer must be, in the refusal's words, when a tool cannot read it: its fact, else a form that holds it. */
|
|
886
|
+
function refusalExpectation(refused, facts) {
|
|
887
|
+
return refused === 'time-future' ||
|
|
888
|
+
refused === 'time-past' ||
|
|
889
|
+
refused === 'beyond-retention' ||
|
|
890
|
+
refused === 'over-max-range'
|
|
891
|
+
? factExpectation(refused, facts)
|
|
892
|
+
: exports.WINDOW_FORM_EXPECTATION;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* The window written into every member call's forms — the one owner's answer
|
|
896
|
+
* per tool (`core/time/convert.ts` · `convertForTool`: exactly, else wider,
|
|
897
|
+
* after the tool's facts) — or what it failed. Each fill also carries the
|
|
898
|
+
* decision its `call-window` row records (`filled`, the person's ANSWERED
|
|
899
|
+
* window, with the wider read's `sent` and `extra`).
|
|
900
|
+
*/
|
|
737
901
|
function windowFills(field, range, zone, byId, toolOf, time) {
|
|
738
902
|
const fills = {};
|
|
903
|
+
const filled = {};
|
|
904
|
+
const answered = {
|
|
905
|
+
source: 'answered',
|
|
906
|
+
mention: field.window.mention,
|
|
907
|
+
range,
|
|
908
|
+
zone,
|
|
909
|
+
};
|
|
739
910
|
for (const member of field.members) {
|
|
740
911
|
const rules = (0, declare_js_1.rulesOf)(toolOf(member.toolName));
|
|
741
912
|
if (rules === undefined || (0, declare_js_1.isRefused)(rules))
|
|
742
913
|
return { expected: exports.WINDOW_FORM_EXPECTATION };
|
|
743
914
|
const forms = (0, declare_js_1.periodFormsOf)(rules.period);
|
|
744
915
|
const facts = (0, declare_js_1.periodFactsOf)(rules.period);
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
916
|
+
const read = (0, convert_js_1.convertForTool)({ range }, forms, facts, {
|
|
917
|
+
now: time.now,
|
|
918
|
+
zone,
|
|
919
|
+
...(time.appZone !== undefined && { appZone: time.appZone }),
|
|
920
|
+
granularityMs: (0, convert_js_1.granularityMsOf)(facts),
|
|
921
|
+
});
|
|
922
|
+
if ('refused' in read)
|
|
923
|
+
return { expected: refusalExpectation(read.refused, facts) };
|
|
924
|
+
const conversion = read.conversion;
|
|
925
|
+
const wider = 'sent' in conversion
|
|
926
|
+
? facts.filtersToAsked === true
|
|
927
|
+
? 'tool-trims'
|
|
928
|
+
: 'reads-more'
|
|
929
|
+
: undefined;
|
|
748
930
|
for (const id of member.toolCallIds) {
|
|
749
931
|
if (fills[id] !== undefined || !byId.has(id))
|
|
750
932
|
continue;
|
|
751
|
-
const ctx = {
|
|
752
|
-
now: time.now,
|
|
753
|
-
zone,
|
|
754
|
-
...(time.appZone !== undefined && { appZone: time.appZone }),
|
|
755
|
-
granularityMs: (0, convert_js_1.granularityMsOf)(facts),
|
|
756
|
-
};
|
|
757
|
-
const exact = (0, convert_js_1.convertExact)({ range }, forms, ctx);
|
|
758
|
-
const conversion = exact ?? (0, convert_js_1.convertWidened)({ range }, forms, ctx, (0, convert_js_1.widestMsOf)(facts));
|
|
759
|
-
if (conversion === undefined)
|
|
760
|
-
return { expected: exports.WINDOW_FORM_EXPECTATION };
|
|
761
933
|
for (const [argument, value] of Object.entries(conversion.values)) {
|
|
762
934
|
if (typeof value === 'object')
|
|
763
935
|
continue;
|
|
@@ -767,15 +939,20 @@ function windowFills(field, range, zone, byId, toolOf, time) {
|
|
|
767
939
|
return { expected: expected ?? exports.WINDOW_FORM_EXPECTATION };
|
|
768
940
|
}
|
|
769
941
|
}
|
|
770
|
-
fills[id] = {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
942
|
+
fills[id] = { values: conversion.values, ...(wider !== undefined && { wider }) };
|
|
943
|
+
filled[id] = {
|
|
944
|
+
toolName: member.toolName,
|
|
945
|
+
decision: {
|
|
946
|
+
how: 'filled',
|
|
947
|
+
window: answered,
|
|
948
|
+
conversion,
|
|
949
|
+
...(wider === 'tool-trims' && { trimmedByTool: true }),
|
|
950
|
+
...(read.partlyBeyondRetention === true && { partlyBeyondRetention: true }),
|
|
951
|
+
},
|
|
775
952
|
};
|
|
776
953
|
}
|
|
777
954
|
}
|
|
778
|
-
return { fills };
|
|
955
|
+
return { fills, filled };
|
|
779
956
|
}
|
|
780
957
|
/** The `answered` rows of a bound window: one per (call, argument) the window filled, in the tool's own view. */
|
|
781
958
|
function windowAnsweredRows(field, fills, byId, toolOf, stamp) {
|