oracular 0.11.0 → 0.12.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/CHANGELOG.md +16 -0
- package/README.md +3 -3
- package/dist/index.cjs +77 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -10
- package/dist/index.d.ts +34 -10
- package/dist/index.js +77 -20
- package/dist/index.js.map +1 -1
- package/dist/presets.cjs.map +1 -1
- package/dist/presets.d.cts +25 -3
- package/dist/presets.d.ts +25 -3
- package/dist/presets.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version of the serialized state shape, stored in every state as `state.version`. Bumped only
|
|
3
|
-
* when `EngineState` changes
|
|
3
|
+
* when `EngineState` changes shape, and every bump ships its migration: {@link deserialize}
|
|
4
4
|
* upgrades states saved by earlier versions of this library on its own (version 1, written by
|
|
5
|
-
* oracular 0.1.0 to 0.2.x,
|
|
6
|
-
* state one version newer than its own as its own (a
|
|
7
|
-
* by one release keeps the user's history), and
|
|
8
|
-
* it does not know.
|
|
5
|
+
* oracular 0.1.0 to 0.2.x, version 2, written by 0.3.0 to 0.5.x, and version 3, written by 0.6.0
|
|
6
|
+
* to 0.11.x, become version 4), reads a state one version newer than its own as its own (a
|
|
7
|
+
* version bump only adds fields, so a rollback by one release keeps the user's history), and
|
|
8
|
+
* calls your `migrate` function only for a version it does not know.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* });
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
declare const STATE_VERSION:
|
|
24
|
+
declare const STATE_VERSION: 4;
|
|
25
25
|
/**
|
|
26
26
|
* Version of the recorder format: the `v` field of every record that {@link createRecord} and
|
|
27
27
|
* {@link createEndRecord} produce. A new optional field does not change it; readers ignore fields
|
|
@@ -230,6 +230,20 @@ interface EpisodeState {
|
|
|
230
230
|
lastEventAt: number | null;
|
|
231
231
|
intents: Record<string, EpisodeIntentRecord>;
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* The engine's clock: the latest event time it trusts, and one event stamped behind it that is
|
|
235
|
+
* not yet confirmed as a clock correction. See `observe` for the rule.
|
|
236
|
+
*/
|
|
237
|
+
interface ClockState {
|
|
238
|
+
/** The latest event time trusted so far; null before the first event. */
|
|
239
|
+
at: number | null;
|
|
240
|
+
/**
|
|
241
|
+
* The stamp of the last event that arrived behind `at`, held until the next event decides: a
|
|
242
|
+
* second event behind `at` confirms the clock was corrected and every anchor ahead of this
|
|
243
|
+
* time moves back to it; an event at or after `at` makes it a glitch that changed nothing.
|
|
244
|
+
*/
|
|
245
|
+
behindAt: number | null;
|
|
246
|
+
}
|
|
233
247
|
/**
|
|
234
248
|
* The whole engine state: plain JSON with no functions, class instances or hidden fields.
|
|
235
249
|
* Persist it with `serialize`, restore it with `deserialize`.
|
|
@@ -247,6 +261,7 @@ interface EngineState {
|
|
|
247
261
|
config: EngineConfig;
|
|
248
262
|
intents: Record<string, IntentState>;
|
|
249
263
|
episode: EpisodeState;
|
|
264
|
+
clock: ClockState;
|
|
250
265
|
}
|
|
251
266
|
/**
|
|
252
267
|
* What the app feeds to `observe`. Any event name is accepted; only names that appear in some
|
|
@@ -359,7 +374,7 @@ interface Evaluation {
|
|
|
359
374
|
/**
|
|
360
375
|
* Turns a stored state of a version this library cannot migrate itself into a current
|
|
361
376
|
* `EngineState`. Receives the parsed JSON as-is and the version it declares. States saved by
|
|
362
|
-
* oracular 0.1.0 to 0.
|
|
377
|
+
* oracular 0.1.0 to 0.11.x (versions 1 to 3) never reach it, nor does a state one version newer
|
|
363
378
|
* than `STATE_VERSION`: `deserialize` handles both on its own.
|
|
364
379
|
*
|
|
365
380
|
* @example
|
|
@@ -384,7 +399,7 @@ interface DeserializeOptions {
|
|
|
384
399
|
config?: EngineConfig;
|
|
385
400
|
/**
|
|
386
401
|
* Called when the stored version is neither `STATE_VERSION`, nor one this library migrates
|
|
387
|
-
* itself (states from oracular 0.1.0 to 0.
|
|
402
|
+
* itself (states from oracular 0.1.0 to 0.11.x are upgraded without it), nor the next version,
|
|
388
403
|
* which it reads as its own.
|
|
389
404
|
*/
|
|
390
405
|
migrate?: Migrate;
|
|
@@ -637,6 +652,15 @@ declare function explain(state: EngineState, intent: string, at: number, opts?:
|
|
|
637
652
|
* or extends the current episode; an idle gap longer than `config.episodeTimeout` closes the
|
|
638
653
|
* previous episode first.
|
|
639
654
|
*
|
|
655
|
+
* The clock. An event at or after the latest event time moves the engine's clock there, and
|
|
656
|
+
* anything stamped ahead of it (a reveal stamped by a clock that was ahead) is pinned to it. An
|
|
657
|
+
* event stamped *behind* the clock counts now, at the clock's time, and its stamp is held: if the
|
|
658
|
+
* next event is also behind the clock, the clock was corrected, and every anchor ahead of the
|
|
659
|
+
* held stamp moves back to it so decay and cooldowns resume from the corrected time; if the next
|
|
660
|
+
* event is at or after the clock, the held stamp was one glitch and nothing moved. So one event
|
|
661
|
+
* stamped years ahead cannot hold an intent past the two events that follow it, and one stamped
|
|
662
|
+
* years behind cannot wipe the evidence or close the session.
|
|
663
|
+
*
|
|
640
664
|
* @param state - The current state. Not mutated.
|
|
641
665
|
* @param event - `name` is matched against every intent's signals and outcome; `at` is the
|
|
642
666
|
* event's timestamp, in the same unit as your durations; `props` are accepted and ignored,
|
|
@@ -831,7 +855,7 @@ declare function serialize(state: EngineState): string;
|
|
|
831
855
|
* before those checks existed still loads),
|
|
832
856
|
* intents and signals no longer declared are dropped (α and β are recomputed from the kept
|
|
833
857
|
* contributions), and newly declared intents start at their prior. States saved by oracular
|
|
834
|
-
* 0.1.0 to 0.
|
|
858
|
+
* 0.1.0 to 0.11.x are upgraded to the current shape on their own, and a state one version newer
|
|
835
859
|
* than {@link STATE_VERSION} (written by the next release, read after a rollback) is read as the
|
|
836
860
|
* current one, ignoring the fields it adds; `migrate` is only for a version this library does
|
|
837
861
|
* not know.
|
|
@@ -952,4 +976,4 @@ declare const ms: Units;
|
|
|
952
976
|
*/
|
|
953
977
|
declare function validateConfig(config: unknown): EngineConfig;
|
|
954
978
|
|
|
955
|
-
export { type DeserializeOptions, type EndRecord, type EngineConfig, type EngineState, type EpisodeIntentRecord, type EpisodeState, type EvaluateOptions, type Evaluation, type EventRecord, type ExplainOptions, type IntentConfig, type IntentEvent, type IntentSnapshot, type IntentState, type LearningConfig, type LiftType, type Migrate, type PendingEpisode, type Prediction, type PresetName, type PresetSource, RECORD_VERSION, type RecordMode, type RecordOptions, type RecordedDecision, type RecorderRecord, type RevealOptions, STATE_VERSION, type SignalContribution, type SignalExplanation, type SignalRecord, type Snapshot, type Units, createEndRecord, createRecord, createState, deserialize, endEpisode, episodeClosed, evaluate, evidenceToReveal, explain, inspect, markRevealed, ms, observe, predict, serialize, shouldReveal, validateConfig };
|
|
979
|
+
export { type ClockState, type DeserializeOptions, type EndRecord, type EngineConfig, type EngineState, type EpisodeIntentRecord, type EpisodeState, type EvaluateOptions, type Evaluation, type EventRecord, type ExplainOptions, type IntentConfig, type IntentEvent, type IntentSnapshot, type IntentState, type LearningConfig, type LiftType, type Migrate, type PendingEpisode, type Prediction, type PresetName, type PresetSource, RECORD_VERSION, type RecordMode, type RecordOptions, type RecordedDecision, type RecorderRecord, type RevealOptions, STATE_VERSION, type SignalContribution, type SignalExplanation, type SignalRecord, type Snapshot, type Units, createEndRecord, createRecord, createState, deserialize, endEpisode, episodeClosed, evaluate, evidenceToReveal, explain, inspect, markRevealed, ms, observe, predict, serialize, shouldReveal, validateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Version of the serialized state shape, stored in every state as `state.version`. Bumped only
|
|
3
|
-
* when `EngineState` changes
|
|
3
|
+
* when `EngineState` changes shape, and every bump ships its migration: {@link deserialize}
|
|
4
4
|
* upgrades states saved by earlier versions of this library on its own (version 1, written by
|
|
5
|
-
* oracular 0.1.0 to 0.2.x,
|
|
6
|
-
* state one version newer than its own as its own (a
|
|
7
|
-
* by one release keeps the user's history), and
|
|
8
|
-
* it does not know.
|
|
5
|
+
* oracular 0.1.0 to 0.2.x, version 2, written by 0.3.0 to 0.5.x, and version 3, written by 0.6.0
|
|
6
|
+
* to 0.11.x, become version 4), reads a state one version newer than its own as its own (a
|
|
7
|
+
* version bump only adds fields, so a rollback by one release keeps the user's history), and
|
|
8
|
+
* calls your `migrate` function only for a version it does not know.
|
|
9
9
|
*
|
|
10
10
|
* @example
|
|
11
11
|
* ```ts
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* });
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
declare const STATE_VERSION:
|
|
24
|
+
declare const STATE_VERSION: 4;
|
|
25
25
|
/**
|
|
26
26
|
* Version of the recorder format: the `v` field of every record that {@link createRecord} and
|
|
27
27
|
* {@link createEndRecord} produce. A new optional field does not change it; readers ignore fields
|
|
@@ -230,6 +230,20 @@ interface EpisodeState {
|
|
|
230
230
|
lastEventAt: number | null;
|
|
231
231
|
intents: Record<string, EpisodeIntentRecord>;
|
|
232
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* The engine's clock: the latest event time it trusts, and one event stamped behind it that is
|
|
235
|
+
* not yet confirmed as a clock correction. See `observe` for the rule.
|
|
236
|
+
*/
|
|
237
|
+
interface ClockState {
|
|
238
|
+
/** The latest event time trusted so far; null before the first event. */
|
|
239
|
+
at: number | null;
|
|
240
|
+
/**
|
|
241
|
+
* The stamp of the last event that arrived behind `at`, held until the next event decides: a
|
|
242
|
+
* second event behind `at` confirms the clock was corrected and every anchor ahead of this
|
|
243
|
+
* time moves back to it; an event at or after `at` makes it a glitch that changed nothing.
|
|
244
|
+
*/
|
|
245
|
+
behindAt: number | null;
|
|
246
|
+
}
|
|
233
247
|
/**
|
|
234
248
|
* The whole engine state: plain JSON with no functions, class instances or hidden fields.
|
|
235
249
|
* Persist it with `serialize`, restore it with `deserialize`.
|
|
@@ -247,6 +261,7 @@ interface EngineState {
|
|
|
247
261
|
config: EngineConfig;
|
|
248
262
|
intents: Record<string, IntentState>;
|
|
249
263
|
episode: EpisodeState;
|
|
264
|
+
clock: ClockState;
|
|
250
265
|
}
|
|
251
266
|
/**
|
|
252
267
|
* What the app feeds to `observe`. Any event name is accepted; only names that appear in some
|
|
@@ -359,7 +374,7 @@ interface Evaluation {
|
|
|
359
374
|
/**
|
|
360
375
|
* Turns a stored state of a version this library cannot migrate itself into a current
|
|
361
376
|
* `EngineState`. Receives the parsed JSON as-is and the version it declares. States saved by
|
|
362
|
-
* oracular 0.1.0 to 0.
|
|
377
|
+
* oracular 0.1.0 to 0.11.x (versions 1 to 3) never reach it, nor does a state one version newer
|
|
363
378
|
* than `STATE_VERSION`: `deserialize` handles both on its own.
|
|
364
379
|
*
|
|
365
380
|
* @example
|
|
@@ -384,7 +399,7 @@ interface DeserializeOptions {
|
|
|
384
399
|
config?: EngineConfig;
|
|
385
400
|
/**
|
|
386
401
|
* Called when the stored version is neither `STATE_VERSION`, nor one this library migrates
|
|
387
|
-
* itself (states from oracular 0.1.0 to 0.
|
|
402
|
+
* itself (states from oracular 0.1.0 to 0.11.x are upgraded without it), nor the next version,
|
|
388
403
|
* which it reads as its own.
|
|
389
404
|
*/
|
|
390
405
|
migrate?: Migrate;
|
|
@@ -637,6 +652,15 @@ declare function explain(state: EngineState, intent: string, at: number, opts?:
|
|
|
637
652
|
* or extends the current episode; an idle gap longer than `config.episodeTimeout` closes the
|
|
638
653
|
* previous episode first.
|
|
639
654
|
*
|
|
655
|
+
* The clock. An event at or after the latest event time moves the engine's clock there, and
|
|
656
|
+
* anything stamped ahead of it (a reveal stamped by a clock that was ahead) is pinned to it. An
|
|
657
|
+
* event stamped *behind* the clock counts now, at the clock's time, and its stamp is held: if the
|
|
658
|
+
* next event is also behind the clock, the clock was corrected, and every anchor ahead of the
|
|
659
|
+
* held stamp moves back to it so decay and cooldowns resume from the corrected time; if the next
|
|
660
|
+
* event is at or after the clock, the held stamp was one glitch and nothing moved. So one event
|
|
661
|
+
* stamped years ahead cannot hold an intent past the two events that follow it, and one stamped
|
|
662
|
+
* years behind cannot wipe the evidence or close the session.
|
|
663
|
+
*
|
|
640
664
|
* @param state - The current state. Not mutated.
|
|
641
665
|
* @param event - `name` is matched against every intent's signals and outcome; `at` is the
|
|
642
666
|
* event's timestamp, in the same unit as your durations; `props` are accepted and ignored,
|
|
@@ -831,7 +855,7 @@ declare function serialize(state: EngineState): string;
|
|
|
831
855
|
* before those checks existed still loads),
|
|
832
856
|
* intents and signals no longer declared are dropped (α and β are recomputed from the kept
|
|
833
857
|
* contributions), and newly declared intents start at their prior. States saved by oracular
|
|
834
|
-
* 0.1.0 to 0.
|
|
858
|
+
* 0.1.0 to 0.11.x are upgraded to the current shape on their own, and a state one version newer
|
|
835
859
|
* than {@link STATE_VERSION} (written by the next release, read after a rollback) is read as the
|
|
836
860
|
* current one, ignoring the fields it adds; `migrate` is only for a version this library does
|
|
837
861
|
* not know.
|
|
@@ -952,4 +976,4 @@ declare const ms: Units;
|
|
|
952
976
|
*/
|
|
953
977
|
declare function validateConfig(config: unknown): EngineConfig;
|
|
954
978
|
|
|
955
|
-
export { type DeserializeOptions, type EndRecord, type EngineConfig, type EngineState, type EpisodeIntentRecord, type EpisodeState, type EvaluateOptions, type Evaluation, type EventRecord, type ExplainOptions, type IntentConfig, type IntentEvent, type IntentSnapshot, type IntentState, type LearningConfig, type LiftType, type Migrate, type PendingEpisode, type Prediction, type PresetName, type PresetSource, RECORD_VERSION, type RecordMode, type RecordOptions, type RecordedDecision, type RecorderRecord, type RevealOptions, STATE_VERSION, type SignalContribution, type SignalExplanation, type SignalRecord, type Snapshot, type Units, createEndRecord, createRecord, createState, deserialize, endEpisode, episodeClosed, evaluate, evidenceToReveal, explain, inspect, markRevealed, ms, observe, predict, serialize, shouldReveal, validateConfig };
|
|
979
|
+
export { type ClockState, type DeserializeOptions, type EndRecord, type EngineConfig, type EngineState, type EpisodeIntentRecord, type EpisodeState, type EvaluateOptions, type Evaluation, type EventRecord, type ExplainOptions, type IntentConfig, type IntentEvent, type IntentSnapshot, type IntentState, type LearningConfig, type LiftType, type Migrate, type PendingEpisode, type Prediction, type PresetName, type PresetSource, RECORD_VERSION, type RecordMode, type RecordOptions, type RecordedDecision, type RecorderRecord, type RevealOptions, STATE_VERSION, type SignalContribution, type SignalExplanation, type SignalRecord, type Snapshot, type Units, createEndRecord, createRecord, createState, deserialize, endEpisode, episodeClosed, evaluate, evidenceToReveal, explain, inspect, markRevealed, ms, observe, predict, serialize, shouldReveal, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -136,8 +136,9 @@ function mapRecord(record2, fn) {
|
|
|
136
136
|
var NONE = { hits: 0, misses: 0 };
|
|
137
137
|
function decayLearned(state, learning, at) {
|
|
138
138
|
if (state.learnedAt === null) return { ...state, learnedAt: at };
|
|
139
|
+
if (!(at > state.learnedAt)) return state;
|
|
139
140
|
const d = learning.halfLife === null ? 1 : decayFactor(at - state.learnedAt, learning.halfLife);
|
|
140
|
-
if (d === 1) return
|
|
141
|
+
if (d === 1) return { ...state, learnedAt: at };
|
|
141
142
|
return {
|
|
142
143
|
...state,
|
|
143
144
|
learnedAt: at,
|
|
@@ -249,8 +250,9 @@ function withContributions(state, intent, contributions) {
|
|
|
249
250
|
}
|
|
250
251
|
function decayIntent(state, intent, at) {
|
|
251
252
|
if (state.updatedAt === null) return { ...state, updatedAt: at };
|
|
253
|
+
if (!(at > state.updatedAt)) return state;
|
|
252
254
|
const d = decayFactor(at - state.updatedAt, intent.halfLife);
|
|
253
|
-
if (d === 1) return
|
|
255
|
+
if (d === 1) return { ...state, updatedAt: at };
|
|
254
256
|
const contributions = mapRecord(state.contributions, (c) => ({
|
|
255
257
|
alpha: c.alpha * d,
|
|
256
258
|
beta: c.beta * d
|
|
@@ -568,7 +570,7 @@ function markResolved(episode, intent) {
|
|
|
568
570
|
}
|
|
569
571
|
|
|
570
572
|
// src/version.ts
|
|
571
|
-
var STATE_VERSION =
|
|
573
|
+
var STATE_VERSION = 4;
|
|
572
574
|
var RECORD_VERSION = 1;
|
|
573
575
|
|
|
574
576
|
// src/state.ts
|
|
@@ -578,10 +580,16 @@ function createState(config) {
|
|
|
578
580
|
for (const [name, intent] of Object.entries(normalized.intents)) {
|
|
579
581
|
intents[name] = initIntent(intent);
|
|
580
582
|
}
|
|
581
|
-
return {
|
|
583
|
+
return {
|
|
584
|
+
version: STATE_VERSION,
|
|
585
|
+
config: normalized,
|
|
586
|
+
intents,
|
|
587
|
+
episode: emptyEpisode(),
|
|
588
|
+
clock: { at: null, behindAt: null }
|
|
589
|
+
};
|
|
582
590
|
}
|
|
583
591
|
function requireState(state) {
|
|
584
|
-
if (!isRecord(state) || state.version !== STATE_VERSION || !isRecord(state.config) || !isRecord(state.intents) || !isRecord(state.episode)) {
|
|
592
|
+
if (!isRecord(state) || state.version !== STATE_VERSION || !isRecord(state.config) || !isRecord(state.intents) || !isRecord(state.episode) || !isRecord(state.clock)) {
|
|
585
593
|
throw new TypeError("oracular: state must be an EngineState from createState or deserialize");
|
|
586
594
|
}
|
|
587
595
|
return state;
|
|
@@ -718,8 +726,10 @@ function explain(state, intent, at, opts) {
|
|
|
718
726
|
|
|
719
727
|
// src/observe.ts
|
|
720
728
|
function observe(state, event) {
|
|
721
|
-
|
|
722
|
-
const
|
|
729
|
+
const { name, at: stamp } = validateEvent(event);
|
|
730
|
+
const synced = syncClock(requireState(state), stamp);
|
|
731
|
+
let s = synced.state;
|
|
732
|
+
const at = synced.at;
|
|
723
733
|
const closeAt = timedOutAt(s.episode, s.config.episodeTimeout, at);
|
|
724
734
|
if (closeAt !== null) s = closeEpisode(s, closeAt);
|
|
725
735
|
const learning = resolveLearning(s.config);
|
|
@@ -732,7 +742,6 @@ function observe(state, event) {
|
|
|
732
742
|
let is = getIntentState(s, intentName, intent);
|
|
733
743
|
is = decayIntent(is, intent, at);
|
|
734
744
|
is = settlePending(is, intent, learning, at);
|
|
735
|
-
is = pinAnchors(is, at);
|
|
736
745
|
if (isSignal) {
|
|
737
746
|
const { effective } = signalWeights(is, intent, learning, name);
|
|
738
747
|
const n = occurrences(episode, intentName, name) + 1;
|
|
@@ -753,15 +762,36 @@ function observe(state, event) {
|
|
|
753
762
|
}
|
|
754
763
|
return { ...s, intents, episode };
|
|
755
764
|
}
|
|
756
|
-
function
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
if (
|
|
760
|
-
return {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
+
function syncClock(state, stamp) {
|
|
766
|
+
const { at, behindAt } = state.clock;
|
|
767
|
+
if (at === null || stamp >= at) return { state: pinTo(state, stamp), at: stamp };
|
|
768
|
+
if (behindAt !== null) return syncClock(pinTo(state, behindAt), stamp);
|
|
769
|
+
return { state: { ...state, clock: { at, behindAt: stamp } }, at };
|
|
770
|
+
}
|
|
771
|
+
var INTENT_ANCHORS = ["updatedAt", "learnedAt", "lastRevealedAt", "lastOutcomeAt"];
|
|
772
|
+
var EPISODE_ANCHORS = ["startedAt", "lastEventAt"];
|
|
773
|
+
function pinTo(state, t) {
|
|
774
|
+
let intents = state.intents;
|
|
775
|
+
for (const [name, is] of Object.entries(state.intents)) {
|
|
776
|
+
let pinned = pinKeys(is, INTENT_ANCHORS, t);
|
|
777
|
+
if (is.pending.some((p) => p.endedAt > t)) {
|
|
778
|
+
pinned = {
|
|
779
|
+
...pinned,
|
|
780
|
+
pending: is.pending.map((p) => p.endedAt > t ? { ...p, endedAt: t } : p)
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
if (pinned !== is) intents = { ...intents, [name]: pinned };
|
|
784
|
+
}
|
|
785
|
+
const episode = pinKeys(state.episode, EPISODE_ANCHORS, t);
|
|
786
|
+
return { ...state, intents, episode, clock: { at: t, behindAt: null } };
|
|
787
|
+
}
|
|
788
|
+
function pinKeys(obj, keys, t) {
|
|
789
|
+
let out = obj;
|
|
790
|
+
for (const key of keys) {
|
|
791
|
+
const value = obj[key];
|
|
792
|
+
if (typeof value === "number" && value > t) out = { ...out, [key]: t };
|
|
793
|
+
}
|
|
794
|
+
return out;
|
|
765
795
|
}
|
|
766
796
|
function endEpisode(state, at) {
|
|
767
797
|
const s = requireState(state);
|
|
@@ -887,7 +917,8 @@ function createEndRecord(at, opts) {
|
|
|
887
917
|
// src/migrate.ts
|
|
888
918
|
var MIGRATIONS = {
|
|
889
919
|
1: fromV1,
|
|
890
|
-
2: fromV2
|
|
920
|
+
2: fromV2,
|
|
921
|
+
3: fromV3
|
|
891
922
|
};
|
|
892
923
|
function canMigrate(version) {
|
|
893
924
|
return hasOwn(MIGRATIONS, String(version));
|
|
@@ -928,6 +959,9 @@ function fromV2(stored) {
|
|
|
928
959
|
}
|
|
929
960
|
return { ...stored, version: 3, intents };
|
|
930
961
|
}
|
|
962
|
+
function fromV3(stored) {
|
|
963
|
+
return { ...stored, version: 4, clock: { at: null, behindAt: null } };
|
|
964
|
+
}
|
|
931
965
|
|
|
932
966
|
// src/serialize.ts
|
|
933
967
|
function serialize(state) {
|
|
@@ -1073,6 +1107,13 @@ function episodeState(value, path) {
|
|
|
1073
1107
|
intents: mapOwn(r.intents, `${path}.intents`, episodeIntent)
|
|
1074
1108
|
};
|
|
1075
1109
|
}
|
|
1110
|
+
function clockState(value, path) {
|
|
1111
|
+
const r = record(value, path);
|
|
1112
|
+
return {
|
|
1113
|
+
at: nullOrFinite(r.at, `${path}.at`),
|
|
1114
|
+
behindAt: nullOrFinite(r.behindAt, `${path}.behindAt`)
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1076
1117
|
function parseState(value) {
|
|
1077
1118
|
const r = record(value, "state");
|
|
1078
1119
|
if (r.version !== STATE_VERSION) {
|
|
@@ -1084,7 +1125,8 @@ function parseState(value) {
|
|
|
1084
1125
|
// when it is only stored, and the config passed in replaces it anyway.
|
|
1085
1126
|
config: normalizeConfig(r.config, false),
|
|
1086
1127
|
intents: mapOwn(r.intents, "state.intents", intentState),
|
|
1087
|
-
episode: episodeState(r.episode, "state.episode")
|
|
1128
|
+
episode: episodeState(r.episode, "state.episode"),
|
|
1129
|
+
clock: clockState(r.clock, "state.clock")
|
|
1088
1130
|
};
|
|
1089
1131
|
}
|
|
1090
1132
|
function requireComputable(state) {
|
|
@@ -1158,9 +1200,24 @@ function reconcile(raw, config) {
|
|
|
1158
1200
|
startedAt: raw.episode.startedAt,
|
|
1159
1201
|
lastEventAt: raw.episode.startedAt === null ? null : raw.episode.lastEventAt,
|
|
1160
1202
|
intents: episodeIntents
|
|
1161
|
-
}
|
|
1203
|
+
},
|
|
1204
|
+
clock: trustedClock(raw.clock, intents, raw.episode)
|
|
1162
1205
|
};
|
|
1163
1206
|
}
|
|
1207
|
+
function trustedClock(clock, intents, episode) {
|
|
1208
|
+
let at = clock.at;
|
|
1209
|
+
const see = (value) => {
|
|
1210
|
+
if (value !== null && (at === null || value > at)) at = value;
|
|
1211
|
+
};
|
|
1212
|
+
see(episode.startedAt);
|
|
1213
|
+
see(episode.lastEventAt);
|
|
1214
|
+
for (const is of Object.values(intents)) {
|
|
1215
|
+
see(is.updatedAt);
|
|
1216
|
+
see(is.lastOutcomeAt);
|
|
1217
|
+
}
|
|
1218
|
+
const behindAt = clock.behindAt !== null && at !== null && clock.behindAt < at ? clock.behindAt : null;
|
|
1219
|
+
return at === clock.at && behindAt === clock.behindAt ? clock : { at, behindAt };
|
|
1220
|
+
}
|
|
1164
1221
|
|
|
1165
1222
|
// src/units.ts
|
|
1166
1223
|
var ms = {
|