oracular 0.10.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 +22 -0
- package/README.md +6 -5
- package/dist/cli.js +7 -3
- package/dist/index.cjs +83 -25
- 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 +83 -25
- package/dist/index.js.map +1 -1
- package/dist/presets.cjs +2 -2
- 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 +2 -2
- 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
|
@@ -77,8 +77,8 @@ function lowerBound(alpha, beta, z, bound) {
|
|
|
77
77
|
function repeatFactor(n) {
|
|
78
78
|
return Math.log1p(1 / n) / Math.LN2;
|
|
79
79
|
}
|
|
80
|
-
function
|
|
81
|
-
return (successes +
|
|
80
|
+
function smoothRate(successes, total, rate) {
|
|
81
|
+
return (successes + 2 * rate) / (total + 2);
|
|
82
82
|
}
|
|
83
83
|
function lift(q, b, type = "normalized-difference") {
|
|
84
84
|
if (type === "absolute-difference") return q - b;
|
|
@@ -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,
|
|
@@ -152,12 +153,13 @@ function signalWeights(state, intent, learning, signal) {
|
|
|
152
153
|
}
|
|
153
154
|
const record2 = getOwn(state.learned, signal) ?? NONE;
|
|
154
155
|
const nWith = record2.hits + record2.misses;
|
|
155
|
-
const qWith = laplace(record2.hits, nWith);
|
|
156
156
|
const totalEpisodes = state.episodes.hits + state.episodes.misses;
|
|
157
157
|
const nWithout = Math.max(0, totalEpisodes - nWith);
|
|
158
158
|
const outcomesWithout = Math.min(nWithout, Math.max(0, state.episodes.hits - record2.hits));
|
|
159
|
-
const
|
|
160
|
-
const
|
|
159
|
+
const base = totalEpisodes === 0 ? 0 : state.episodes.hits / totalEpisodes;
|
|
160
|
+
const qWith = smoothRate(record2.hits, nWith, base);
|
|
161
|
+
const qWithout = smoothRate(outcomesWithout, nWithout, base);
|
|
162
|
+
const baseline = learning.lift === "association-rule" ? smoothRate(record2.hits + outcomesWithout, nWith + nWithout, base) : qWithout;
|
|
161
163
|
const score = learningScore(lift(qWith, baseline, learning.lift), learning.lift);
|
|
162
164
|
const learned = nWith === 0 ? 0 : learning.gain * Math.abs(initial) * score;
|
|
163
165
|
const nEff = nWith + nWithout === 0 ? 0 : nWith * nWithout / (nWith + nWithout);
|
|
@@ -248,8 +250,9 @@ function withContributions(state, intent, contributions) {
|
|
|
248
250
|
}
|
|
249
251
|
function decayIntent(state, intent, at) {
|
|
250
252
|
if (state.updatedAt === null) return { ...state, updatedAt: at };
|
|
253
|
+
if (!(at > state.updatedAt)) return state;
|
|
251
254
|
const d = decayFactor(at - state.updatedAt, intent.halfLife);
|
|
252
|
-
if (d === 1) return
|
|
255
|
+
if (d === 1) return { ...state, updatedAt: at };
|
|
253
256
|
const contributions = mapRecord(state.contributions, (c) => ({
|
|
254
257
|
alpha: c.alpha * d,
|
|
255
258
|
beta: c.beta * d
|
|
@@ -567,7 +570,7 @@ function markResolved(episode, intent) {
|
|
|
567
570
|
}
|
|
568
571
|
|
|
569
572
|
// src/version.ts
|
|
570
|
-
var STATE_VERSION =
|
|
573
|
+
var STATE_VERSION = 4;
|
|
571
574
|
var RECORD_VERSION = 1;
|
|
572
575
|
|
|
573
576
|
// src/state.ts
|
|
@@ -577,10 +580,16 @@ function createState(config) {
|
|
|
577
580
|
for (const [name, intent] of Object.entries(normalized.intents)) {
|
|
578
581
|
intents[name] = initIntent(intent);
|
|
579
582
|
}
|
|
580
|
-
return {
|
|
583
|
+
return {
|
|
584
|
+
version: STATE_VERSION,
|
|
585
|
+
config: normalized,
|
|
586
|
+
intents,
|
|
587
|
+
episode: emptyEpisode(),
|
|
588
|
+
clock: { at: null, behindAt: null }
|
|
589
|
+
};
|
|
581
590
|
}
|
|
582
591
|
function requireState(state) {
|
|
583
|
-
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)) {
|
|
584
593
|
throw new TypeError("oracular: state must be an EngineState from createState or deserialize");
|
|
585
594
|
}
|
|
586
595
|
return state;
|
|
@@ -717,8 +726,10 @@ function explain(state, intent, at, opts) {
|
|
|
717
726
|
|
|
718
727
|
// src/observe.ts
|
|
719
728
|
function observe(state, event) {
|
|
720
|
-
|
|
721
|
-
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;
|
|
722
733
|
const closeAt = timedOutAt(s.episode, s.config.episodeTimeout, at);
|
|
723
734
|
if (closeAt !== null) s = closeEpisode(s, closeAt);
|
|
724
735
|
const learning = resolveLearning(s.config);
|
|
@@ -731,7 +742,6 @@ function observe(state, event) {
|
|
|
731
742
|
let is = getIntentState(s, intentName, intent);
|
|
732
743
|
is = decayIntent(is, intent, at);
|
|
733
744
|
is = settlePending(is, intent, learning, at);
|
|
734
|
-
is = pinAnchors(is, at);
|
|
735
745
|
if (isSignal) {
|
|
736
746
|
const { effective } = signalWeights(is, intent, learning, name);
|
|
737
747
|
const n = occurrences(episode, intentName, name) + 1;
|
|
@@ -752,15 +762,36 @@ function observe(state, event) {
|
|
|
752
762
|
}
|
|
753
763
|
return { ...s, intents, episode };
|
|
754
764
|
}
|
|
755
|
-
function
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
if (
|
|
759
|
-
return {
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
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;
|
|
764
795
|
}
|
|
765
796
|
function endEpisode(state, at) {
|
|
766
797
|
const s = requireState(state);
|
|
@@ -886,7 +917,8 @@ function createEndRecord(at, opts) {
|
|
|
886
917
|
// src/migrate.ts
|
|
887
918
|
var MIGRATIONS = {
|
|
888
919
|
1: fromV1,
|
|
889
|
-
2: fromV2
|
|
920
|
+
2: fromV2,
|
|
921
|
+
3: fromV3
|
|
890
922
|
};
|
|
891
923
|
function canMigrate(version) {
|
|
892
924
|
return hasOwn(MIGRATIONS, String(version));
|
|
@@ -927,6 +959,9 @@ function fromV2(stored) {
|
|
|
927
959
|
}
|
|
928
960
|
return { ...stored, version: 3, intents };
|
|
929
961
|
}
|
|
962
|
+
function fromV3(stored) {
|
|
963
|
+
return { ...stored, version: 4, clock: { at: null, behindAt: null } };
|
|
964
|
+
}
|
|
930
965
|
|
|
931
966
|
// src/serialize.ts
|
|
932
967
|
function serialize(state) {
|
|
@@ -1072,6 +1107,13 @@ function episodeState(value, path) {
|
|
|
1072
1107
|
intents: mapOwn(r.intents, `${path}.intents`, episodeIntent)
|
|
1073
1108
|
};
|
|
1074
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
|
+
}
|
|
1075
1117
|
function parseState(value) {
|
|
1076
1118
|
const r = record(value, "state");
|
|
1077
1119
|
if (r.version !== STATE_VERSION) {
|
|
@@ -1083,7 +1125,8 @@ function parseState(value) {
|
|
|
1083
1125
|
// when it is only stored, and the config passed in replaces it anyway.
|
|
1084
1126
|
config: normalizeConfig(r.config, false),
|
|
1085
1127
|
intents: mapOwn(r.intents, "state.intents", intentState),
|
|
1086
|
-
episode: episodeState(r.episode, "state.episode")
|
|
1128
|
+
episode: episodeState(r.episode, "state.episode"),
|
|
1129
|
+
clock: clockState(r.clock, "state.clock")
|
|
1087
1130
|
};
|
|
1088
1131
|
}
|
|
1089
1132
|
function requireComputable(state) {
|
|
@@ -1157,9 +1200,24 @@ function reconcile(raw, config) {
|
|
|
1157
1200
|
startedAt: raw.episode.startedAt,
|
|
1158
1201
|
lastEventAt: raw.episode.startedAt === null ? null : raw.episode.lastEventAt,
|
|
1159
1202
|
intents: episodeIntents
|
|
1160
|
-
}
|
|
1203
|
+
},
|
|
1204
|
+
clock: trustedClock(raw.clock, intents, raw.episode)
|
|
1161
1205
|
};
|
|
1162
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
|
+
}
|
|
1163
1221
|
|
|
1164
1222
|
// src/units.ts
|
|
1165
1223
|
var ms = {
|