oracular 0.3.0 → 0.4.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 +10 -0
- package/README.md +5 -3
- package/dist/cli.js +7 -3
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/schema/config.schema.json +141 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#22](https://github.com/Yuribenjamin/oracular/pull/22) [`0bb5841`](https://github.com/Yuribenjamin/oracular/commit/0bb5841fb93d2f16dcac74601f1ee218b70dda0e) Thanks [@Yuribenjamin](https://github.com/Yuribenjamin)! - Three small ergonomics for writing configs.
|
|
8
|
+
|
|
9
|
+
- **`validateConfig(config)`** runs the checks `createState` and `deserialize` run, without building a state, and returns the normalized copy they would store. It accepts `unknown`, so parsed JSON can be passed as it is; use it to fail fast at startup or in a test.
|
|
10
|
+
- **`ms`**: `ms.seconds(n)`, `ms.minutes(n)`, `ms.hours(n)`, `ms.days(n)` and `ms.weeks(n)` spell durations in milliseconds, so `halfLife: ms.days(7)` replaces `7 * 24 * 60 * 60 * 1000`. Plain multiplications, nothing else.
|
|
11
|
+
- **A JSON Schema for the config**, shipped in the package as `oracular/config.schema.json` and served at https://yuribenjamin.github.io/oracular/config.schema.json, for completion and checks on a config kept as JSON. `oracular replay --config file.json` now ignores a top-level `$schema` key, so a file can point at the schema and still be the config the engine runs.
|
|
12
|
+
|
|
3
13
|
## 0.3.0
|
|
4
14
|
|
|
5
15
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -250,6 +250,8 @@ createRecord(state, event, { installId, mode }): EventRecord // one recorder l
|
|
|
250
250
|
createEndRecord(at, { installId, mode }): EndRecord // one recorder line: the episode ended
|
|
251
251
|
serialize(state): string
|
|
252
252
|
deserialize(json: string, opts?: { config?: EngineConfig; migrate?: Migrate }): EngineState
|
|
253
|
+
validateConfig(config: unknown): EngineConfig // the checks createState runs, without a state; returns the normalized copy
|
|
254
|
+
ms.seconds(n), ms.minutes(n), ms.hours(n), ms.days(n), ms.weeks(n) // durations in milliseconds
|
|
253
255
|
STATE_VERSION // 2
|
|
254
256
|
RECORD_VERSION // 1
|
|
255
257
|
```
|
|
@@ -286,7 +288,7 @@ interface RevealOptions {
|
|
|
286
288
|
}
|
|
287
289
|
```
|
|
288
290
|
|
|
289
|
-
Reveal options resolve per call → intent → engine → defaults. Invalid config throws a `TypeError` or `RangeError` with the offending path in the message.
|
|
291
|
+
Reveal options resolve per call → intent → engine → defaults. Invalid config throws a `TypeError` or `RangeError` with the offending path in the message. `validateConfig(config)` runs those checks without building a state and returns the normalized copy `createState` stores, so a bad weight fails at startup or in a test rather than on first use. For a config kept as JSON, the package ships a [JSON Schema](./schema/config.schema.json) as `oracular/config.schema.json`, also served at <https://yuribenjamin.github.io/oracular/config.schema.json>, for editor completion; `oracular replay` ignores the `$schema` key such a file carries, the engine does not.
|
|
290
292
|
|
|
291
293
|
### Results
|
|
292
294
|
|
|
@@ -317,7 +319,7 @@ interface SignalExplanation {
|
|
|
317
319
|
|
|
318
320
|
### Time
|
|
319
321
|
|
|
320
|
-
`at` is a timestamp. Every duration (`halfLife`, `cooldown`, `episodeTimeout`, `learning.halfLife`) is in the same unit as `at`. Milliseconds from `Date.now()` are the obvious choice, but the library never calls `Date.now()` itself: you pass time in, which keeps every function deterministic and makes tests trivial. A clock that jumps backwards is tolerated (no decay, no error).
|
|
322
|
+
`at` is a timestamp. Every duration (`halfLife`, `cooldown`, `episodeTimeout`, `learning.halfLife`) is in the same unit as `at`. Milliseconds from `Date.now()` are the obvious choice, but the library never calls `Date.now()` itself: you pass time in, which keeps every function deterministic and makes tests trivial. A clock that jumps backwards is tolerated (no decay, no error). When `at` is in milliseconds, `ms.days(7)`, `ms.hours(3)` and `ms.minutes(30)` spell the durations; they are plain multiplications.
|
|
321
323
|
|
|
322
324
|
## Persistence
|
|
323
325
|
|
|
@@ -385,7 +387,7 @@ npx oracular replay shadow.ndjson --config oracular.config.json
|
|
|
385
387
|
|
|
386
388
|
## Roadmap
|
|
387
389
|
|
|
388
|
-
|
|
390
|
+
Now is 0.4, themed **learning that matches how people behave**: cross-episode attribution, so a session's signals can earn credit for an outcome days later; `npx oracular tune` to seed a config from a shadow log; presets; and small ergonomics (`validateConfig`, `ms`, a JSON Schema for the config). 0.3 shipped shadow mode, `npx oracular replay` and per-episode saturation. Then a written stability policy before 1.0. Framework bindings wait for a third integration. The full list, with the reasoning and what was declined, is on the [roadmap page](https://yuribenjamin.github.io/oracular/guide/roadmap).
|
|
389
391
|
|
|
390
392
|
## Contributing
|
|
391
393
|
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { readFileSync } from "fs";
|
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { extname, resolve } from "path";
|
|
9
9
|
import { pathToFileURL } from "url";
|
|
10
|
-
import {
|
|
10
|
+
import { validateConfig } from "oracular";
|
|
11
11
|
|
|
12
12
|
// cli/log.ts
|
|
13
13
|
import { RECORD_VERSION } from "oracular";
|
|
@@ -425,7 +425,8 @@ reveals per user, and a reliability table with a Brier score.
|
|
|
425
425
|
|
|
426
426
|
Options:
|
|
427
427
|
--config <file> the config to replay under: a .json file, or a .js, .mjs or .cjs
|
|
428
|
-
module whose default (or \`config\`) export is the config
|
|
428
|
+
module whose default (or \`config\`) export is the config. A top-level
|
|
429
|
+
"$schema" key in a .json file is ignored
|
|
429
430
|
--thresholds <list> thresholds to score, comma separated
|
|
430
431
|
(default: 0.3 to 0.9 by 0.1, plus each intent's configured threshold)
|
|
431
432
|
--window <duration> how long after a reveal an outcome still counts for it: 3d, 12h, 90m
|
|
@@ -544,6 +545,9 @@ async function loadConfig(path, io) {
|
|
|
544
545
|
} catch (error) {
|
|
545
546
|
throw new Error(`${path}: ${error.message}`);
|
|
546
547
|
}
|
|
548
|
+
if (typeof value === "object" && value !== null && "$schema" in value) {
|
|
549
|
+
value = Object.fromEntries(Object.entries(value).filter(([key]) => key !== "$schema"));
|
|
550
|
+
}
|
|
547
551
|
} else if (ext === ".js" || ext === ".mjs" || ext === ".cjs") {
|
|
548
552
|
const mod = await import(pathToFileURL(path).href);
|
|
549
553
|
value = mod.default ?? mod.config;
|
|
@@ -568,7 +572,7 @@ async function replayCommand(argv, io) {
|
|
|
568
572
|
const config = await loadConfig(configPath, io);
|
|
569
573
|
let declared;
|
|
570
574
|
try {
|
|
571
|
-
declared = Object.keys(
|
|
575
|
+
declared = Object.keys(validateConfig(config).intents);
|
|
572
576
|
} catch (error) {
|
|
573
577
|
throw new Error(
|
|
574
578
|
`${configPath} is not a valid config: ${error.message.replace(/^oracular: /, "")}`
|
package/dist/index.cjs
CHANGED
|
@@ -943,6 +943,20 @@ function reconcile(raw, config) {
|
|
|
943
943
|
};
|
|
944
944
|
}
|
|
945
945
|
|
|
946
|
+
// src/units.ts
|
|
947
|
+
var ms = {
|
|
948
|
+
seconds: (n) => n * 1e3,
|
|
949
|
+
minutes: (n) => n * 6e4,
|
|
950
|
+
hours: (n) => n * 36e5,
|
|
951
|
+
days: (n) => n * 864e5,
|
|
952
|
+
weeks: (n) => n * 6048e5
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
// src/validate.ts
|
|
956
|
+
function validateConfig(config) {
|
|
957
|
+
return normalizeConfig(config);
|
|
958
|
+
}
|
|
959
|
+
|
|
946
960
|
exports.RECORD_VERSION = RECORD_VERSION;
|
|
947
961
|
exports.STATE_VERSION = STATE_VERSION;
|
|
948
962
|
exports.createEndRecord = createEndRecord;
|
|
@@ -955,9 +969,11 @@ exports.evaluate = evaluate;
|
|
|
955
969
|
exports.evidenceToReveal = evidenceToReveal;
|
|
956
970
|
exports.explain = explain;
|
|
957
971
|
exports.markRevealed = markRevealed;
|
|
972
|
+
exports.ms = ms;
|
|
958
973
|
exports.observe = observe;
|
|
959
974
|
exports.predict = predict;
|
|
960
975
|
exports.serialize = serialize;
|
|
961
976
|
exports.shouldReveal = shouldReveal;
|
|
977
|
+
exports.validateConfig = validateConfig;
|
|
962
978
|
//# sourceMappingURL=index.cjs.map
|
|
963
979
|
//# sourceMappingURL=index.cjs.map
|