@adam0white/human-framework 2.10.0 → 2.11.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.
@@ -40,6 +40,7 @@ import { agendaTermsWithGoalEstimates } from '../agenda/index.js';
40
40
  import { trustOf } from '../beliefs/index.js';
41
41
  import { normTerms } from '../conscience/index.js';
42
42
  import { clamp01, cmpStr, dexp, minuteOfDay, round } from '../core/index.js';
43
+ import { curiosityTerms } from '../curiosity/index.js';
43
44
  import { habitEase, habitPull } from '../habits/index.js';
44
45
  import { expectedEffect } from '../memory/index.js';
45
46
  import { methodEvidence } from '../methods/index.js';
@@ -452,6 +453,9 @@ export function consider(p, aff, ctx) {
452
453
  if (!inBreak(p))
453
454
  for (const t of rememberedTerms(p, aff, ctx.now, voices))
454
455
  push(t.source, t.value);
456
+ if (p.curiosity)
457
+ for (const t of curiosityTerms(p, aff, ctx))
458
+ push(t.source, t.value);
455
459
  // Promptings (2.4.0, opt-in): a recurrence pull under `fault` and a practised turning-away from a noticed prompt.
456
460
  if (p.promptings)
457
461
  for (const t of promptingTerms(p, aff.action, terms, aff.tags, ctx.now, ctx.desperation))
@@ -0,0 +1,28 @@
1
+ import type { ConsiderContext } from '../cognition/index.ts';
2
+ import type { Affordance, CuriosityReading, CuriositySpec, CuriosityState, Minute, Outcome, Person, Term } from '../types.ts';
3
+ /** Engineering defaults: unknown .8 exceeds mild effort (.1 base) and learned pleasure (.6 scale).
4
+ * Need relief uses cognition's scale 4; urgency and fear also suppress this entire drive.
5
+ */
6
+ export declare const CURIOSITY_DEFAULTS: {
7
+ readonly unknownScale: 0.8;
8
+ readonly progressScale: 2;
9
+ readonly varyScale: 0.15;
10
+ readonly recentMinutes: 60;
11
+ readonly rewardMax: 0.03;
12
+ readonly fastAlpha: 0.5;
13
+ readonly slowAlpha: 0.15;
14
+ readonly maxKeys: 32;
15
+ readonly maxRecent: 8;
16
+ };
17
+ /** Enable or reconfigure; retain the bounded history. Invalid overrides use defaults. */
18
+ export declare function enableCuriosity(p: Person, spec?: CuriositySpec): CuriosityState;
19
+ type Context = Pick<ConsiderContext, 'needs' | 'mods' | 'now'>;
20
+ /** Read raw signals and drive without mutation or RNG. `now` changes only the recent-act window. */
21
+ export declare function curiosityOf(p: Person, aff: Affordance, now?: Minute): CuriosityReading;
22
+ /** Cognition uses the actual decision context, including perceived needs. Fixed term order. */
23
+ export declare function curiosityTerms(p: Person, aff: Affordance, ctx: Context): Term[];
24
+ /** Called before memory learns this outcome. Needs owner applies the small, bounded reward. */
25
+ export declare function noteCuriosity(p: Person, aff: Affordance, outcome: Outcome, felt: number): void;
26
+ /** Conservative restore: drop malformed or ambiguous slices; skip malformed entries and bound lists. */
27
+ export declare function sanitizeCuriosity(input: unknown, now: Minute): CuriosityState | undefined;
28
+ export {};
@@ -0,0 +1,190 @@
1
+ /**
2
+ * SCOPE: opt-in intrinsic exploration and play. Uncertainty draws a relaxed person toward unfamiliar
3
+ * offers; falling prediction error (learning progress) pulls and rewards; recent action kinds invite variation in target or
4
+ * place. Shapes, not fitted values: learning progress (Oudeyer, Kaplan & Hafner 2007), information gaps
5
+ * (Loewenstein 1994; Kidd & Hayden 2015; Gottlieb et al. 2013), effectance and competence (White 1959;
6
+ * Deci & Ryan), relaxed repetition with variation (Burghardt 2005), openness (DeYoung), childhood
7
+ * exploration (Gopnik 2020). See research/curiosity-and-play.md. All coefficients are engineering defaults.
8
+ * This owns only p.curiosity. It neither adopts projects nor plans, reads no host novelty tags, and
9
+ * implements no faith lens, LLM or RNG. Without the slice it adds no state, terms or reward.
10
+ */
11
+ import { emotionLevel } from '../affect/index.js';
12
+ import { clamp01, isNum, isObj, round } from '../core/index.js';
13
+ import { lifeModifiers } from '../lifecourse/index.js';
14
+ import { expectationKey, expectedEffect } from '../memory/index.js';
15
+ import { satisfy } from '../needs/index.js';
16
+ import { readPerson } from '../person.js';
17
+ import { MAX_MINUTE, PHYSIOLOGICAL_NEEDS } from '../types.js';
18
+ /** Engineering defaults: unknown .8 exceeds mild effort (.1 base) and learned pleasure (.6 scale).
19
+ * Need relief uses cognition's scale 4; urgency and fear also suppress this entire drive.
20
+ */
21
+ export const CURIOSITY_DEFAULTS = {
22
+ unknownScale: 0.8,
23
+ progressScale: 2,
24
+ varyScale: 0.15,
25
+ recentMinutes: 60,
26
+ rewardMax: 0.03,
27
+ fastAlpha: 0.5,
28
+ slowAlpha: 0.15,
29
+ maxKeys: 32,
30
+ maxRecent: 8,
31
+ };
32
+ const SPEC_KEYS = ['unknownScale', 'progressScale', 'varyScale', 'recentMinutes', 'rewardMax'];
33
+ function cleanSpec(input) {
34
+ const out = {};
35
+ if (!isObj(input))
36
+ return out;
37
+ for (const key of SPEC_KEYS) {
38
+ const x = input[key];
39
+ const max = key === 'recentMinutes' ? 1440 : CURIOSITY_DEFAULTS[key];
40
+ if (isNum(x) && x >= 0 && x <= max)
41
+ out[key] = x;
42
+ }
43
+ return out;
44
+ }
45
+ const setting = (s, key) => s.spec?.[key] ?? CURIOSITY_DEFAULTS[key];
46
+ /** Enable or reconfigure; retain the bounded history. Invalid overrides use defaults. */
47
+ export function enableCuriosity(p, spec = {}) {
48
+ const out = { errors: p.curiosity?.errors ?? [], recent: p.curiosity?.recent ?? [] };
49
+ const clean = cleanSpec(spec);
50
+ if (Object.keys(clean).length)
51
+ out.spec = clean;
52
+ p.curiosity = out;
53
+ return out;
54
+ }
55
+ /** Identifiers the slice records and restores; longer ones are not tracked, so a save round-trips. */
56
+ const str = (x) => typeof x === 'string' && x.length > 0 && x.length <= 512;
57
+ function reading(p, aff, ctx) {
58
+ const s = p.curiosity;
59
+ if (!s)
60
+ return { unknown: 0, progress: 0, vary: 0, drive: 0 };
61
+ let urgency = 0;
62
+ for (const n of ctx.needs)
63
+ if (n.id === 'safety' || PHYSIOLOGICAL_NEEDS.includes(n.id))
64
+ urgency = Math.max(urgency, n.urgency);
65
+ const drive = (1 - clamp01(urgency)) *
66
+ (1 - emotionLevel(p, 'fear')) *
67
+ (0.25 + 0.75 * p.traits.openness) *
68
+ (1.25 - 0.5 * ctx.mods.maturity);
69
+ const error = s.errors.find((e) => e.key === expectationKey(aff));
70
+ const recent = s.recent.filter((r) => r.at <= ctx.now && ctx.now - r.at <= setting(s, 'recentMinutes') && r.action === aff.action);
71
+ const identical = recent.some((r) => r.targetId === aff.targetId && r.placeId === aff.placeId);
72
+ return {
73
+ unknown: clamp01(1 - expectedEffect(p, aff).confidence),
74
+ progress: error ? clamp01(error.slow - error.fast) : 0,
75
+ vary: !identical && recent.length > 0 ? 1 : 0,
76
+ drive,
77
+ };
78
+ }
79
+ /** Read raw signals and drive without mutation or RNG. `now` changes only the recent-act window. */
80
+ export function curiosityOf(p, aff, now = p.now) {
81
+ if (!p.curiosity)
82
+ return { unknown: 0, progress: 0, vary: 0, drive: 0 };
83
+ return reading(p, aff, { now, needs: readPerson(p).needs, mods: lifeModifiers(p) });
84
+ }
85
+ /** Cognition uses the actual decision context, including perceived needs. Fixed term order. */
86
+ export function curiosityTerms(p, aff, ctx) {
87
+ const s = p.curiosity;
88
+ if (!s)
89
+ return [];
90
+ const r = reading(p, aff, ctx);
91
+ return ['unknown', 'progress', 'vary'].map((key) => ({
92
+ source: `curiosity:${key}`,
93
+ value: round(Math.min(setting(s, `${key}Scale`), setting(s, `${key}Scale`) * r[key] * r.drive)),
94
+ }));
95
+ }
96
+ /** Called before memory learns this outcome. Needs owner applies the small, bounded reward. */
97
+ export function noteCuriosity(p, aff, outcome, felt) {
98
+ const s = p.curiosity;
99
+ if (!s || outcome.status === 'interrupted')
100
+ return;
101
+ const before = curiosityOf(p, aff);
102
+ const expected = expectedEffect(p, aff);
103
+ const error = clamp01((Math.abs((outcome.status === 'completed' ? 1 : 0) - expected.successRate) +
104
+ Math.abs(felt - expected.valence) / 2) /
105
+ 2);
106
+ const key = expectationKey(aff);
107
+ if (!str(key) ||
108
+ !str(aff.action) ||
109
+ (aff.targetId !== undefined && !str(aff.targetId)) ||
110
+ (aff.placeId !== undefined && !str(aff.placeId)))
111
+ return;
112
+ let entry = s.errors.find((e) => e.key === key);
113
+ if (!entry) {
114
+ if (s.errors.length >= CURIOSITY_DEFAULTS.maxKeys) {
115
+ let oldest = 0;
116
+ let oldestAt = s.errors[0]?.lastAt ?? p.now;
117
+ for (const [i, held] of s.errors.entries()) {
118
+ if (held.lastAt < oldestAt) {
119
+ oldest = i;
120
+ oldestAt = held.lastAt;
121
+ }
122
+ }
123
+ s.errors.splice(oldest, 1);
124
+ }
125
+ entry = { key, fast: error, slow: error, n: 1, lastAt: p.now };
126
+ s.errors.push(entry);
127
+ }
128
+ else {
129
+ entry.fast = clamp01(entry.fast + CURIOSITY_DEFAULTS.fastAlpha * (error - entry.fast));
130
+ entry.slow = clamp01(entry.slow + CURIOSITY_DEFAULTS.slowAlpha * (error - entry.slow));
131
+ entry.n = Math.min(Number.MAX_SAFE_INTEGER, entry.n + 1);
132
+ entry.lastAt = p.now;
133
+ }
134
+ const act = { action: aff.action, at: p.now };
135
+ if (aff.targetId !== undefined)
136
+ act.targetId = aff.targetId;
137
+ if (aff.placeId !== undefined)
138
+ act.placeId = aff.placeId;
139
+ s.recent.push(act);
140
+ if (s.recent.length > CURIOSITY_DEFAULTS.maxRecent)
141
+ s.recent.shift();
142
+ const progress = Math.max(0, entry.slow - entry.fast);
143
+ const reward = Math.min(setting(s, 'rewardMax'), setting(s, 'rewardMax') * progress * before.drive);
144
+ satisfy(p, { leisure: reward / 2, competence: reward / 2 });
145
+ }
146
+ const unit = (x) => isNum(x) && x >= 0 && x <= 1;
147
+ const minute = (x, now) => isNum(x) && Math.abs(x) <= MAX_MINUTE && x <= now;
148
+ /** Conservative restore: drop malformed or ambiguous slices; skip malformed entries and bound lists. */
149
+ export function sanitizeCuriosity(input, now) {
150
+ if (!isObj(input) || !Array.isArray(input.errors) || !Array.isArray(input.recent))
151
+ return undefined;
152
+ if (input.spec !== undefined && !isObj(input.spec))
153
+ return undefined;
154
+ const out = { errors: [], recent: [] };
155
+ if (input.spec !== undefined) {
156
+ const spec = cleanSpec(input.spec);
157
+ if (Object.keys(spec).length !== Object.keys(input.spec).length)
158
+ return undefined;
159
+ if (Object.keys(spec).length)
160
+ out.spec = spec;
161
+ }
162
+ const seen = new Set();
163
+ for (const e of input.errors.slice(0, CURIOSITY_DEFAULTS.maxKeys)) {
164
+ if (!isObj(e) ||
165
+ !str(e.key) ||
166
+ !unit(e.fast) ||
167
+ !unit(e.slow) ||
168
+ !minute(e.lastAt, now) ||
169
+ !Number.isSafeInteger(e.n) ||
170
+ e.n < 1)
171
+ continue;
172
+ if (seen.has(e.key))
173
+ return undefined;
174
+ seen.add(e.key);
175
+ out.errors.push({ key: e.key, fast: e.fast, slow: e.slow, n: e.n, lastAt: e.lastAt });
176
+ }
177
+ for (const r of input.recent.slice(-CURIOSITY_DEFAULTS.maxRecent)) {
178
+ if (!isObj(r) || !str(r.action) || !minute(r.at, now))
179
+ continue;
180
+ if ((r.targetId !== undefined && !str(r.targetId)) || (r.placeId !== undefined && !str(r.placeId)))
181
+ continue;
182
+ const act = { action: r.action, at: r.at };
183
+ if (r.targetId !== undefined)
184
+ act.targetId = r.targetId;
185
+ if (r.placeId !== undefined)
186
+ act.placeId = r.placeId;
187
+ out.recent.push(act);
188
+ }
189
+ return out;
190
+ }
@@ -0,0 +1 @@
1
+ export * from './curiosity.ts';
@@ -0,0 +1 @@
1
+ export * from './curiosity.js';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Human Framework public surface. */
2
- export declare const FRAMEWORK_VERSION = "2.10.0";
2
+ export declare const FRAMEWORK_VERSION = "2.11.0";
3
3
  export type { CrisisEvent, PracticeKind } from './affect/index.ts';
4
4
  export { AFFECT_DEFAULTS, actionTendencies, advanceAffect, appraise, breakAllows, breakBehaviour, breakHazard, CRISIS_DEFAULTS, checkCrisis, createAffect, easeBreak, emotionLevel, enableBreaks, feel, inBreak, readAffect, regulate, release as releaseEmotion, skipCrisis, strain, tendencyEmotions, } from './affect/index.ts';
5
5
  export type { GoalTemplate, PrayerCalendar, PrayerSchedule, PrayerTimes, Retimer, } from './agenda/index.ts';
@@ -13,6 +13,7 @@ export { COGNITION_DEFAULTS, consider, decide as scoreAndResolve, desperationOf,
13
13
  export * from './conscience/index.ts';
14
14
  export * from './conversation/index.ts';
15
15
  export * from './core/index.ts';
16
+ export { CURIOSITY_DEFAULTS, curiosityOf, enableCuriosity } from './curiosity/index.ts';
16
17
  export * from './environment/index.ts';
17
18
  export * from './family/index.ts';
18
19
  export * from './habits/index.ts';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Human Framework public surface. */
2
- export const FRAMEWORK_VERSION = '2.10.0';
2
+ export const FRAMEWORK_VERSION = '2.11.0';
3
3
  export { AFFECT_DEFAULTS, actionTendencies, advanceAffect, appraise, breakAllows, breakBehaviour, breakHazard, CRISIS_DEFAULTS, checkCrisis, createAffect, easeBreak, emotionLevel, enableBreaks, feel, inBreak, readAffect, regulate, release as releaseEmotion, skipCrisis, strain, tendencyEmotions, } from './affect/index.js';
4
4
  // `agenda.release` collides with `affect.release`; the agenda one is re-exported under a clearer name.
5
5
  export { AGENDA_DEFAULTS, abandonGoal, adoptGoal, advanceAgenda, agendaTerms, applyExemptions, CARE_DEFAULTS, calendarRetimer, careDuty, commitmentPressure, createAgenda, DEFAULT_PRAYER_TIMES, EID_PRAYER_IMPORTANCE, EXEMPTION_DEFAULTS, eidPrayer, eidWindow, fastWindow, iftarWindow, inMakruhTime, isUnderWay, MAKRUH_DEFAULTS, makruhWindows, missedExcuse, onFinished, owedMakeUps, PRAYER_IMPORTANCE, PURPOSE_DEFAULTS, prayerWindows, pressureReachedAt, promise, proposeGoals, RAMADAN_DEFAULTS, ramadanFast, ramadanMeals, release as releaseCommitment, retimeCommitments, revisePurposes, scheduleMakeUp, spanMeetsWindow, suhoorWindow, timesFor, violatesAbstention, } from './agenda/index.js';
@@ -12,6 +12,7 @@ export { COGNITION_DEFAULTS, consider, decide as scoreAndResolve, desperationOf,
12
12
  export * from './conscience/index.js';
13
13
  export * from './conversation/index.js';
14
14
  export * from './core/index.js';
15
+ export { CURIOSITY_DEFAULTS, curiosityOf, enableCuriosity } from './curiosity/index.js';
15
16
  export * from './environment/index.js';
16
17
  export * from './family/index.js';
17
18
  export * from './habits/index.js';
package/dist/migrate.js CHANGED
@@ -18,7 +18,7 @@
18
18
  * version; fullness stays absent unless enabled, and alongside partners and reconsideration are opt-in.
19
19
  * 2.8.0 to 2.9.0 stamps the version; prompting cues and practice half-lives stay absent unless supplied,
20
20
  * review terms may carry refs, and an outcome naming several commitments now keeps each rather than only
21
- * the earliest-ending one. 2.9.0 to 2.10.0 stamps the version; belief provenance, noted contradictions and
21
+ * the earliest-ending one. 2.10.0 to 2.11.0 stamps the version (curiosity starts absent). 2.9.0 to 2.10.0 stamps the version; belief provenance, noted contradictions and
22
22
  * believed regard stay absent unless a host supplies them;
23
23
  * `regard` becomes a reserved impression cue key. A migrated save continues under the current engine's rules: it restores and runs,
24
24
  * but it does not reproduce what the old engine would have done next where the rules changed (1.4.0 to 1.5.0
@@ -70,6 +70,11 @@ export const MIGRATIONS = {
70
70
  note: 'the surroundings field `ambient.now` is renamed `ambient.percept`; per-person retention is optional and starts absent; behaviour unchanged',
71
71
  apply: renameAmbient,
72
72
  },
73
+ '2.10.0': {
74
+ to: '2.11.0',
75
+ note: 'the curiosity slice (Person.curiosity) is optional and starts absent; existing hosts retain exact behavior',
76
+ apply: stamp,
77
+ },
73
78
  '2.9.0': {
74
79
  to: '2.10.0',
75
80
  note: 'belief provenance (Belief.provenance), noted contradictions (memory.contradictions) and the believed-regard impression cue are optional and start absent; existing hosts retain exact behavior',
package/dist/person.js CHANGED
@@ -28,6 +28,7 @@ import { closeDay, enableYearbook, endDay, noteAnswer, noteCommitments, noteDeci
28
28
  import { decide as cognitionDecide, desperationOf, scoreAll } from './cognition/index.js';
29
29
  import { createConscience, guiltLoad, heldNorms, normVeto, recordDeed, recordRepair, repent, } from './conscience/index.js';
30
30
  import { clamp01, clampSigned, createRng, dayOf, isObj, minuteOfDay } from './core/index.js';
31
+ import { enableCuriosity, noteCuriosity } from './curiosity/index.js';
31
32
  import { ambientBodyParams, ambientModifiers, ambientMood, ambientNeeds } from './environment/index.js';
32
33
  import { aptitudeOf, createFamily, pregnancyModifiers } from './family/index.js';
33
34
  import { advanceHabits, reinforce, withholdCued } from './habits/index.js';
@@ -159,6 +160,8 @@ export function createPerson(spec) {
159
160
  enableFatigueStore(p);
160
161
  if (on?.fullness)
161
162
  enableFullness(p);
163
+ if (on?.curiosity)
164
+ enableCuriosity(p, on.curiosity === true ? {} : on.curiosity);
162
165
  if (on?.promptings)
163
166
  enablePromptings(p, on.promptings);
164
167
  if (on?.review)
@@ -1360,6 +1363,8 @@ export function finish(p, outcome, opts = {}) {
1360
1363
  dischargeAdvice(p, aff.action);
1361
1364
  }
1362
1365
  // Memory.
1366
+ if (p.curiosity)
1367
+ noteCuriosity(p, aff, outcome, felt);
1363
1368
  learnOutcome(p, aff, outcome, realized, felt);
1364
1369
  const episode = {
1365
1370
  at: now,
package/dist/restore.d.ts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * SCOPE: saving and loading a person. `snapshot` copies a person as plain JSON; `restore` validates a saved person,
3
+ * upgrades a save from an earlier supported engine (`migrate`), fills defaults for missing or mistyped core fields,
4
+ * hands every optional slice to its owning module's sanitizer (the chronicle, its day accumulator and the lexicon
5
+ * need only a type check, done here), and holds every list to the bound the live code keeps.
6
+ * A save the engine wrote restores to exactly what was saved. It does not repair a save's meaning (a plausible but
7
+ * wrong value stays), merge two saves, or read any format but the person JSON.
8
+ */
1
9
  import type { Person } from './types.ts';
2
10
  /** A deep copy of the person as plain JSON (what a host saves). */
3
11
  export declare function snapshot(p: Person): Person;
package/dist/restore.js CHANGED
@@ -13,6 +13,7 @@ import { sanitizeBody, sanitizeInjuries } from './body/index.js';
13
13
  import { sanitizeCharacter } from './character/index.js';
14
14
  import { CHRONICLE_DEFAULTS, sanitizeYears } from './chronicle/index.js';
15
15
  import { isNum } from './core/index.js';
16
+ import { sanitizeCuriosity } from './curiosity/index.js';
16
17
  import { sanitizeAmbient } from './environment/index.js';
17
18
  import { sanitizeFamily } from './family/index.js';
18
19
  import { HABIT_DEFAULTS } from './habits/index.js';
@@ -85,6 +86,7 @@ const OPTIONAL_KEYS = new Set([
85
86
  'methods',
86
87
  'prospection',
87
88
  'promptings',
89
+ 'curiosity',
88
90
  'review',
89
91
  ]);
90
92
  /** Kind of a JSON value for the per-field type check in `restore`. */
@@ -200,6 +202,7 @@ export function restore(input) {
200
202
  keepValid(out, 'retention', cleanRetention);
201
203
  keepValid(out, 'methods', sanitizeMethods);
202
204
  keepValid(out, 'prospection', (x) => sanitizeProspection(x, out.now, out.id));
205
+ keepValid(out, 'curiosity', (x) => sanitizeCuriosity(x, out.now));
203
206
  keepValid(out, 'promptings', (x) => sanitizePromptings(x, out.now));
204
207
  keepValid(out, 'review', (x) => sanitizeReview(x, out.now));
205
208
  boundLists(out);
package/dist/types.d.ts CHANGED
@@ -885,7 +885,7 @@ export declare const PERSON_SCHEMA = "human/person@1";
885
885
  * and an activity that would cover a closing stretch is reviewed when the stretch begins (`isUnderWay`,
886
886
  * `dutyReviewAt`). Person shape unchanged.
887
887
  */
888
- export declare const ENGINE_VERSION = "2.10.0";
888
+ export declare const ENGINE_VERSION = "2.11.0";
889
889
  /** A pregnancy carried by this person (host-driven; see `family/`). */
890
890
  export interface Pregnancy {
891
891
  fatherId: PersonId;
@@ -1052,6 +1052,8 @@ export interface Person {
1052
1052
  prospection?: ProspectionState;
1053
1053
  /** Noticed promptings and practised turnings-away (2.4.0, opt-in via `enablePromptings`). Owned by `promptings/`. */
1054
1054
  promptings?: PromptingsState;
1055
+ /** Intrinsic exploration and varied repetition. Owned by `curiosity/`; absent unless enabled. */
1056
+ curiosity?: CuriosityState;
1055
1057
  /** Evening self-review and its resolves (2.7.0, opt-in via `enableReview`). Owned by `review/`. */
1056
1058
  review?: ReviewState;
1057
1059
  /** Practice consolidation (1.8.0, opt-in via `enableSkillRetention`). Owned by `skills/`. */
@@ -1496,6 +1498,8 @@ export interface PersonSpec {
1496
1498
  fullness?: boolean;
1497
1499
  /** Promptings and turning away (2.4.0): the same as `enablePromptings(p, spec)` after creation. */
1498
1500
  promptings?: PromptingsSpec;
1501
+ /** Enable intrinsic exploration and play, using engineering defaults or bounded overrides. */
1502
+ curiosity?: boolean | CuriositySpec;
1499
1503
  /** Evening self-review (2.7.0): the same as `enableReview(p, spec)` after creation. */
1500
1504
  review?: ReviewSpec;
1501
1505
  };
@@ -1890,3 +1894,39 @@ export interface DayRecord {
1890
1894
  */
1891
1895
  state?: ChronicleState;
1892
1896
  }
1897
+ /** Engineering overrides for intrinsic motivation; scales are capped at their defaults. */
1898
+ export interface CuriositySpec {
1899
+ unknownScale?: number;
1900
+ progressScale?: number;
1901
+ varyScale?: number;
1902
+ recentMinutes?: number;
1903
+ rewardMax?: number;
1904
+ }
1905
+ /** Prediction-error history for the same key used by memory expectations. */
1906
+ export interface CuriosityError {
1907
+ key: string;
1908
+ fast: Unit;
1909
+ slow: Unit;
1910
+ n: number;
1911
+ lastAt: Minute;
1912
+ }
1913
+ /** A completed or failed act, retained only for bounded repetition with variation. */
1914
+ export interface CuriosityAct {
1915
+ action: string;
1916
+ targetId?: EntityId;
1917
+ placeId?: EntityId;
1918
+ at: Minute;
1919
+ }
1920
+ /** Optional JSON state owned by curiosity/. */
1921
+ export interface CuriosityState {
1922
+ spec?: CuriositySpec;
1923
+ errors: CuriosityError[];
1924
+ recent: CuriosityAct[];
1925
+ }
1926
+ /** Raw bounded signals and their common drive; no RNG or mutation. */
1927
+ export interface CuriosityReading {
1928
+ unknown: Unit;
1929
+ progress: Unit;
1930
+ vary: Unit;
1931
+ drive: number;
1932
+ }
package/dist/types.js CHANGED
@@ -120,4 +120,4 @@ export const PERSON_SCHEMA = 'human/person@1';
120
120
  * and an activity that would cover a closing stretch is reviewed when the stretch begins (`isUnderWay`,
121
121
  * `dutyReviewAt`). Person shape unchanged.
122
122
  */
123
- export const ENGINE_VERSION = '2.10.0';
123
+ export const ENGINE_VERSION = '2.11.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adam0white/human-framework",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "Deterministic, explainable simulated people: body, needs, affect, memory, beliefs, skills, habits, relationships, values and a will that can refuse a suggestion",
5
5
  "keywords": [
6
6
  "simulation",