oracular 0.13.0 → 0.14.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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#71](https://github.com/Yuribenjamin/oracular/pull/71) [`d9e9769`](https://github.com/Yuribenjamin/oracular/commit/d9e9769e922c3bc4251f4585a6cc1c7697468958) Thanks [@Yuribenjamin](https://github.com/Yuribenjamin)! - **A cooldown of its own after the outcome.** The outcome event paused reveals for the reveal `cooldown`: after a "stuck" intent's outcome, help was suppressed for the hour the cooldown named, and the next attempt that went wrong, exactly when the help should be back, got nothing; the only way out, `cooldown: 0`, disabled the reveal cooldown too. `reveal.outcomeCooldown` is the pause after the outcome, resolved per call → intent → engine like the other reveal options, 0 allowed; it defaults to the resolved `cooldown`, so nothing moves unless it is set. The JSON Schema lists it. Found by the second production integration.
8
+
9
+ **Rollback.** A stored config that sets `outcomeCooldown` loads in 0.13 and later, which do not read the stored snapshot when the app passes its config; 0.12 and earlier throw a `TypeError` on the unknown key, so remove it from the saved config before rolling a deploy back that far. The state version is unchanged.
10
+
3
11
  ## 0.13.0
4
12
 
5
13
  ### Minor Changes
package/README.md CHANGED
@@ -224,7 +224,7 @@ Learned counts never decay unless you set `learning.halfLife`.
224
224
 
225
225
  When the outcome event arrives, the intent's evidence returns to the prior (α, β and contributions), while everything learned is kept. The user already upgraded; stop predicting that they will. Set `resetOnOutcome: false` for intents where the outcome can repeat inside one episode.
226
226
 
227
- The outcome also starts the intent's `cooldown`: `shouldReveal` stays false until it ends, however strong the fresh evidence, so a user who just bought is not immediately pitched again. The time of the last outcome is kept on the state as `lastOutcomeAt`.
227
+ The outcome also starts a cooldown, `outcomeCooldown`, by default the same `cooldown` a reveal starts: `shouldReveal` stays false until it ends, however strong the fresh evidence, so a user who just bought is not immediately pitched again. Set it on its own, 0 included, for an intent whose outcome recurs within minutes, or a "stuck" intent whose help should be back at the next attempt: the outcome then pauses nothing while a reveal still starts its cooldown. The time of the last outcome is kept on the state as `lastOutcomeAt`.
228
228
 
229
229
  ### Every answer comes with its reasons
230
230
 
@@ -302,9 +302,10 @@ interface IntentConfig {
302
302
  }
303
303
 
304
304
  interface RevealOptions {
305
- threshold?: number; // default 0.5
306
- z?: number; // default 1.28
307
- cooldown?: number; // default: the intent's halfLife
305
+ threshold?: number; // default 0.5
306
+ z?: number; // default 1.28
307
+ cooldown?: number; // after markRevealed; default: the intent's halfLife
308
+ outcomeCooldown?: number; // after the outcome event; default: cooldown. 0 lets the outcome pause nothing
308
309
  bound?: 'normal' | 'exact'; // default 'normal': p − z·sd; 'exact': the Beta quantile at Φ(−z)
309
310
  }
310
311
  ```
package/dist/index.cjs CHANGED
@@ -289,7 +289,7 @@ var INTENT_KEYS = [
289
289
  "attribution",
290
290
  "reveal"
291
291
  ];
292
- var REVEAL_KEYS = ["threshold", "z", "cooldown", "bound"];
292
+ var REVEAL_KEYS = ["threshold", "z", "cooldown", "outcomeCooldown", "bound"];
293
293
  var LEARNING_KEYS = ["halfLife", "gain", "priorEpisodes", "lift"];
294
294
  var LIFT_TYPES = [
295
295
  "absolute-difference",
@@ -375,6 +375,9 @@ function normalizeRevealOptions(value, path, strict = true) {
375
375
  if (record2.cooldown !== void 0) {
376
376
  out.cooldown = expectNonNegative(record2.cooldown, `${path}.cooldown`);
377
377
  }
378
+ if (record2.outcomeCooldown !== void 0) {
379
+ out.outcomeCooldown = expectNonNegative(record2.outcomeCooldown, `${path}.outcomeCooldown`);
380
+ }
378
381
  if (record2.bound !== void 0) {
379
382
  if (record2.bound !== "normal" && record2.bound !== "exact") {
380
383
  typeError(`${path}.bound`, 'must be "normal" or "exact"');
@@ -513,10 +516,12 @@ function splitEvaluateOptions(value, path) {
513
516
  }
514
517
  function resolveReveal(config, intent, opts, normalized = false) {
515
518
  const o = opts === void 0 ? {} : normalized ? opts : normalizeRevealOptions(opts, "options", false);
519
+ const cooldown = o.cooldown ?? intent.reveal?.cooldown ?? config?.reveal?.cooldown ?? intent.halfLife;
516
520
  return {
517
521
  threshold: o.threshold ?? intent.reveal?.threshold ?? config?.reveal?.threshold ?? DEFAULT_THRESHOLD,
518
522
  z: o.z ?? intent.reveal?.z ?? config?.reveal?.z ?? DEFAULT_Z,
519
- cooldown: o.cooldown ?? intent.reveal?.cooldown ?? config?.reveal?.cooldown ?? intent.halfLife,
523
+ cooldown,
524
+ outcomeCooldown: o.outcomeCooldown ?? intent.reveal?.outcomeCooldown ?? config?.reveal?.outcomeCooldown ?? cooldown,
520
525
  bound: o.bound ?? intent.reveal?.bound ?? config?.reveal?.bound ?? "normal"
521
526
  };
522
527
  }
@@ -621,7 +626,7 @@ function buildPrediction(is, name, reveal, at) {
621
626
  const p = betaMean(is.alpha, is.beta);
622
627
  const sd = betaSd(is.alpha, is.beta);
623
628
  const revealEnd = cooldownEnd(is.lastRevealedAt, reveal.cooldown, at);
624
- const outcomeEnd = cooldownEnd(is.lastOutcomeAt, reveal.cooldown, at);
629
+ const outcomeEnd = cooldownEnd(is.lastOutcomeAt, reveal.outcomeCooldown, at);
625
630
  const suppressedUntil = revealEnd === null ? outcomeEnd : outcomeEnd === null ? revealEnd : Math.max(revealEnd, outcomeEnd);
626
631
  return {
627
632
  intent: name,