@adam0white/human-framework 2.9.1 → 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.
- package/dist/beliefs/beliefs.d.ts +23 -1
- package/dist/beliefs/beliefs.js +158 -5
- package/dist/cognition/cognition.js +4 -0
- package/dist/conversation/conversation.d.ts +4 -0
- package/dist/conversation/conversation.js +25 -0
- package/dist/curiosity/curiosity.d.ts +28 -0
- package/dist/curiosity/curiosity.js +190 -0
- package/dist/curiosity/index.d.ts +1 -0
- package/dist/curiosity/index.js +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/memory/memory.js +4 -1
- package/dist/migrate.js +13 -1
- package/dist/person.js +34 -2
- package/dist/restore.d.ts +8 -0
- package/dist/restore.js +5 -0
- package/dist/social/impressions.d.ts +31 -2
- package/dist/social/impressions.js +38 -1
- package/dist/social/social.d.ts +2 -0
- package/dist/social/social.js +18 -2
- package/dist/types.d.ts +95 -6
- package/dist/types.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Belief, EntityId, Minute, Percept, Person, Unit } from '../types.ts';
|
|
1
|
+
import type { Belief, BeliefProvenance, EntityId, Minute, Percept, Person, Unit } from '../types.ts';
|
|
2
2
|
export declare const BELIEF_DEFAULTS: {
|
|
3
3
|
maxBeliefs: number;
|
|
4
4
|
maxSources: number;
|
|
@@ -17,6 +17,9 @@ export declare const BELIEF_DEFAULTS: {
|
|
|
17
17
|
/** Trust change on confirmation: gain toward 1, loss toward 0 (loss > gain). */
|
|
18
18
|
trustGain: number;
|
|
19
19
|
trustLoss: number;
|
|
20
|
+
/** Loss of source trust after attending incompatible accounts from that same source. */
|
|
21
|
+
contradictionLoss: number;
|
|
22
|
+
maxContradictions: number;
|
|
20
23
|
/** Half-life of log-odds drift toward 0. */
|
|
21
24
|
driftHalfLife: number;
|
|
22
25
|
attentionBase: number;
|
|
@@ -89,3 +92,22 @@ export declare function contested(p: Person, prop: string): {
|
|
|
89
92
|
against: EntityId[];
|
|
90
93
|
contested: boolean;
|
|
91
94
|
};
|
|
95
|
+
/** Where this person first got the belief on `prop`, or undefined. */
|
|
96
|
+
export declare function provenanceOf(p: Person, prop: string): BeliefProvenance | undefined;
|
|
97
|
+
export interface ContradictionResult {
|
|
98
|
+
applied: boolean;
|
|
99
|
+
reason?: 'no-account' | 'same-account' | 'already-noted' | 'self' | 'untrusted-reporter';
|
|
100
|
+
before: Unit;
|
|
101
|
+
after: Unit;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The person learns that `sourceId` also gave the account `prop = value` elsewhere, against what that source told
|
|
105
|
+
* them. See the contradiction SCOPE.
|
|
106
|
+
*/
|
|
107
|
+
export declare function contradicted(p: Person, ev: {
|
|
108
|
+
sourceId: EntityId;
|
|
109
|
+
prop: string;
|
|
110
|
+
value: boolean;
|
|
111
|
+
reporterId?: EntityId;
|
|
112
|
+
at?: Minute;
|
|
113
|
+
}): ContradictionResult;
|
package/dist/beliefs/beliefs.js
CHANGED
|
@@ -12,11 +12,20 @@
|
|
|
12
12
|
* Three independent stores: `social.relationships[].trust` moves through authored ties, social events
|
|
13
13
|
* (including forgiveness), judged acts and reputation; `will.voices[].trust` moves through followed-advice
|
|
14
14
|
* outcomes, insisting, commands, `noteVoicePromise`, `seedVoice` and `adoptVoiceTrust` (also used by upbringing).
|
|
15
|
-
* This module owns `memory.sourceTrust`: `seedSourceTrust` authors it
|
|
15
|
+
* This module owns `memory.sourceTrust`: `seedSourceTrust` authors it, `confirm` recalibrates it and
|
|
16
|
+
* `contradicted` reduces it when one source gives incompatible accounts.
|
|
16
17
|
* `believe`, including testimony delivered by `perceive`, weights claims by source trust, read by `trustOf`.
|
|
18
|
+
*
|
|
19
|
+
* SCOPE (Word and witness): optional provenance records the first known origin, direct teller and opaque
|
|
20
|
+
* host lineage of a belief. It does not change credence or decisions, reconstruct a relay graph, verify an
|
|
21
|
+
* origin, or survive belief eviction. Contradiction reduces information-source trust only when a retained
|
|
22
|
+
* first-hand account from that source opposes the newly learned account. Independent sources disagreeing
|
|
23
|
+
* are not a contradiction here. A third-party report is weighted by the reporter's source trust; this does
|
|
24
|
+
* not prove deceit, change the belief, or change relationship or voice trust. Loss and retention limits are
|
|
25
|
+
* engineering defaults, not calibrated effect sizes. Deduplication lasts only while its bounded record stays.
|
|
17
26
|
*/
|
|
18
|
-
import { clamp, clamp01, decay, dpow, expit, logit } from '../core/index.js';
|
|
19
|
-
import { MINUTES_PER_DAY } from '../types.js';
|
|
27
|
+
import { clamp, clamp01, decay, dpow, expit, isNum, isObj, logit } from '../core/index.js';
|
|
28
|
+
import { MAX_MINUTE, MINUTES_PER_DAY } from '../types.js';
|
|
20
29
|
export const BELIEF_DEFAULTS = {
|
|
21
30
|
maxBeliefs: 300,
|
|
22
31
|
maxSources: 6,
|
|
@@ -35,6 +44,9 @@ export const BELIEF_DEFAULTS = {
|
|
|
35
44
|
/** Trust change on confirmation: gain toward 1, loss toward 0 (loss > gain). */
|
|
36
45
|
trustGain: 0.1,
|
|
37
46
|
trustLoss: 0.25,
|
|
47
|
+
/** Loss of source trust after attending incompatible accounts from that same source. */
|
|
48
|
+
contradictionLoss: 0.25,
|
|
49
|
+
maxContradictions: 32,
|
|
38
50
|
/** Half-life of log-odds drift toward 0. */
|
|
39
51
|
driftHalfLife: 45 * MINUTES_PER_DAY,
|
|
40
52
|
// Attention
|
|
@@ -107,7 +119,9 @@ export function attend(p, percepts, ctx) {
|
|
|
107
119
|
export function trustOf(p, sourceId) {
|
|
108
120
|
if (sourceId === p.id || sourceId === 'self')
|
|
109
121
|
return BELIEF_DEFAULTS.selfTrust;
|
|
110
|
-
|
|
122
|
+
// Own entries only: an id such as `constructor` or `__proto__` must not read an inherited property.
|
|
123
|
+
const own = Object.hasOwn(p.memory.sourceTrust, sourceId) ? p.memory.sourceTrust[sourceId] : undefined;
|
|
124
|
+
return typeof own === 'number' ? own : BELIEF_DEFAULTS.defaultTrust;
|
|
111
125
|
}
|
|
112
126
|
/**
|
|
113
127
|
* Set authored trust in an information source, clamped to 0..1. Writes only `memory.sourceTrust`;
|
|
@@ -233,7 +247,13 @@ export function confirm(p, prop, truth, now) {
|
|
|
233
247
|
if (s.id === p.id || s.id === 'self')
|
|
234
248
|
continue;
|
|
235
249
|
const t = trustOf(p, s.id);
|
|
236
|
-
|
|
250
|
+
// An own data property even for ids such as `__proto__` (plain assignment would set the prototype).
|
|
251
|
+
Object.defineProperty(p.memory.sourceTrust, s.id, {
|
|
252
|
+
value: clamp01(s.value === truth ? t + d.trustGain * (1 - t) : t - d.trustLoss * t),
|
|
253
|
+
writable: true,
|
|
254
|
+
enumerable: true,
|
|
255
|
+
configurable: true,
|
|
256
|
+
});
|
|
237
257
|
if (s.value !== truth)
|
|
238
258
|
misled.push(s.id);
|
|
239
259
|
}
|
|
@@ -272,3 +292,136 @@ export function contested(p, prop) {
|
|
|
272
292
|
}
|
|
273
293
|
// Remembered advice (N9) is owned by `will/` (`standingAdvice`, fed from `Percept.advice`); review 2026-10-03
|
|
274
294
|
// removed a second, unused belief-based implementation here so the two could not drift apart.
|
|
295
|
+
// ---------------------------------------------------------------------------------------------
|
|
296
|
+
// Word and witness (2.10.0): provenance and contradiction.
|
|
297
|
+
// ---------------------------------------------------------------------------------------------
|
|
298
|
+
const validId = (x) => typeof x === 'string' && x.length > 0;
|
|
299
|
+
const validMinute = (x) => isNum(x) && Math.abs(x) <= MAX_MINUTE;
|
|
300
|
+
function cleanProvenance(x) {
|
|
301
|
+
if (!isObj(x) || !validId(x.origin) || !validId(x.firstFrom) || !validMinute(x.at))
|
|
302
|
+
return undefined;
|
|
303
|
+
if (x.lineage !== undefined && typeof x.lineage !== 'string')
|
|
304
|
+
return undefined;
|
|
305
|
+
const out = { origin: x.origin, firstFrom: x.firstFrom, at: x.at };
|
|
306
|
+
if (typeof x.lineage === 'string' && x.lineage.length <= 64)
|
|
307
|
+
out.lineage = x.lineage;
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
/** Record where a belief came from, if it has no provenance yet (first acquisition wins). @internal */
|
|
311
|
+
export function noteProvenance(p, claim, teller, at) {
|
|
312
|
+
if (claim.origin === undefined && claim.lineage === undefined)
|
|
313
|
+
return;
|
|
314
|
+
const b = findBelief(p, claim.prop);
|
|
315
|
+
if (!b || b.provenance !== undefined)
|
|
316
|
+
return;
|
|
317
|
+
const input = { origin: claim.origin ?? teller, firstFrom: teller, at };
|
|
318
|
+
if (claim.lineage !== undefined)
|
|
319
|
+
input.lineage = claim.lineage;
|
|
320
|
+
const meta = cleanProvenance(input);
|
|
321
|
+
if (meta !== undefined)
|
|
322
|
+
b.provenance = meta;
|
|
323
|
+
}
|
|
324
|
+
/** Where this person first got the belief on `prop`, or undefined. */
|
|
325
|
+
export function provenanceOf(p, prop) {
|
|
326
|
+
return findBelief(p, prop)?.provenance;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The person learns that `sourceId` also gave the account `prop = value` elsewhere, against what that source told
|
|
330
|
+
* them. See the contradiction SCOPE.
|
|
331
|
+
*/
|
|
332
|
+
export function contradicted(p, ev) {
|
|
333
|
+
return applyContradiction(p, ev, ev.reporterId === undefined ? undefined : trustOf(p, ev.reporterId));
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* `contradicted` with the reporter's trust given, so that several reports delivered together share one trust basis
|
|
337
|
+
* (`perceive` reads it once per percept, before any report applies). @internal
|
|
338
|
+
*/
|
|
339
|
+
export function applyContradiction(p, ev, reporterTrust) {
|
|
340
|
+
const t = trustOf(p, ev.sourceId);
|
|
341
|
+
const refused = (reason) => ({
|
|
342
|
+
applied: false,
|
|
343
|
+
reason,
|
|
344
|
+
before: t,
|
|
345
|
+
after: t,
|
|
346
|
+
});
|
|
347
|
+
if (ev.sourceId === p.id || ev.sourceId === 'self')
|
|
348
|
+
return refused('self');
|
|
349
|
+
const weight = reporterTrust === undefined ? 1 : testimonyWeight(reporterTrust);
|
|
350
|
+
// One loss per (source, proposition), at the strongest evidence: a weaker earlier report leaves room for the rest.
|
|
351
|
+
const prior = p.memory.contradictions?.find((x) => x.sourceId === ev.sourceId && x.prop === ev.prop);
|
|
352
|
+
const priorWeight = prior === undefined ? 0 : (prior.weight ?? 1);
|
|
353
|
+
if (prior !== undefined && priorWeight >= weight)
|
|
354
|
+
return refused('already-noted');
|
|
355
|
+
const account = findBelief(p, ev.prop)?.sources.find((s) => s.id === ev.sourceId);
|
|
356
|
+
if (!account)
|
|
357
|
+
return refused('no-account');
|
|
358
|
+
if (account.value === ev.value)
|
|
359
|
+
return refused('same-account');
|
|
360
|
+
// A reporter at or below the trust floor carries no weight and must not use up the pair.
|
|
361
|
+
if (weight <= 0)
|
|
362
|
+
return refused('untrusted-reporter');
|
|
363
|
+
const at = ev.at ?? p.now;
|
|
364
|
+
if (!validMinute(at))
|
|
365
|
+
throw new Error('human: contradiction time must be a finite supported minute');
|
|
366
|
+
const loss = BELIEF_DEFAULTS.contradictionLoss;
|
|
367
|
+
// Multiplicative, so the total over a pair is `1 - loss * strongest weight` whatever the arrival order.
|
|
368
|
+
const after = seedSourceTrust(p, ev.sourceId, (t * (1 - loss * weight)) / (1 - loss * priorWeight));
|
|
369
|
+
if (prior !== undefined) {
|
|
370
|
+
if (weight >= 1)
|
|
371
|
+
delete prior.weight;
|
|
372
|
+
else
|
|
373
|
+
prior.weight = weight;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
const noted = p.memory.contradictions ?? [];
|
|
377
|
+
const row = { sourceId: ev.sourceId, prop: ev.prop, at };
|
|
378
|
+
if (weight < 1)
|
|
379
|
+
row.weight = weight;
|
|
380
|
+
noted.push(row);
|
|
381
|
+
noted.sort((a, b) => a.at - b.at);
|
|
382
|
+
p.memory.contradictions = noted.slice(-BELIEF_DEFAULTS.maxContradictions);
|
|
383
|
+
}
|
|
384
|
+
return { applied: true, before: t, after };
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Restore (2.10.0): drop malformed provenance (lineage over 64 characters is dropped, not truncated) and bound and
|
|
388
|
+
* validate `contradictions`. A save the engine wrote is unchanged. @internal
|
|
389
|
+
*/
|
|
390
|
+
export function sanitizeBeliefMeta(memory) {
|
|
391
|
+
for (const b of memory.beliefs) {
|
|
392
|
+
if (!isObj(b) || b.provenance === undefined)
|
|
393
|
+
continue;
|
|
394
|
+
const meta = cleanProvenance(b.provenance);
|
|
395
|
+
if (meta === undefined)
|
|
396
|
+
delete b.provenance;
|
|
397
|
+
else
|
|
398
|
+
b.provenance = meta;
|
|
399
|
+
}
|
|
400
|
+
if (memory.contradictions === undefined)
|
|
401
|
+
return;
|
|
402
|
+
if (!Array.isArray(memory.contradictions)) {
|
|
403
|
+
delete memory.contradictions;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const clean = [];
|
|
407
|
+
const seen = new Map();
|
|
408
|
+
for (const x of memory.contradictions) {
|
|
409
|
+
if (!isObj(x) || typeof x.sourceId !== 'string' || typeof x.prop !== 'string' || !validMinute(x.at))
|
|
410
|
+
continue;
|
|
411
|
+
const props = seen.get(x.sourceId) ?? new Set();
|
|
412
|
+
if (props.has(x.prop))
|
|
413
|
+
continue;
|
|
414
|
+
props.add(x.prop);
|
|
415
|
+
seen.set(x.sourceId, props);
|
|
416
|
+
const row = { sourceId: x.sourceId, prop: x.prop, at: x.at };
|
|
417
|
+
// A malformed weight reads as 1 (fully applied): restore never reopens a pair to a further loss.
|
|
418
|
+
if (typeof x.weight === 'number' && Number.isFinite(x.weight) && x.weight > 0 && x.weight < 1)
|
|
419
|
+
row.weight = x.weight;
|
|
420
|
+
clean.push(row);
|
|
421
|
+
}
|
|
422
|
+
clean.sort((a, b) => a.at - b.at);
|
|
423
|
+
if (clean.length === 0)
|
|
424
|
+
delete memory.contradictions;
|
|
425
|
+
else
|
|
426
|
+
memory.contradictions = clean.slice(-BELIEF_DEFAULTS.maxContradictions);
|
|
427
|
+
}
|
|
@@ -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))
|
|
@@ -53,6 +53,8 @@ export interface ConverseContext {
|
|
|
53
53
|
magnitude?: Unit;
|
|
54
54
|
/** Character-trait vocabulary (defaults to `REPUTATION_TRAITS`). */
|
|
55
55
|
traits?: Record<string, TraitEffect>;
|
|
56
|
+
/** Carry belief origin and opaque lineage into told claims (off by default). */
|
|
57
|
+
provenance?: boolean;
|
|
56
58
|
}
|
|
57
59
|
/** Host-visible record of one claim. */
|
|
58
60
|
export interface ToldClaim {
|
|
@@ -69,6 +71,8 @@ export interface ToldClaim {
|
|
|
69
71
|
/** The person the claim is about, when it is a character claim. */
|
|
70
72
|
about?: PersonId;
|
|
71
73
|
score: number;
|
|
74
|
+
origin?: EntityId;
|
|
75
|
+
lineage?: string;
|
|
72
76
|
}
|
|
73
77
|
export interface AdviceGiven {
|
|
74
78
|
action: string;
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
* percept (it is in `claims`, not `told`). A claim this listener already heard from this speaker, in the same
|
|
30
30
|
* direction, is dropped before the speaker chooses, so it is not even a deed. The speaker keeps no memory of what was said to whom; the listener's
|
|
31
31
|
* belief stands in for it.
|
|
32
|
+
*
|
|
33
|
+
* SCOPE (Word and witness, 2.10.0): `provenance: true` carries each claim's first known origin and opaque
|
|
34
|
+
* lineage into testimony. Missing provenance names the speaker as origin. Metadata changes neither claim
|
|
35
|
+
* selection nor repetition rules; the host still owns the relay graph and disclosure. It does not establish
|
|
36
|
+
* that an origin is truthful, expose deceit to the listener, or infer who else heard a claim.
|
|
32
37
|
*/
|
|
33
38
|
import { clamp01, expit, round } from '../core/index.js';
|
|
34
39
|
import { isDeceased, isUnfavourableTrait, parseTraitProp, REPUTATION_TRAITS, relationshipWith, } from '../social/index.js';
|
|
@@ -191,6 +196,12 @@ export function converse(speaker, listener, ctx) {
|
|
|
191
196
|
.slice(0, Math.max(0, maxClaims));
|
|
192
197
|
const norms = new Map();
|
|
193
198
|
for (const c of chosen) {
|
|
199
|
+
if (ctx.provenance) {
|
|
200
|
+
const meta = speaker.memory.beliefs.find((b) => b.prop === c.prop)?.provenance;
|
|
201
|
+
c.origin = meta?.origin ?? speaker.id;
|
|
202
|
+
if (meta?.lineage !== undefined)
|
|
203
|
+
c.lineage = meta.lineage;
|
|
204
|
+
}
|
|
194
205
|
out.claims.push(c);
|
|
195
206
|
const pcNorms = [];
|
|
196
207
|
if (c.backbiting) {
|
|
@@ -220,6 +231,11 @@ export function converse(speaker, listener, ctx) {
|
|
|
220
231
|
? `${nameOf(speaker)} says ${c.about} is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`
|
|
221
232
|
: `${nameOf(speaker)} says ${c.prop} is ${c.value ? 'so' : 'not so'}`,
|
|
222
233
|
};
|
|
234
|
+
const claim = pc.claims?.[0];
|
|
235
|
+
if (claim && c.origin !== undefined)
|
|
236
|
+
claim.origin = c.origin;
|
|
237
|
+
if (claim && c.lineage !== undefined)
|
|
238
|
+
claim.lineage = c.lineage;
|
|
223
239
|
if (c.about !== undefined) {
|
|
224
240
|
pc.targetId = c.about;
|
|
225
241
|
const parsed = parseTraitProp(c.prop, table);
|
|
@@ -260,6 +276,15 @@ export function converse(speaker, listener, ctx) {
|
|
|
260
276
|
claims: [{ prop: `advice:${a.action}`, value: true, confidence: round(a.cred) }],
|
|
261
277
|
summary: `${nameOf(speaker)} says I should ${a.action}`,
|
|
262
278
|
};
|
|
279
|
+
if (ctx.provenance) {
|
|
280
|
+
const meta = beliefs.find((b) => b.prop === `${prefix}${a.action}`)?.provenance;
|
|
281
|
+
const claim = percept.claims?.[0];
|
|
282
|
+
if (claim) {
|
|
283
|
+
claim.origin = meta?.origin ?? speaker.id;
|
|
284
|
+
if (meta?.lineage !== undefined)
|
|
285
|
+
claim.lineage = meta.lineage;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
263
288
|
if (ctx.placeId !== undefined)
|
|
264
289
|
percept.placeId = ctx.placeId;
|
|
265
290
|
const suggestion = { voiceId: speaker.id, action: a.action, strength: round(a.strength) };
|
|
@@ -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.
|
|
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.
|
|
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/memory/memory.js
CHANGED
|
@@ -349,7 +349,10 @@ export function retell(teller, listener, when, knobs = {}) {
|
|
|
349
349
|
}
|
|
350
350
|
/** The same thing happening, whoever it happened to (kind, action, target, place). */
|
|
351
351
|
const sameStory = (g, e) => g.kind === e.kind && g.action === e.action && g.targetId === e.targetId && g.placeId === e.placeId;
|
|
352
|
-
const trustOfSource = (p, id) =>
|
|
352
|
+
const trustOfSource = (p, id) => {
|
|
353
|
+
const own = Object.hasOwn(p.memory.sourceTrust, id) ? p.memory.sourceTrust[id] : undefined;
|
|
354
|
+
return typeof own === 'number' ? own : 0.5;
|
|
355
|
+
};
|
|
353
356
|
const idNum = (id) => Number(id.slice(1)) || 0;
|
|
354
357
|
const byId = (a, b) => idNum(a.id) - idNum(b.id);
|
|
355
358
|
function recency(now, at) {
|
package/dist/migrate.js
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
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.
|
|
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
|
+
* believed regard stay absent unless a host supplies them;
|
|
23
|
+
* `regard` becomes a reserved impression cue key. A migrated save continues under the current engine's rules: it restores and runs,
|
|
22
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
|
|
23
25
|
* changed how standing advice is heard; 1.8.0 to 1.9.0 changed the omission rule). Saves older than 1.4.0 and unknown versions are refused with an error.
|
|
24
26
|
*
|
|
@@ -68,6 +70,16 @@ export const MIGRATIONS = {
|
|
|
68
70
|
note: 'the surroundings field `ambient.now` is renamed `ambient.percept`; per-person retention is optional and starts absent; behaviour unchanged',
|
|
69
71
|
apply: renameAmbient,
|
|
70
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
|
+
},
|
|
78
|
+
'2.9.0': {
|
|
79
|
+
to: '2.10.0',
|
|
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',
|
|
81
|
+
apply: stamp,
|
|
82
|
+
},
|
|
71
83
|
'2.8.0': {
|
|
72
84
|
to: '2.9.0',
|
|
73
85
|
note: 'prompting responses may carry a cue and the slice a practice half-life (both optional, absent as before); review terms may carry a ref; an outcome naming two or more commitments in fulfills now keeps each of them, where 2.8.0 kept the earliest-ending one',
|
package/dist/person.js
CHANGED
|
@@ -21,13 +21,14 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { actionTendencies, advanceAffect, appraise, CRISIS_DEFAULTS, checkCrisis, createAffect, easeBreak, emotionLevel, feel, readAffect, regulate, release, skipAffect, skipCrisis, strain, tendencyEmotions, } from './affect/index.js';
|
|
23
23
|
import { advanceAgenda, createAgenda, onFinished, promise, proposeGoals, revisePurposes, } from './agenda/index.js';
|
|
24
|
-
import { advanceBeliefs, attend, believe, confirm, credence } from './beliefs/index.js';
|
|
24
|
+
import { advanceBeliefs, applyContradiction, attend, believe, confirm, credence, noteProvenance, trustOf, } from './beliefs/index.js';
|
|
25
25
|
import { advanceBody, BODY_DEFAULTS, checkDowned, clearDowned, consume, createBody, downedAllows, enableFatigueStore, enableFullness, expose, injure, nextBodyThreshold, readBody, setDowned, sicken, skipBody, } from './body/index.js';
|
|
26
26
|
import { ageCharacter, enableCharacterChange, noteCharacterDay, noteCharacterSocial, } from './character/index.js';
|
|
27
27
|
import { closeDay, enableYearbook, endDay, noteAnswer, noteCommitments, noteDecision, noteMood, noteOutcome, trimChronicle, } from './chronicle/index.js';
|
|
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';
|
|
@@ -40,7 +41,7 @@ import { enablePromptings, nafsPatterns, notePromptings } from './promptings/ind
|
|
|
40
41
|
import { bodyForecastFor, consumeConsequences, forecastLine, forecastThresholds, pruneConsequences, receiveConsequences, } from './prospection/index.js';
|
|
41
42
|
import { enableReview, reviewIfDue } from './review/index.js';
|
|
42
43
|
import { enableSkillRetention, learnByWatching, practise, seedSkills, } from './skills/index.js';
|
|
43
|
-
import { advanceSocial, applyReputationBelief, careFor, closeness, judge, markDeceased, relationshipWith, seedRelationships, socialEvent, threatAppraisal, } from './social/index.js';
|
|
44
|
+
import { advanceSocial, applyReputationBelief, careFor, closeness, judge, markDeceased, noteKnownBy, relationshipWith, seedRelationships, socialEvent, threatAppraisal, } from './social/index.js';
|
|
44
45
|
import { ENGINE_VERSION, MAX_MINUTE, PERSON_SCHEMA, PHYSIOLOGICAL_NEEDS, PSYCHOLOGICAL_NEEDS, TRAIT_KEYS, VALUE_KEYS, } from './types.js';
|
|
45
46
|
import { advanceWill, answerSuggestion, chargeCommand, commandTargets, createWill, creditedVoices, dischargeAdvice, dutyReviewAt, endCommand, learnFromVoice, noteCommandOutcome, predictResponse, rememberAdvice, resolveCommand, takeCommand, voicesIn, WILL_DEFAULTS, wakeReviewAt, } from './will/index.js';
|
|
46
47
|
export const PERSON_DEFAULTS = {
|
|
@@ -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)
|
|
@@ -571,9 +574,36 @@ function perceiveOne(p, pc) {
|
|
|
571
574
|
confirm(p, claim.prop, claim.value, at);
|
|
572
575
|
else
|
|
573
576
|
believe(p, claim.prop, claim.value, claim.confidence, direct ? p.id : (actor ?? 'rumour'), at);
|
|
577
|
+
// Provenance (2.10.0): recorded only when the claim carries it; first acquisition wins.
|
|
578
|
+
if (claim.origin !== undefined || claim.lineage !== undefined)
|
|
579
|
+
noteProvenance(p, claim, direct ? p.id : (actor ?? 'rumour'), at);
|
|
574
580
|
// Hearsay about someone's character moves my relationship with them (reputation).
|
|
575
581
|
applyReputationBelief(p, claim.prop, before, credence(p, claim.prop), at);
|
|
576
582
|
}
|
|
583
|
+
// Another source's account, reported by the teller (2.10.0): one source telling two people opposite things.
|
|
584
|
+
if (pc.channel === 'told' && actor !== undefined && Array.isArray(pc.reports)) {
|
|
585
|
+
// One trust basis for the whole percept: a report that lowers the reporter's own trust must not reweigh the rest.
|
|
586
|
+
const reporterTrust = trustOf(p, actor);
|
|
587
|
+
for (const r of pc.reports.slice(0, 4))
|
|
588
|
+
applyContradiction(p, { ...r, reporterId: actor, at }, reporterTrust);
|
|
589
|
+
}
|
|
590
|
+
// My own act, seen by others (2.10.0): what I believe they now think of me.
|
|
591
|
+
if (byMe && Array.isArray(pc.witnesses)) {
|
|
592
|
+
for (const w of pc.witnesses.slice(0, 8)) {
|
|
593
|
+
if (typeof w !== 'string')
|
|
594
|
+
continue;
|
|
595
|
+
if (w === p.id)
|
|
596
|
+
continue;
|
|
597
|
+
const act = { kind: pc.kind };
|
|
598
|
+
if (target !== undefined)
|
|
599
|
+
act.targetId = target;
|
|
600
|
+
if (pc.valence !== undefined)
|
|
601
|
+
act.valence = pc.valence;
|
|
602
|
+
if (pc.norms)
|
|
603
|
+
act.norms = pc.norms;
|
|
604
|
+
noteKnownBy(p, { otherId: w, act, at, via: 'seen' });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
577
607
|
// Advice carried by testimony keeps pulling after the speaker falls silent (standing advice, N9).
|
|
578
608
|
if (pc.channel === 'told')
|
|
579
609
|
rememberAdvice(p, pc);
|
|
@@ -1333,6 +1363,8 @@ export function finish(p, outcome, opts = {}) {
|
|
|
1333
1363
|
dischargeAdvice(p, aff.action);
|
|
1334
1364
|
}
|
|
1335
1365
|
// Memory.
|
|
1366
|
+
if (p.curiosity)
|
|
1367
|
+
noteCuriosity(p, aff, outcome, felt);
|
|
1336
1368
|
learnOutcome(p, aff, outcome, realized, felt);
|
|
1337
1369
|
const episode = {
|
|
1338
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
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { sanitizeCrisis } from './affect/index.js';
|
|
10
10
|
import { sanitizeAgenda } from './agenda/index.js';
|
|
11
|
+
import { sanitizeBeliefMeta } from './beliefs/index.js';
|
|
11
12
|
import { sanitizeBody, sanitizeInjuries } from './body/index.js';
|
|
12
13
|
import { sanitizeCharacter } from './character/index.js';
|
|
13
14
|
import { CHRONICLE_DEFAULTS, sanitizeYears } from './chronicle/index.js';
|
|
14
15
|
import { isNum } from './core/index.js';
|
|
16
|
+
import { sanitizeCuriosity } from './curiosity/index.js';
|
|
15
17
|
import { sanitizeAmbient } from './environment/index.js';
|
|
16
18
|
import { sanitizeFamily } from './family/index.js';
|
|
17
19
|
import { HABIT_DEFAULTS } from './habits/index.js';
|
|
@@ -84,6 +86,7 @@ const OPTIONAL_KEYS = new Set([
|
|
|
84
86
|
'methods',
|
|
85
87
|
'prospection',
|
|
86
88
|
'promptings',
|
|
89
|
+
'curiosity',
|
|
87
90
|
'review',
|
|
88
91
|
]);
|
|
89
92
|
/** Kind of a JSON value for the per-field type check in `restore`. */
|
|
@@ -188,6 +191,7 @@ export function restore(input) {
|
|
|
188
191
|
sanitizeCrisis(out.affect);
|
|
189
192
|
sanitizeAgenda(out.agenda);
|
|
190
193
|
sanitizeGists(out.memory);
|
|
194
|
+
sanitizeBeliefMeta(out.memory);
|
|
191
195
|
// Optional top-level slices (1.8.0 onward), each checked by its owner.
|
|
192
196
|
keepValid(out, 'chronicleYears', sanitizeYears);
|
|
193
197
|
keepValid(out, 'character', sanitizeCharacter);
|
|
@@ -198,6 +202,7 @@ export function restore(input) {
|
|
|
198
202
|
keepValid(out, 'retention', cleanRetention);
|
|
199
203
|
keepValid(out, 'methods', sanitizeMethods);
|
|
200
204
|
keepValid(out, 'prospection', (x) => sanitizeProspection(x, out.now, out.id));
|
|
205
|
+
keepValid(out, 'curiosity', (x) => sanitizeCuriosity(x, out.now));
|
|
201
206
|
keepValid(out, 'promptings', (x) => sanitizePromptings(x, out.now));
|
|
202
207
|
keepValid(out, 'review', (x) => sanitizeReview(x, out.now));
|
|
203
208
|
boundLists(out);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { EntityId, Minute, Person, PersonId, Signed, StateCue, Traits, Unit } from '../types.ts';
|
|
2
|
-
export type CueKind = 'state' | 'place' | 'trait' | 'tie' | 'trust' | 'skill';
|
|
1
|
+
import type { EntityId, Minute, NormTag, Person, PersonId, Signed, StateCue, Traits, Unit } from '../types.ts';
|
|
2
|
+
export type CueKind = 'state' | 'place' | 'trait' | 'tie' | 'trust' | 'skill' | 'regard';
|
|
3
3
|
export declare const STATE_CUES: readonly StateCue[];
|
|
4
4
|
/** @deprecated since 2.1: use `TRAIT_KEYS` (types.ts), the same list. Removed in 3.0. */
|
|
5
5
|
export declare const TRAIT_NAMES: readonly (keyof Traits)[];
|
|
@@ -21,6 +21,7 @@ export declare const IMPRESSION_DEFAULTS: {
|
|
|
21
21
|
tie: number;
|
|
22
22
|
trust: number;
|
|
23
23
|
skill: number;
|
|
24
|
+
regard: number;
|
|
24
25
|
};
|
|
25
26
|
/** Default reserve: base + emotionalityCoef × (1 − emotionality) for pain and fear; fatigue and mood a little. */
|
|
26
27
|
reserve: {
|
|
@@ -129,3 +130,31 @@ export declare function impressionOf(observer: Person, targetId: PersonId, now:
|
|
|
129
130
|
* and unhurt reads positive, frightened or hurt negative. Cognition weighs it on risky offers shared with them.
|
|
130
131
|
*/
|
|
131
132
|
export declare function companionSteadiness(p: Person, otherId: PersonId, now: Minute): Signed;
|
|
133
|
+
/**
|
|
134
|
+
* SCOPE (believed regard): the person's estimate of how another regards them, updated only by their own evidence
|
|
135
|
+
* that the other knows of an act. Seen witnesses carry full weight; a learned relay carries information-source
|
|
136
|
+
* trust. The person projects the act's valence and their own held-norm judgement, without reading the other's
|
|
137
|
+
* mind. Negative samples carry the social negativity bias. Evidence halves over 60 days. This does not model
|
|
138
|
+
* hidden witnesses, unknown relays, recursive beliefs or the other's actual feelings. Relationships and
|
|
139
|
+
* impressions remain separate stores. The projection weights and half-life are engineering defaults.
|
|
140
|
+
*/
|
|
141
|
+
/** An act of mine that someone else knows of (2.10.0). */
|
|
142
|
+
export interface KnownAct {
|
|
143
|
+
kind: string;
|
|
144
|
+
targetId?: PersonId;
|
|
145
|
+
valence?: Signed;
|
|
146
|
+
norms?: NormTag[];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The person has their own evidence that `otherId` knows of their act: they saw them see it (`via: 'seen'`) or were
|
|
150
|
+
* told they heard (`via: 'told'`, weighted by trust in `sourceId`). Moves the believed regard cue. See the SCOPE.
|
|
151
|
+
*/
|
|
152
|
+
export declare function noteKnownBy(p: Person, ev: {
|
|
153
|
+
otherId: PersonId;
|
|
154
|
+
act: KnownAct;
|
|
155
|
+
at?: Minute;
|
|
156
|
+
via: 'seen' | 'told';
|
|
157
|
+
sourceId?: EntityId;
|
|
158
|
+
}): Estimate | undefined;
|
|
159
|
+
/** How `p` believes `otherId` regards them; the neutral prior has zero confidence. Pure and RNG-free. */
|
|
160
|
+
export declare function regardOf(p: Person, otherId: PersonId, now: Minute): Estimate;
|
|
@@ -25,8 +25,10 @@
|
|
|
25
25
|
* of one's own state, deliberate deception beyond reserve, the target noticing being watched, or gossip about
|
|
26
26
|
* impressions (conversation carries reputation beliefs separately, `social.ts`).
|
|
27
27
|
*/
|
|
28
|
+
import { testimonyWeight, trustOf } from '../beliefs/beliefs.js';
|
|
28
29
|
import { clamp, clamp01, cmpStr, decay, latestPerId, newestN, round } from '../core/index.js';
|
|
29
30
|
import { MINUTES_PER_DAY, MINUTES_PER_YEAR, TRAIT_KEYS } from '../types.js';
|
|
31
|
+
import { judge, SOCIAL_DEFAULTS } from './social.js';
|
|
30
32
|
export const STATE_CUES = ['fatigue', 'pain', 'fear', 'mood'];
|
|
31
33
|
/** @deprecated since 2.1: use `TRAIT_KEYS` (types.ts), the same list. Removed in 3.0. */
|
|
32
34
|
export const TRAIT_NAMES = TRAIT_KEYS;
|
|
@@ -40,6 +42,7 @@ export const IMPRESSION_DEFAULTS = {
|
|
|
40
42
|
trust: 30 * MINUTES_PER_DAY,
|
|
41
43
|
/** `skill:<id>`: how good someone is at a craft changes over seasons, and what was seen of it lasts. */
|
|
42
44
|
skill: MINUTES_PER_YEAR,
|
|
45
|
+
regard: 60 * MINUTES_PER_DAY,
|
|
43
46
|
},
|
|
44
47
|
/** confidence = weight / (weight + priorWeight). */
|
|
45
48
|
priorWeight: 1.5,
|
|
@@ -56,6 +59,7 @@ export const IMPRESSION_DEFAULTS = {
|
|
|
56
59
|
tie: 0,
|
|
57
60
|
trust: 0.5,
|
|
58
61
|
skill: 0.3,
|
|
62
|
+
regard: 0,
|
|
59
63
|
},
|
|
60
64
|
/** Default reserve: base + emotionalityCoef × (1 − emotionality) for pain and fear; fatigue and mood a little. */
|
|
61
65
|
reserve: { base: 0.1, emotionalityCoef: 0.4, fatigue: 0.1, mood: 0.2 },
|
|
@@ -83,6 +87,8 @@ export const IMPRESSION_DEFAULTS = {
|
|
|
83
87
|
maxCues: 48,
|
|
84
88
|
};
|
|
85
89
|
function cueKind(key) {
|
|
90
|
+
if (key === 'regard')
|
|
91
|
+
return 'regard';
|
|
86
92
|
if (key.startsWith('trait:'))
|
|
87
93
|
return 'trait';
|
|
88
94
|
if (key.startsWith('tie:'))
|
|
@@ -103,7 +109,7 @@ function priorOf(key) {
|
|
|
103
109
|
return P[kind];
|
|
104
110
|
}
|
|
105
111
|
function rangeOf(key) {
|
|
106
|
-
return key === 'mood' || cueKind(key) === 'tie' ? [-1, 1] : [0, 1];
|
|
112
|
+
return key === 'mood' || key === 'regard' || cueKind(key) === 'tie' ? [-1, 1] : [0, 1];
|
|
107
113
|
}
|
|
108
114
|
/** FNV-1a over a string, as a 0..1 fraction: deterministic observation error with no RNG drawn. */
|
|
109
115
|
function hash01(s) {
|
|
@@ -379,3 +385,34 @@ export function sanitizeImpressions(social) {
|
|
|
379
385
|
else
|
|
380
386
|
social.impressions = kept;
|
|
381
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* The person has their own evidence that `otherId` knows of their act: they saw them see it (`via: 'seen'`) or were
|
|
390
|
+
* told they heard (`via: 'told'`, weighted by trust in `sourceId`). Moves the believed regard cue. See the SCOPE.
|
|
391
|
+
*/
|
|
392
|
+
export function noteKnownBy(p, ev) {
|
|
393
|
+
if (ev.otherId === p.id)
|
|
394
|
+
return undefined;
|
|
395
|
+
const weight = ev.via === 'seen' ? 1 : ev.sourceId === undefined ? 0 : testimonyWeight(trustOf(p, ev.sourceId));
|
|
396
|
+
if (!(weight > 0))
|
|
397
|
+
return undefined;
|
|
398
|
+
const at = ev.at ?? p.now;
|
|
399
|
+
const defaults = {
|
|
400
|
+
'promise-broken': -0.6,
|
|
401
|
+
'promise-kept': 0.3,
|
|
402
|
+
harm: -0.6,
|
|
403
|
+
help: 0.4,
|
|
404
|
+
apology: 0.2,
|
|
405
|
+
};
|
|
406
|
+
const valence = ev.act.valence ?? (Object.hasOwn(defaults, ev.act.kind) ? (defaults[ev.act.kind] ?? 0) : 0);
|
|
407
|
+
const judgement = judge(p, { actorId: p.id, norms: ev.act.norms ?? [] }, p.conscience.norms, at).praiseworthiness;
|
|
408
|
+
const value = clamp(valence * (ev.act.targetId === ev.otherId ? 1 : 0.5) + 0.3 * judgement, -1, 1);
|
|
409
|
+
if (!Number.isFinite(value) || !Number.isFinite(at))
|
|
410
|
+
return undefined;
|
|
411
|
+
const imp = impressionFor(p, ev.otherId, true, at);
|
|
412
|
+
sample(imp, 'regard', value, weight * (value < 0 ? SOCIAL_DEFAULTS.negativityBias : 1), at);
|
|
413
|
+
return regardOf(p, ev.otherId, at);
|
|
414
|
+
}
|
|
415
|
+
/** How `p` believes `otherId` regards them; the neutral prior has zero confidence. Pure and RNG-free. */
|
|
416
|
+
export function regardOf(p, otherId, now) {
|
|
417
|
+
return estimate(p, otherId, 'regard', now);
|
|
418
|
+
}
|
package/dist/social/social.d.ts
CHANGED
package/dist/social/social.js
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
* helps), reciprocity as a favour ledger (Gouldner 1960), the mere-exposure / familiarity growth with
|
|
7
7
|
* diminishing returns, HEXACO agreeableness as tolerance of provocation and honesty-humility as sensitivity
|
|
8
8
|
* to deceit (Ashton & Lee), Schwartz benevolence as care for close others. Observers judge only the outward
|
|
9
|
-
* act and never see intentions.
|
|
10
|
-
*
|
|
9
|
+
* act and never see intentions. An opt-in `regard` impression estimates the other's view of me and adds a bounded
|
|
10
|
+
* repair term; it never reads the other's actual view. It does NOT model attachment styles
|
|
11
|
+
* (gossip, reputation and bereavement are in the SCOPE paragraphs
|
|
11
12
|
* further down; insiders, outsiders and threats in `groups.ts`); the
|
|
12
13
|
* coefficients are engineering defaults, not calibrated estimates.
|
|
13
14
|
*/
|
|
14
15
|
import { clamp, clamp01, clampSigned, cmpStr, decay, dlog, expit, round } from '../core/index.js';
|
|
15
16
|
import { MINUTES_PER_DAY, MINUTES_PER_YEAR } from '../types.js';
|
|
16
17
|
import { careFor } from './groups.js';
|
|
18
|
+
import { regardOf } from './impressions.js';
|
|
17
19
|
const WEEK = 7 * MINUTES_PER_DAY;
|
|
18
20
|
export const SOCIAL_DEFAULTS = {
|
|
19
21
|
maxRelationships: 80,
|
|
@@ -50,6 +52,8 @@ export const SOCIAL_DEFAULTS = {
|
|
|
50
52
|
group: 0.3,
|
|
51
53
|
/** Group size (others) from which the extraversion term applies. */
|
|
52
54
|
groupFrom: 3,
|
|
55
|
+
regardRepair: 0.5,
|
|
56
|
+
regardRepairMax: 0.3,
|
|
53
57
|
},
|
|
54
58
|
judge: {
|
|
55
59
|
respect: 0.12,
|
|
@@ -328,6 +332,18 @@ export function socialTerms(p, aff, ctx) {
|
|
|
328
332
|
const size = clamp01((others - W.groupFrom + 1) / 6);
|
|
329
333
|
terms.push({ source: 'social:group', value: round(W.group * (p.traits.extraversion - 0.5) * 2 * size) });
|
|
330
334
|
}
|
|
335
|
+
if (aff.targetId !== undefined &&
|
|
336
|
+
aff.targetId !== p.id &&
|
|
337
|
+
(tags.includes('repair') || aff.action === 'apology' || aff.action === 'amends') &&
|
|
338
|
+
!isDeceased(p, aff.targetId)) {
|
|
339
|
+
const regard = regardOf(p, aff.targetId, p.now);
|
|
340
|
+
if (regard.value < 0 && regard.confidence > 0) {
|
|
341
|
+
terms.push({
|
|
342
|
+
source: `regard:${aff.targetId}`,
|
|
343
|
+
value: round(Math.min(W.regardRepairMax, W.regardRepair * -regard.value * regard.confidence * (0.5 + p.traits.agreeableness))),
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
331
347
|
return terms;
|
|
332
348
|
}
|
|
333
349
|
/** Moral weight of an observed act against one held standing, -1..1 before conviction. */
|
package/dist/types.d.ts
CHANGED
|
@@ -393,6 +393,49 @@ export interface Belief {
|
|
|
393
393
|
id: EntityId;
|
|
394
394
|
value: boolean;
|
|
395
395
|
}[];
|
|
396
|
+
/**
|
|
397
|
+
* Where this person first got the belief (2.10.0, `beliefs/`). Absent unless an incoming claim carried provenance
|
|
398
|
+
* (`PerceptClaim.origin` or `lineage`). First acquisition wins; never read by decisions.
|
|
399
|
+
*/
|
|
400
|
+
provenance?: BeliefProvenance;
|
|
401
|
+
}
|
|
402
|
+
/** Where a belief first came from, as far as the person knows (2.10.0). Owned by `beliefs/`. */
|
|
403
|
+
export interface BeliefProvenance {
|
|
404
|
+
/** Who first said it: the first teller of the chain as told, or this person when they saw it themselves. */
|
|
405
|
+
origin: EntityId;
|
|
406
|
+
/** Who this person first heard it from (equal to `origin` when told first-hand or observed). */
|
|
407
|
+
firstFrom: EntityId;
|
|
408
|
+
/** Minute the provenance was acquired. */
|
|
409
|
+
at: Minute;
|
|
410
|
+
/** Opaque host token (at most 64 characters), carried unchanged through relays; HF never parses it. */
|
|
411
|
+
lineage?: string;
|
|
412
|
+
}
|
|
413
|
+
/** One proposition claim carried by a percept. */
|
|
414
|
+
export interface PerceptClaim {
|
|
415
|
+
prop: string;
|
|
416
|
+
value: boolean;
|
|
417
|
+
confidence: Unit;
|
|
418
|
+
/** 2.10.0: who first said it, as the teller passes it on (provenance; never read by decisions). */
|
|
419
|
+
origin?: EntityId;
|
|
420
|
+
/** 2.10.0: opaque host lineage token (at most 64 characters), carried unchanged by `converse`. */
|
|
421
|
+
lineage?: string;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* A report that `sourceId` told the reporter (the percept's `actorId`) that `prop` is `value` (2.10.0). On an attended
|
|
425
|
+
* `told` percept it is checked against what `sourceId` told this person (`beliefs.contradicted`).
|
|
426
|
+
*/
|
|
427
|
+
export interface ReportedAccount {
|
|
428
|
+
sourceId: EntityId;
|
|
429
|
+
prop: string;
|
|
430
|
+
value: boolean;
|
|
431
|
+
}
|
|
432
|
+
/** A noted contradiction by one source on one proposition (2.10.0, `beliefs/`); kept so it counts once. */
|
|
433
|
+
export interface NotedContradiction {
|
|
434
|
+
sourceId: EntityId;
|
|
435
|
+
prop: string;
|
|
436
|
+
at: Minute;
|
|
437
|
+
/** Testimony weight already applied, 0..1; absent means 1 (heard both accounts directly). */
|
|
438
|
+
weight?: Unit;
|
|
396
439
|
}
|
|
397
440
|
/** Learned expectation of an action's actual result for this person (prediction-error learning). */
|
|
398
441
|
export interface ActionExpectation {
|
|
@@ -431,6 +474,8 @@ export interface MemoryState {
|
|
|
431
474
|
/** Lasting gists (1.8.0, opt-in via `enableGists`; absent = episodes are simply forgotten). Bounded. */
|
|
432
475
|
gists?: Gist[];
|
|
433
476
|
nextGist?: number;
|
|
477
|
+
/** Contradictions already noted (2.10.0, `beliefs.contradicted`). Absent until one is noted. Bounded. */
|
|
478
|
+
contradictions?: NotedContradiction[];
|
|
434
479
|
}
|
|
435
480
|
export interface Relationship {
|
|
436
481
|
otherId: PersonId;
|
|
@@ -840,7 +885,7 @@ export declare const PERSON_SCHEMA = "human/person@1";
|
|
|
840
885
|
* and an activity that would cover a closing stretch is reviewed when the stretch begins (`isUnderWay`,
|
|
841
886
|
* `dutyReviewAt`). Person shape unchanged.
|
|
842
887
|
*/
|
|
843
|
-
export declare const ENGINE_VERSION = "2.
|
|
888
|
+
export declare const ENGINE_VERSION = "2.11.0";
|
|
844
889
|
/** A pregnancy carried by this person (host-driven; see `family/`). */
|
|
845
890
|
export interface Pregnancy {
|
|
846
891
|
fatherId: PersonId;
|
|
@@ -1007,6 +1052,8 @@ export interface Person {
|
|
|
1007
1052
|
prospection?: ProspectionState;
|
|
1008
1053
|
/** Noticed promptings and practised turnings-away (2.4.0, opt-in via `enablePromptings`). Owned by `promptings/`. */
|
|
1009
1054
|
promptings?: PromptingsState;
|
|
1055
|
+
/** Intrinsic exploration and varied repetition. Owned by `curiosity/`; absent unless enabled. */
|
|
1056
|
+
curiosity?: CuriosityState;
|
|
1010
1057
|
/** Evening self-review and its resolves (2.7.0, opt-in via `enableReview`). Owned by `review/`. */
|
|
1011
1058
|
review?: ReviewState;
|
|
1012
1059
|
/** Practice consolidation (1.8.0, opt-in via `enableSkillRetention`). Owned by `skills/`. */
|
|
@@ -1157,11 +1204,7 @@ export interface Percept {
|
|
|
1157
1204
|
/** Base salience before attention, 0..1. */
|
|
1158
1205
|
salience: Unit;
|
|
1159
1206
|
/** Proposition claims carried by the percept (e.g. a rumour). */
|
|
1160
|
-
claims?:
|
|
1161
|
-
prop: string;
|
|
1162
|
-
value: boolean;
|
|
1163
|
-
confidence: Unit;
|
|
1164
|
-
}[];
|
|
1207
|
+
claims?: PerceptClaim[];
|
|
1165
1208
|
/** Norms the observed act honoured or violated (for judging others). */
|
|
1166
1209
|
norms?: NormTag[];
|
|
1167
1210
|
/**
|
|
@@ -1206,6 +1249,14 @@ export interface Percept {
|
|
|
1206
1249
|
id: string;
|
|
1207
1250
|
revision: number;
|
|
1208
1251
|
}[];
|
|
1252
|
+
/**
|
|
1253
|
+
* 2.10.0: on a percept of my own act (`actorId` is the perceiver), the people I saw see it. Each one's believed
|
|
1254
|
+
* regard of me moves (`social/impressions.ts`, `noteKnownBy`). Someone present but not listed changes nothing.
|
|
1255
|
+
* The first 8 are read.
|
|
1256
|
+
*/
|
|
1257
|
+
witnesses?: readonly PersonId[];
|
|
1258
|
+
/** 2.10.0: on a `told` percept, accounts the teller says another source gave them (at most four). */
|
|
1259
|
+
reports?: readonly ReportedAccount[];
|
|
1209
1260
|
}
|
|
1210
1261
|
/**
|
|
1211
1262
|
* Domains with distinct age curves for learning rate (`lifecourse.learningMultiplier`). 'general' is
|
|
@@ -1447,6 +1498,8 @@ export interface PersonSpec {
|
|
|
1447
1498
|
fullness?: boolean;
|
|
1448
1499
|
/** Promptings and turning away (2.4.0): the same as `enablePromptings(p, spec)` after creation. */
|
|
1449
1500
|
promptings?: PromptingsSpec;
|
|
1501
|
+
/** Enable intrinsic exploration and play, using engineering defaults or bounded overrides. */
|
|
1502
|
+
curiosity?: boolean | CuriositySpec;
|
|
1450
1503
|
/** Evening self-review (2.7.0): the same as `enableReview(p, spec)` after creation. */
|
|
1451
1504
|
review?: ReviewSpec;
|
|
1452
1505
|
};
|
|
@@ -1841,3 +1894,39 @@ export interface DayRecord {
|
|
|
1841
1894
|
*/
|
|
1842
1895
|
state?: ChronicleState;
|
|
1843
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.
|
|
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.
|
|
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",
|