@kumbatio/energy-system 2.1.1 → 2.1.4
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 +37 -1
- package/README.md +9 -4
- package/SPEC.md +14 -10
- package/api-surface.json +1 -1
- package/conformance.json +1 -1
- package/dist/compat.d.ts +1 -1
- package/dist/defer.d.ts +1 -1
- package/dist/defer.d.ts.map +1 -1
- package/dist/defer.js +12 -23
- package/dist/defer.js.map +1 -1
- package/dist/demand.d.ts +2 -2
- package/dist/demand.d.ts.map +1 -1
- package/dist/demand.js +13 -40
- package/dist/demand.js.map +1 -1
- package/dist/dom.d.ts +1 -1
- package/dist/engine.d.ts +6 -2
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +17 -23
- package/dist/engine.js.map +1 -1
- package/dist/gate.d.ts +3 -3
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +12 -45
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +21 -21
- package/dist/levels.d.ts +1 -1
- package/dist/levels.d.ts.map +1 -1
- package/dist/levels.js +11 -14
- package/dist/levels.js.map +1 -1
- package/dist/metrics.d.ts +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +2 -5
- package/dist/metrics.js.map +1 -1
- package/dist/persistence.d.ts +1 -1
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +3 -2
- package/dist/persistence.js.map +1 -1
- package/dist/presence.d.ts +1 -1
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +3 -6
- package/dist/presence.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +2 -1
- package/dist/react.js.map +1 -1
- package/dist/reconcile.d.ts +1 -1
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +26 -0
- package/dist/runtime.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +11 -42
- package/dist/session.js.map +1 -1
- package/dist/strategies.d.ts +1 -1
- package/dist/strategies.d.ts.map +1 -1
- package/dist/strategies.js +30 -33
- package/dist/strategies.js.map +1 -1
- package/package.json +20 -21
- package/spec/energy-state.schema.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,39 @@ all. Prose returned by `describe()` is not covered; wording is a product decisio
|
|
|
14
14
|
|
|
15
15
|
## [Unreleased]
|
|
16
16
|
|
|
17
|
+
## [2.1.4] - 2026-09-28
|
|
18
|
+
|
|
19
|
+
I didn't write 2.1.4 notes, because nothing user-visible has changed since the last release.
|
|
20
|
+
|
|
21
|
+
Most of the commits you listed are already out. The repo has a `v2.1.3` tag (2c412b8), cut after v2.1.2. Every fix, spec change and docs change in your list (e69a145, 53a9f6f, a9078d4, eda186a, aece94f, 7517d79, 21fb98b, and the `.ts` specifier change) is under `## [2.1.3] - 2026-09-28` in `CHANGELOG.md`. Putting them in 2.1.4 would announce them twice.
|
|
22
|
+
|
|
23
|
+
Only two commits come after v2.1.3, and both are dev-tooling chores that your rules say to leave out:
|
|
24
|
+
|
|
25
|
+
- **dcf6b0d:** pnpm update in `packageManager`.
|
|
26
|
+
- **645086a:** updates release-kit (dev dependency) to `^2.11.0`, and refreshes pnpm and the TypeScript nightly in the lockfile.
|
|
27
|
+
|
|
28
|
+
## [2.1.3] - 2026-09-28
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- SPEC.md §10.2 now says who owns focus when a level change hides the focused element. A surface that only hides — a stylesheet rule, a headless gate like `<EnergyGate>` — must take the element out of the focus order, which the reference stylesheet and `<EnergyGate>` do; choosing a better destination than the document is the host's, from the level-change event. The requirement previously read as if the reference implementation moved focus itself, which it never did.
|
|
33
|
+
- The emitted type declarations now reference their sibling files with `.ts` specifiers (`from './types.ts'`), a side effect of the source running on Node's native type stripping. TypeScript 5.0 and later resolve these; the built package type-checks cleanly under TypeScript 5.0, 5.6, 5.9 and 7.0 with `node16`, `nodenext` and `bundler` resolution. Runtime JavaScript still imports `.js`.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- A manual `engine.hydrate()` no longer reverts a newer local write. When storage lagged the engine — a save still in flight, or one waiting on retry backoff — hydrating applied the older stored state unconditionally and then persisted it over the person's latest choice. Storage now replaces the current state only when it is the unproduced default or SPEC.md §4 prefers the stored state.
|
|
38
|
+
- `NotificationGate.dispose()` no longer loses a notification published re-entrantly from `onDeliver` during its final `released` delivery. The gate is inert before that delivery, so the re-entrant `publish()` throws instead of queueing into a gate nothing drains.
|
|
39
|
+
- `FocusSessionController.dispose()` no longer leaves a live session and a suppressed gate behind when a `stop` listener starts a new session during disposal. The re-entrant `start()` now throws, as it does on any disposed controller.
|
|
40
|
+
- `spec/energy-state.schema.json` and the SPEC.md §3 field table now bound `timestamp` at `Number.MAX_SAFE_INTEGER`, as they already bounded `revision`. The reference implementation has always rejected larger timestamps; the schema accepted them, so a state could validate against the published contract and still be refused here.
|
|
41
|
+
- The README's first React example imported a `useEnergy` hook that does not exist; it now uses `useEnergyLevel`.
|
|
42
|
+
- `spec/energy-state.schema.json` rejects a whitespace-only `origin`, as the reference implementation always has, and SPEC.md §3 now calls the field a non-blank string. The schema only required one character, so a producer could publish `" "` as a valid identity that this implementation then discarded. A new test holds the runtime and the schema to accepting exactly the same states, in both directions.
|
|
43
|
+
|
|
44
|
+
## [2.1.2] - 2026-09-12
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- React and `@types/react` 19.3.0 or later is now required as a peer dependency (previously `>=19.2.8` and `>=19.2.18`), and Node.js 24.21.0 or later is now required (previously 24.20.0). ([f731c06](https://github.com/kumbatio/energy-system/commit/f731c06))
|
|
49
|
+
|
|
17
50
|
## [2.1.1] - 2026-09-10
|
|
18
51
|
|
|
19
52
|
### Changed
|
|
@@ -236,7 +269,10 @@ strategies, the DOM adapter, the React provider and hooks, persistence adapters
|
|
|
236
269
|
with external observation, deterministic clocks, derived metrics, and legacy
|
|
237
270
|
level compatibility mapping.
|
|
238
271
|
|
|
239
|
-
[Unreleased]: https://github.com/kumbatio/energy-system/compare/v2.1.
|
|
272
|
+
[Unreleased]: https://github.com/kumbatio/energy-system/compare/v2.1.4...HEAD
|
|
273
|
+
[2.1.4]: https://github.com/kumbatio/energy-system/compare/v2.1.3...v2.1.4
|
|
274
|
+
[2.1.3]: https://github.com/kumbatio/energy-system/compare/v2.1.2...v2.1.3
|
|
275
|
+
[2.1.2]: https://github.com/kumbatio/energy-system/compare/v2.1.1...v2.1.2
|
|
240
276
|
[2.1.1]: https://github.com/kumbatio/energy-system/compare/v2.1.0...v2.1.1
|
|
241
277
|
[2.1.0]: https://github.com/kumbatio/energy-system/compare/v2.0.6...v2.1.0
|
|
242
278
|
[2.0.6]: https://github.com/kumbatio/energy-system/compare/v2.0.5...v2.0.6
|
package/README.md
CHANGED
|
@@ -26,9 +26,9 @@ engine.resolve(uiVisibilityStrategy) // -> what the UI should do about it
|
|
|
26
26
|
React:
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
|
-
import { EnergyProvider,
|
|
29
|
+
import { EnergyProvider, useEnergyLevel } from '@kumbatio/energy-system/react'
|
|
30
30
|
|
|
31
|
-
const
|
|
31
|
+
const [level, setLevel] = useEnergyLevel() // anywhere under <EnergyProvider>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
| If you want to | Go to |
|
|
@@ -218,6 +218,11 @@ capacity dipped. `<EnergyGate>` hides through React 19.2's `<Activity>`:
|
|
|
218
218
|
component state, DOM and scroll position survive, effects are torn down while
|
|
219
219
|
hidden and re-run on reveal, and hidden content is not server-rendered.
|
|
220
220
|
|
|
221
|
+
Hidden content leaves the focus order. If a level change hides the element that
|
|
222
|
+
had focus, the browser returns focus to the document; the gate cannot know a
|
|
223
|
+
better place for it, so move it from `onLevelChange` when your layout has one
|
|
224
|
+
([SPEC.md §10.2](./SPEC.md#10-accessibility-requirements)).
|
|
225
|
+
|
|
221
226
|
Opt out for subtrees whose cost is worth reclaiming - media, canvases, live
|
|
222
227
|
connections:
|
|
223
228
|
|
|
@@ -547,8 +552,8 @@ Recommended migration sequence:
|
|
|
547
552
|
|
|
548
553
|
## Persistence and reconciliation
|
|
549
554
|
|
|
550
|
-
Persisted states are validated strictly. A state must contain a legal level and source, a
|
|
551
|
-
non-negative timestamp, a non-negative integer revision, and a non-
|
|
555
|
+
Persisted states are validated strictly. A state must contain a legal level and source, a
|
|
556
|
+
non-negative safe-integer timestamp, a non-negative safe-integer revision, and a non-blank origin. Invalid records
|
|
552
557
|
are ignored rather than repaired into a more authoritative state.
|
|
553
558
|
|
|
554
559
|
The engine orders concurrent writes by timestamp, logical revision, source priority, and producer
|
package/SPEC.md
CHANGED
|
@@ -80,13 +80,13 @@ The unit of interchange is an **energy state**: one producer's point-in-time
|
|
|
80
80
|
claim about a person's capacity. Its serialized form is specified by
|
|
81
81
|
[`spec/energy-state.schema.json`](./spec/energy-state.schema.json).
|
|
82
82
|
|
|
83
|
-
| Field | Type
|
|
84
|
-
| ----------- |
|
|
85
|
-
| `level` | `0 \| 25 \| 50 \| 75 \| 100`
|
|
86
|
-
| `timestamp` | integer, epoch milliseconds
|
|
87
|
-
| `source` | `manual \| scheduled \| inferred`
|
|
88
|
-
| `revision` | non-negative safe integer
|
|
89
|
-
| `origin` | non-
|
|
83
|
+
| Field | Type | Role |
|
|
84
|
+
| ----------- | --------------------------------------------- | -------------------------------------------- |
|
|
85
|
+
| `level` | `0 \| 25 \| 50 \| 75 \| 100` | The declared capacity. |
|
|
86
|
+
| `timestamp` | non-negative safe integer, epoch milliseconds | When it was produced. Primary ordering key. |
|
|
87
|
+
| `source` | `manual \| scheduled \| inferred` | How it was arrived at. |
|
|
88
|
+
| `revision` | non-negative safe integer | Sequence among states sharing one timestamp. |
|
|
89
|
+
| `origin` | non-blank string | Stable identity of the producer. |
|
|
90
90
|
|
|
91
91
|
A state **MUST** be immutable once produced. Implementations **SHOULD** enforce
|
|
92
92
|
this with whatever their language offers.
|
|
@@ -379,9 +379,13 @@ requirements, not suggestions.
|
|
|
379
379
|
1. **Hiding MUST remove from the accessibility tree.** A level-hidden element
|
|
380
380
|
**MUST NOT** remain focusable or reachable by assistive technology. Visual
|
|
381
381
|
hiding alone leaves a keyboard trap into content that isn't there.
|
|
382
|
-
2. **Focus MUST survive a level change.** If
|
|
383
|
-
|
|
384
|
-
|
|
382
|
+
2. **Focus MUST survive a level change.** If a transition hides the focused
|
|
383
|
+
element, focus **MUST NOT** be left on it, and it **MUST** end up somewhere
|
|
384
|
+
sensible. A surface that only hides - a stylesheet rule, a headless gate -
|
|
385
|
+
cannot know where that is. It **MUST** take the element out of the focus
|
|
386
|
+
order (`display: none` does, and the platform then returns focus to the
|
|
387
|
+
document), and the choice of a better destination is the host's, made from
|
|
388
|
+
the level-change event it already receives (see item 4).
|
|
385
389
|
3. **Faded chrome MUST reveal on focus, not only on hover.** A control at 10%
|
|
386
390
|
opacity that a keyboard user can tab into but cannot see fails WCAG 2.4.7.
|
|
387
391
|
Every hover-reveal rule **MUST** have a `:focus-within` twin.
|
package/api-surface.json
CHANGED
package/conformance.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./spec/conformance.schema.json",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"timezone": "UTC",
|
|
5
5
|
"note": "Generated from the built library by scripts/extract-conformance.ts. See SPEC.md for the normative rules these vectors encode, including the runtime invariants that no vector can express.",
|
|
6
6
|
"levels": [
|
package/dist/compat.d.ts
CHANGED
package/dist/defer.d.ts
CHANGED
package/dist/defer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defer.d.ts","sourceRoot":"","sources":["../src/defer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"defer.d.ts","sourceRoot":"","sources":["../src/defer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAA;AAWjE,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,OAAO,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB;wBACnB,WAAW;0BACT,cAAc;8BACV,kBAAkB;0BACtB,cAAc;yBACf,aAAa;EAChB,CAAA;AAmBX;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,qBAA0B,GAClC,SAAS,cAAc,EAAE,CA4D3B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,IAAiB,GACrB,MAAM,GAAG,IAAI,CAIf;AAID,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5C,mEAAmE;IACnE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAyDD,eAAO,MAAM,gBAAgB,EAAE,kBAAkB,CAAC,cAAc,CAU/D,CAAA"}
|
package/dist/defer.js
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { getEnergyLevel } from './levels.js';
|
|
2
|
-
/**
|
|
3
|
-
* Deferral ("snooze") - the "not now" primitive. Deferring an item is an
|
|
4
|
-
* energy statement: it declares insufficient capacity for it right now and
|
|
5
|
-
* names when it should resurface. Presets are pure `(now) -> Date` functions;
|
|
6
|
-
* the energy-aware strategy orders them so the default suggestion matches
|
|
7
|
-
* current capacity (low energy -> longer deferrals, because items should
|
|
8
|
-
* resurface when capacity has plausibly recovered, not in an hour).
|
|
9
|
-
*/
|
|
10
|
-
function freezeObject(value) {
|
|
11
|
-
return Object.freeze(value);
|
|
12
|
-
}
|
|
13
2
|
/** Stable preset ids, exported so configs/strategies can reference them */
|
|
14
|
-
export const DEFERRAL_PRESET_IDS =
|
|
3
|
+
export const DEFERRAL_PRESET_IDS = Object.freeze({
|
|
15
4
|
inOneHour: 'in-1-hour',
|
|
16
5
|
thisEvening: 'this-evening',
|
|
17
6
|
tomorrowMorning: 'tomorrow-morning',
|
|
@@ -41,14 +30,14 @@ export function createDeferralPresets(options = {}) {
|
|
|
41
30
|
validateHour('morningHour', morningHour);
|
|
42
31
|
validateHour('eveningHour', eveningHour);
|
|
43
32
|
return Object.freeze([
|
|
44
|
-
|
|
33
|
+
Object.freeze({
|
|
45
34
|
id: DEFERRAL_PRESET_IDS.inOneHour,
|
|
46
35
|
label: 'In 1 hour',
|
|
47
36
|
compute(now) {
|
|
48
37
|
return new Date(now.getTime() + 60 * 60_000);
|
|
49
38
|
},
|
|
50
39
|
}),
|
|
51
|
-
|
|
40
|
+
Object.freeze({
|
|
52
41
|
id: DEFERRAL_PRESET_IDS.thisEvening,
|
|
53
42
|
label: `This evening (${eveningHour}:00)`,
|
|
54
43
|
compute(now) {
|
|
@@ -60,7 +49,7 @@ export function createDeferralPresets(options = {}) {
|
|
|
60
49
|
return evening;
|
|
61
50
|
},
|
|
62
51
|
}),
|
|
63
|
-
|
|
52
|
+
Object.freeze({
|
|
64
53
|
id: DEFERRAL_PRESET_IDS.tomorrowMorning,
|
|
65
54
|
label: `Tomorrow morning (${morningHour}:00)`,
|
|
66
55
|
compute(now) {
|
|
@@ -70,7 +59,7 @@ export function createDeferralPresets(options = {}) {
|
|
|
70
59
|
return morning;
|
|
71
60
|
},
|
|
72
61
|
}),
|
|
73
|
-
|
|
62
|
+
Object.freeze({
|
|
74
63
|
id: DEFERRAL_PRESET_IDS.nextWorkday,
|
|
75
64
|
label: `Next workday (${morningHour}:00)`,
|
|
76
65
|
compute(now) {
|
|
@@ -83,7 +72,7 @@ export function createDeferralPresets(options = {}) {
|
|
|
83
72
|
return day;
|
|
84
73
|
},
|
|
85
74
|
}),
|
|
86
|
-
|
|
75
|
+
Object.freeze({
|
|
87
76
|
id: DEFERRAL_PRESET_IDS.nextMonday,
|
|
88
77
|
label: `Next Monday (${morningHour}:00)`,
|
|
89
78
|
compute(now) {
|
|
@@ -107,8 +96,8 @@ export function resolveDeferral(presets, presetId, now = new Date()) {
|
|
|
107
96
|
return preset.compute(now).getTime();
|
|
108
97
|
}
|
|
109
98
|
const IDS = DEFERRAL_PRESET_IDS;
|
|
110
|
-
const DEFERRAL_CONFIGS =
|
|
111
|
-
100:
|
|
99
|
+
const DEFERRAL_CONFIGS = Object.freeze({
|
|
100
|
+
100: Object.freeze({
|
|
112
101
|
orderedPresetIds: Object.freeze([
|
|
113
102
|
IDS.inOneHour,
|
|
114
103
|
IDS.thisEvening,
|
|
@@ -118,7 +107,7 @@ const DEFERRAL_CONFIGS = freezeObject({
|
|
|
118
107
|
]),
|
|
119
108
|
defaultPresetId: IDS.inOneHour,
|
|
120
109
|
}),
|
|
121
|
-
75:
|
|
110
|
+
75: Object.freeze({
|
|
122
111
|
orderedPresetIds: Object.freeze([
|
|
123
112
|
IDS.inOneHour,
|
|
124
113
|
IDS.thisEvening,
|
|
@@ -128,7 +117,7 @@ const DEFERRAL_CONFIGS = freezeObject({
|
|
|
128
117
|
]),
|
|
129
118
|
defaultPresetId: IDS.inOneHour,
|
|
130
119
|
}),
|
|
131
|
-
50:
|
|
120
|
+
50: Object.freeze({
|
|
132
121
|
orderedPresetIds: Object.freeze([
|
|
133
122
|
IDS.thisEvening,
|
|
134
123
|
IDS.tomorrowMorning,
|
|
@@ -138,7 +127,7 @@ const DEFERRAL_CONFIGS = freezeObject({
|
|
|
138
127
|
]),
|
|
139
128
|
defaultPresetId: IDS.thisEvening,
|
|
140
129
|
}),
|
|
141
|
-
25:
|
|
130
|
+
25: Object.freeze({
|
|
142
131
|
orderedPresetIds: Object.freeze([
|
|
143
132
|
IDS.tomorrowMorning,
|
|
144
133
|
IDS.nextWorkday,
|
|
@@ -148,7 +137,7 @@ const DEFERRAL_CONFIGS = freezeObject({
|
|
|
148
137
|
]),
|
|
149
138
|
defaultPresetId: IDS.tomorrowMorning,
|
|
150
139
|
}),
|
|
151
|
-
0:
|
|
140
|
+
0: Object.freeze({
|
|
152
141
|
orderedPresetIds: Object.freeze([
|
|
153
142
|
IDS.tomorrowMorning,
|
|
154
143
|
IDS.nextMonday,
|
package/dist/defer.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defer.js","sourceRoot":"","sources":["../src/defer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"defer.js","sourceRoot":"","sources":["../src/defer.ts"],"names":[],"mappings":"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"}
|
package/dist/demand.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AutonomyConfig } from './strategies.
|
|
2
|
-
import type { AdaptationStrategy } from './types.
|
|
1
|
+
import type { AutonomyConfig } from './strategies.ts';
|
|
2
|
+
import type { AdaptationStrategy } from './types.ts';
|
|
3
3
|
/**
|
|
4
4
|
* Standing of the originator relative to the user.
|
|
5
5
|
*
|
package/dist/demand.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demand.d.ts","sourceRoot":"","sources":["../src/demand.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"demand.d.ts","sourceRoot":"","sources":["../src/demand.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAA;AA2BjE;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;AAI3D,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAExE;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAA;AAE/D,yCAAyC;AACzC,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;AAE/D,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,GAC3B,mBAAmB,GACnB,eAAe,GACf,eAAe,GACf,yBAAyB,GACzB,kBAAkB,GAClB,cAAc,CAAA;AAElB;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAA;CACxC;AAED,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,oBAAoB,GAAG,IAAI,CAAA;IACpD,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAA;CACrC;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;IACvC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjE,gFAAgF;IAChF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,oBAAoB,EAAE,oBAAoB,CAAA;CACpD;AAkCD,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,qBAAqB,CAmB7E,CAAA;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,WAAW,GAClB,aAAa,CAoEf"}
|
package/dist/demand.js
CHANGED
|
@@ -1,54 +1,27 @@
|
|
|
1
1
|
import { getEnergyLevel } from './levels.js';
|
|
2
|
-
/**
|
|
3
|
-
* Inbound demand - anything arriving from outside that asks for the user's
|
|
4
|
-
* attention or action: an email, a document comment, a review request, a task
|
|
5
|
-
* assignment, a collaboration invite.
|
|
6
|
-
*
|
|
7
|
-
* Email is the only queue in modern life with no backpressure: infinite
|
|
8
|
-
* senders write to a finite human with zero feedback about that human's
|
|
9
|
-
* capacity. Every other triage system is organised around properties of the
|
|
10
|
-
* *message* (sender, urgency, type); this one is organised around the state of
|
|
11
|
-
* the *recipient*.
|
|
12
|
-
*
|
|
13
|
-
* This module is the policy half only, and it is pure. It decides whether a
|
|
14
|
-
* piece of demand reaches the user now, is acknowledged to its originator and
|
|
15
|
-
* captured for later, or is captured silently. It never performs any of those
|
|
16
|
-
* effects: acknowledging an originator means sending mail, posting a comment,
|
|
17
|
-
* or updating a status chip depending on the app, and those effects are
|
|
18
|
-
* irreversible in ways an in-process runtime cannot make transactional. The
|
|
19
|
-
* consuming app owns the orchestration; it owns the ordering, the retries, and
|
|
20
|
-
* the deduplication that go with irreversible outbound effects.
|
|
21
|
-
*
|
|
22
|
-
* Pair the outcome with `deferralStrategy` for the resurface horizon: the
|
|
23
|
-
* acknowledgment states when the user will actually get to the item, and the
|
|
24
|
-
* energy-ordered deferral default is that answer.
|
|
25
|
-
*/
|
|
26
|
-
function freezeObject(value) {
|
|
27
|
-
return Object.freeze(value);
|
|
28
|
-
}
|
|
29
2
|
const ORIGINATOR_TIERS = new Set(['exempt', 'known', 'unknown']);
|
|
30
3
|
/** Validate that an unknown value is a valid OriginatorTier */
|
|
31
4
|
export function isOriginatorTier(value) {
|
|
32
5
|
return typeof value === 'string' && ORIGINATOR_TIERS.has(value);
|
|
33
6
|
}
|
|
34
|
-
const ADMISSION_CONFIGS =
|
|
7
|
+
const ADMISSION_CONFIGS = Object.freeze({
|
|
35
8
|
// Full capacity: no policy at all. Everything reaches the user.
|
|
36
|
-
100:
|
|
9
|
+
100: Object.freeze({
|
|
37
10
|
originatorThreshold: 'all',
|
|
38
11
|
acknowledge: false,
|
|
39
12
|
acknowledgmentDetail: 'full',
|
|
40
13
|
}),
|
|
41
|
-
75:
|
|
14
|
+
75: Object.freeze({
|
|
42
15
|
originatorThreshold: 'known',
|
|
43
16
|
acknowledge: true,
|
|
44
17
|
acknowledgmentDetail: 'full',
|
|
45
18
|
}),
|
|
46
|
-
50:
|
|
19
|
+
50: Object.freeze({
|
|
47
20
|
originatorThreshold: 'exempt',
|
|
48
21
|
acknowledge: true,
|
|
49
22
|
acknowledgmentDetail: 'full',
|
|
50
23
|
}),
|
|
51
|
-
25:
|
|
24
|
+
25: Object.freeze({
|
|
52
25
|
originatorThreshold: 'exempt',
|
|
53
26
|
acknowledge: true,
|
|
54
27
|
acknowledgmentDetail: 'brief',
|
|
@@ -56,7 +29,7 @@ const ADMISSION_CONFIGS = freezeObject({
|
|
|
56
29
|
// Rest: the acknowledgment survives, stripped to a fixed template. The
|
|
57
30
|
// originator's social debt still clears - that is the whole point of the
|
|
58
31
|
// loop - but nothing is composed on the user's behalf.
|
|
59
|
-
0:
|
|
32
|
+
0: Object.freeze({
|
|
60
33
|
originatorThreshold: 'exempt',
|
|
61
34
|
acknowledge: true,
|
|
62
35
|
acknowledgmentDetail: 'minimal',
|
|
@@ -114,14 +87,14 @@ export function resolveDemandOutcome(config, autonomy, demand) {
|
|
|
114
87
|
// "deprioritised" and escalates through another channel only succeeds if
|
|
115
88
|
// their escalation is one the user cannot ignore - which makes them exempt.
|
|
116
89
|
if (demand.originatorTier === 'exempt') {
|
|
117
|
-
return
|
|
90
|
+
return Object.freeze({
|
|
118
91
|
admission: 'live',
|
|
119
92
|
acknowledgment: null,
|
|
120
93
|
reason: 'exempt-originator',
|
|
121
94
|
});
|
|
122
95
|
}
|
|
123
96
|
if (isTierAdmitted(config.originatorThreshold, demand.originatorTier)) {
|
|
124
|
-
return
|
|
97
|
+
return Object.freeze({
|
|
125
98
|
admission: 'live',
|
|
126
99
|
acknowledgment: null,
|
|
127
100
|
reason: 'tier-admitted',
|
|
@@ -130,14 +103,14 @@ export function resolveDemandOutcome(config, autonomy, demand) {
|
|
|
130
103
|
// Informational mail asks nothing, so there is nothing to acknowledge or
|
|
131
104
|
// capture. Whether it should *interrupt* is the notification gate's question.
|
|
132
105
|
if (!demand.bearsObligation) {
|
|
133
|
-
return
|
|
106
|
+
return Object.freeze({
|
|
134
107
|
admission: 'live',
|
|
135
108
|
acknowledgment: null,
|
|
136
109
|
reason: 'no-obligation',
|
|
137
110
|
});
|
|
138
111
|
}
|
|
139
112
|
if (!config.acknowledge) {
|
|
140
|
-
return
|
|
113
|
+
return Object.freeze({
|
|
141
114
|
admission: 'silent',
|
|
142
115
|
acknowledgment: null,
|
|
143
116
|
reason: 'acknowledgment-disabled',
|
|
@@ -148,15 +121,15 @@ export function resolveDemandOutcome(config, autonomy, demand) {
|
|
|
148
121
|
// acknowledgment on the user's behalf. Silence is recoverable; a sent message
|
|
149
122
|
// is not.
|
|
150
123
|
if (confidence < autonomy.confidenceThreshold) {
|
|
151
|
-
return
|
|
124
|
+
return Object.freeze({
|
|
152
125
|
admission: 'silent',
|
|
153
126
|
acknowledgment: null,
|
|
154
127
|
reason: 'below-confidence',
|
|
155
128
|
});
|
|
156
129
|
}
|
|
157
|
-
return
|
|
130
|
+
return Object.freeze({
|
|
158
131
|
admission: 'acknowledge',
|
|
159
|
-
acknowledgment:
|
|
132
|
+
acknowledgment: Object.freeze({
|
|
160
133
|
detail: config.acknowledgmentDetail,
|
|
161
134
|
allowGeneratedContent: autonomy.allowGeneratedContent,
|
|
162
135
|
}),
|
package/dist/demand.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"demand.js","sourceRoot":"","sources":["../src/demand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"demand.js","sourceRoot":"","sources":["../src/demand.ts"],"names":[],"mappings":"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"}
|
package/dist/dom.d.ts
CHANGED
package/dist/engine.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdaptationStrategy, EnergyClock, EnergyChangeListener, EnergyLevel, EnergyPersistence, EnergySource, EnergyState } from './types.
|
|
1
|
+
import type { AdaptationStrategy, EnergyClock, EnergyChangeListener, EnergyLevel, EnergyPersistence, EnergySource, EnergyState } from './types.ts';
|
|
2
2
|
export interface EnergyEngineOptions {
|
|
3
3
|
initialLevel?: EnergyLevel;
|
|
4
4
|
persistence?: EnergyPersistence;
|
|
@@ -47,7 +47,11 @@ export interface EnergyEngine {
|
|
|
47
47
|
subscribe(listener: EnergyChangeListener): () => void;
|
|
48
48
|
/** Resolve a strategy against current energy state */
|
|
49
49
|
resolve<T>(strategy: AdaptationStrategy<T>): T;
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Load persisted state (called automatically, but can be called manually).
|
|
52
|
+
* The stored state replaces the current one only when the current state is
|
|
53
|
+
* the unproduced default or `isPreferredEnergyState` prefers the stored one.
|
|
54
|
+
*/
|
|
51
55
|
hydrate(): Promise<void>;
|
|
52
56
|
/**
|
|
53
57
|
* Wait until the current state version is durably persisted.
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACjE,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,MAAM,CAAC,CAAA;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;;;;;;OAUG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,KAAK,IAAI,IAAI,CAAA;IACb,+BAA+B;IAC/B,QAAQ,IAAI,WAAW,CAAA;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IACzD,iCAAiC;IACjC,UAAU,IAAI,IAAI,CAAA;IAClB,gEAAgE;IAChE,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAA;IACrD,sDAAsD;IACtD,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAC9C;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB;;;;OAIG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,mDAAmD;IACnD,OAAO,IAAI,IAAI,CAAA;CAChB;AAsCD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB,GAAG,YAAY,CAmXlF"}
|
package/dist/engine.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import { UNPRODUCED_ORIGIN, UNPRODUCED_TIMESTAMP, createEnergyOrigin, createEnergyState, cycleEnergyLevel, parseExternalEnergyState, } from './levels.js';
|
|
1
|
+
import { UNPRODUCED_ORIGIN, UNPRODUCED_TIMESTAMP, createEnergyOrigin, createEnergyState, cycleEnergyLevel, isUnproducedState, parseExternalEnergyState, } from './levels.js';
|
|
2
2
|
import { isPreferredEnergyState } from './reconcile.js';
|
|
3
|
-
|
|
4
|
-
console.error(`[energy-system] ${message}`, err);
|
|
5
|
-
}
|
|
3
|
+
import { logContainedError, resolveNow } from './runtime.js';
|
|
6
4
|
const PERSIST_RETRY_INITIAL_MS = 250;
|
|
7
5
|
const PERSIST_RETRY_MAX_MS = 30_000;
|
|
8
6
|
const DEFAULT_MAX_FUTURE_SKEW_MS = 5 * 60_000;
|
|
9
|
-
function resolveNow(clock) {
|
|
10
|
-
if (typeof clock === 'function')
|
|
11
|
-
return clock;
|
|
12
|
-
if (clock?.now)
|
|
13
|
-
return () => clock.now();
|
|
14
|
-
return () => Date.now();
|
|
15
|
-
}
|
|
16
7
|
function isSameState(a, b) {
|
|
17
8
|
return (a.level === b.level &&
|
|
18
9
|
a.timestamp === b.timestamp &&
|
|
@@ -109,13 +100,13 @@ export function createEnergyEngine(options = {}) {
|
|
|
109
100
|
resolvePersistWaiters();
|
|
110
101
|
}
|
|
111
102
|
catch (err) {
|
|
112
|
-
|
|
103
|
+
logContainedError('Failed to persist energy state', err);
|
|
113
104
|
if (onPersistenceError) {
|
|
114
105
|
try {
|
|
115
106
|
onPersistenceError(err, snapshot);
|
|
116
107
|
}
|
|
117
108
|
catch (err) {
|
|
118
|
-
|
|
109
|
+
logContainedError('onPersistenceError callback threw', err);
|
|
119
110
|
}
|
|
120
111
|
}
|
|
121
112
|
isPersisting = false;
|
|
@@ -161,7 +152,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
161
152
|
onChange(transition.next, transition.prev);
|
|
162
153
|
}
|
|
163
154
|
catch (err) {
|
|
164
|
-
|
|
155
|
+
logContainedError('onChange listener threw', err);
|
|
165
156
|
}
|
|
166
157
|
}
|
|
167
158
|
const transitionListeners = [...listeners];
|
|
@@ -170,7 +161,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
170
161
|
listener(transition.next, transition.prev);
|
|
171
162
|
}
|
|
172
163
|
catch (err) {
|
|
173
|
-
|
|
164
|
+
logContainedError('Energy subscriber threw', err);
|
|
174
165
|
}
|
|
175
166
|
}
|
|
176
167
|
}
|
|
@@ -201,7 +192,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
201
192
|
return;
|
|
202
193
|
started = true;
|
|
203
194
|
initialHydrationTask = engine.hydrate().catch((err) => {
|
|
204
|
-
|
|
195
|
+
logContainedError('Unexpected hydrate failure', err);
|
|
205
196
|
});
|
|
206
197
|
if (!persistence.observe)
|
|
207
198
|
return;
|
|
@@ -214,7 +205,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
214
205
|
normalized = normalizeState(externalState, now(), maxFutureSkewMs);
|
|
215
206
|
}
|
|
216
207
|
catch (err) {
|
|
217
|
-
|
|
208
|
+
logContainedError('Ignoring invalid observed energy state', err);
|
|
218
209
|
return;
|
|
219
210
|
}
|
|
220
211
|
if (!isPreferredEnergyState(normalized, state))
|
|
@@ -223,7 +214,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
223
214
|
});
|
|
224
215
|
}
|
|
225
216
|
catch (err) {
|
|
226
|
-
|
|
217
|
+
logContainedError('Failed to subscribe to persistence observation', err);
|
|
227
218
|
}
|
|
228
219
|
},
|
|
229
220
|
getState() {
|
|
@@ -267,7 +258,6 @@ export function createEnergyEngine(options = {}) {
|
|
|
267
258
|
async hydrate() {
|
|
268
259
|
if (!persistence || disposed)
|
|
269
260
|
return;
|
|
270
|
-
const hydrateStartVersion = stateVersion;
|
|
271
261
|
let stored;
|
|
272
262
|
try {
|
|
273
263
|
stored = await persistence.load();
|
|
@@ -276,7 +266,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
276
266
|
}
|
|
277
267
|
catch (err) {
|
|
278
268
|
persistenceLoadError = err;
|
|
279
|
-
|
|
269
|
+
logContainedError('Failed to load persisted energy state', err);
|
|
280
270
|
return;
|
|
281
271
|
}
|
|
282
272
|
if (!stored || disposed)
|
|
@@ -286,12 +276,16 @@ export function createEnergyEngine(options = {}) {
|
|
|
286
276
|
normalized = normalizeState(stored, now(), maxFutureSkewMs);
|
|
287
277
|
}
|
|
288
278
|
catch (err) {
|
|
289
|
-
|
|
279
|
+
logContainedError('Ignoring invalid persisted energy state', err);
|
|
290
280
|
return;
|
|
291
281
|
}
|
|
292
282
|
if (isSameState(normalized, state))
|
|
293
283
|
return;
|
|
294
|
-
|
|
284
|
+
// Storage replaces the untouched default unconditionally (SPEC.md §3.2),
|
|
285
|
+
// and anything else only when §4 prefers it. A later manual hydrate can
|
|
286
|
+
// read storage that lags this engine - a save still in flight, or one
|
|
287
|
+
// waiting on backoff - and must not revert the newer local state.
|
|
288
|
+
if (isUnproducedState(state) || isPreferredEnergyState(normalized, state)) {
|
|
295
289
|
applyState(normalized);
|
|
296
290
|
}
|
|
297
291
|
},
|
|
@@ -331,7 +325,7 @@ export function createEnergyEngine(options = {}) {
|
|
|
331
325
|
disposePersistenceObservation();
|
|
332
326
|
}
|
|
333
327
|
catch (err) {
|
|
334
|
-
|
|
328
|
+
logContainedError('Failed to release persistence observation', err);
|
|
335
329
|
}
|
|
336
330
|
if (persistRetryTimer) {
|
|
337
331
|
clearTimeout(persistRetryTimer);
|