@adam0white/human-framework 2.9.0 → 2.10.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/person.js CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * SCOPE: the composite. `createPerson` wires every faculty's constructor; `tick` advances all faculties in
3
3
  * closed form from `p.now` to a later minute under the current activity's body load (idle awake otherwise),
4
- * splitting at the activity's end; `perceive` runs attention, then beliefs, relationships, judgement of
5
- * others' acts, memory and appraisal; `decide` scores offers and resolves the will (`answerNow` answers a
6
- * suggestion immediately); `begin` and `finish` bracket an activity and route the outcome into body, needs,
7
- * skills, habits, memory, conscience, agenda, relationships and trust in advising voices. Direct control is
8
- * `command`, `releaseCommand` and `previewCommand`; `knockDown` and `standUp` move a person in and out of the
9
- * downed state; `observeSkill` is observational learning; `setRetention` sets the per-person retention
4
+ * splitting at the activity's end and the absolute hourly grid; `perceive` runs attention, then beliefs,
5
+ * relationships, judgement of others' acts, memory and appraisal; `decide` scores offers and resolves the
6
+ * will (`answerNow` answers a suggestion immediately); `begin` and `finish` bracket an activity and route the
7
+ * outcome into body, needs, skills, habits, memory, conscience, agenda, relationships and trust in advising
8
+ * voices. Direct control is `command`, `releaseCommand` and `previewCommand`; `knockDown` and `standUp`
9
+ * move a person in and out of the downed state; `observeSkill` is observational learning; `setRetention` sets the per-person retention
10
10
  * bounds; `readPerson` is the read-only summary. Snapshot and restore live in `restore.ts`. This file owns
11
11
  * `now`, `activity`, `trace`, `nextDecision` and `retention`; every other slice is written only through its
12
12
  * owning module. It sequences the modules and makes no behavioural claims of its own beyond the engineering
@@ -21,7 +21,7 @@
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';
@@ -40,7 +40,7 @@ import { enablePromptings, nafsPatterns, notePromptings } from './promptings/ind
40
40
  import { bodyForecastFor, consumeConsequences, forecastLine, forecastThresholds, pruneConsequences, receiveConsequences, } from './prospection/index.js';
41
41
  import { enableReview, reviewIfDue } from './review/index.js';
42
42
  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';
43
+ import { advanceSocial, applyReputationBelief, careFor, closeness, judge, markDeceased, noteKnownBy, relationshipWith, seedRelationships, socialEvent, threatAppraisal, } from './social/index.js';
44
44
  import { ENGINE_VERSION, MAX_MINUTE, PERSON_SCHEMA, PHYSIOLOGICAL_NEEDS, PSYCHOLOGICAL_NEEDS, TRAIT_KEYS, VALUE_KEYS, } from './types.js';
45
45
  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
46
  export const PERSON_DEFAULTS = {
@@ -571,9 +571,36 @@ function perceiveOne(p, pc) {
571
571
  confirm(p, claim.prop, claim.value, at);
572
572
  else
573
573
  believe(p, claim.prop, claim.value, claim.confidence, direct ? p.id : (actor ?? 'rumour'), at);
574
+ // Provenance (2.10.0): recorded only when the claim carries it; first acquisition wins.
575
+ if (claim.origin !== undefined || claim.lineage !== undefined)
576
+ noteProvenance(p, claim, direct ? p.id : (actor ?? 'rumour'), at);
574
577
  // Hearsay about someone's character moves my relationship with them (reputation).
575
578
  applyReputationBelief(p, claim.prop, before, credence(p, claim.prop), at);
576
579
  }
580
+ // Another source's account, reported by the teller (2.10.0): one source telling two people opposite things.
581
+ if (pc.channel === 'told' && actor !== undefined && Array.isArray(pc.reports)) {
582
+ // One trust basis for the whole percept: a report that lowers the reporter's own trust must not reweigh the rest.
583
+ const reporterTrust = trustOf(p, actor);
584
+ for (const r of pc.reports.slice(0, 4))
585
+ applyContradiction(p, { ...r, reporterId: actor, at }, reporterTrust);
586
+ }
587
+ // My own act, seen by others (2.10.0): what I believe they now think of me.
588
+ if (byMe && Array.isArray(pc.witnesses)) {
589
+ for (const w of pc.witnesses.slice(0, 8)) {
590
+ if (typeof w !== 'string')
591
+ continue;
592
+ if (w === p.id)
593
+ continue;
594
+ const act = { kind: pc.kind };
595
+ if (target !== undefined)
596
+ act.targetId = target;
597
+ if (pc.valence !== undefined)
598
+ act.valence = pc.valence;
599
+ if (pc.norms)
600
+ act.norms = pc.norms;
601
+ noteKnownBy(p, { otherId: w, act, at, via: 'seen' });
602
+ }
603
+ }
577
604
  // Advice carried by testimony keeps pulling after the speaker falls silent (standing advice, N9).
578
605
  if (pc.channel === 'told')
579
606
  rememberAdvice(p, pc);
@@ -1136,6 +1163,115 @@ export function learningFor(p, domain) {
1136
1163
  export function observeSkill(p, skill, minutes, modelLevel, domain) {
1137
1164
  return learnByWatching(p, skill, minutes, modelLevel, learningFor(p, domain), p.now);
1138
1165
  }
1166
+ /** Realized need changes, using readings captured before and after the outcome's deltas. */
1167
+ function realizedNeedDeltas(act, given, before, after) {
1168
+ const aff = act.affordance;
1169
+ const realized = {};
1170
+ for (const id of [...PHYSIOLOGICAL_NEEDS, ...PSYCHOLOGICAL_NEEDS]) {
1171
+ if (given[id] === undefined)
1172
+ continue;
1173
+ realized[id] =
1174
+ id === 'food' || id === 'water' || id === 'relief'
1175
+ ? levelOf(after, id) - levelOf(before, id)
1176
+ : given[id];
1177
+ }
1178
+ for (const id of ['sleep', 'rest']) {
1179
+ if (aff.advertises[id] !== undefined && given[id] === undefined) {
1180
+ realized[id] = levelOf(after, id) - (act.needsAtStart[id] ?? levelOf(before, id));
1181
+ }
1182
+ }
1183
+ return realized;
1184
+ }
1185
+ /** Record the deed and apply its appraisal, repentance, repair and regulation. */
1186
+ function respondToConscience(p, act, now, completed, minutes) {
1187
+ const aff = act.affordance;
1188
+ const deed = recordDeed(p, aff, act.intention, now, completed, act.necessity ? { necessity: true } : {});
1189
+ for (const ev of deed.appraisal)
1190
+ appraise(p, ev);
1191
+ const tags = aff.tags ?? [];
1192
+ if (completed && (tags.includes('worship') || tags.includes('repent'))) {
1193
+ // Worship closes breaches against God alone; guilt toward a wronged person waits for repair.
1194
+ const repented = new Set();
1195
+ for (const b of p.conscience.breaches) {
1196
+ if (!b.repaired && b.victimId === undefined && repent(p, b.id, now))
1197
+ repented.add(b.normId);
1198
+ }
1199
+ for (const b of p.conscience.breaches)
1200
+ if (!b.repaired && b.victimId !== undefined)
1201
+ repented.delete(b.normId);
1202
+ for (const normId of [...repented].sort())
1203
+ release(p, { id: 'guilt', causePrefix: `breach:${normId}` }, 0.6);
1204
+ regulate(p, minutes, 'worship');
1205
+ }
1206
+ if (completed && (tags.includes('repair') || tags.includes('apologize')) && aff.targetId !== undefined) {
1207
+ recordRepair(p, aff.targetId, now);
1208
+ release(p, { id: 'guilt', targetId: aff.targetId });
1209
+ socialEvent(p, { at: now, kind: 'apology', otherId: aff.targetId, byMe: true, magnitude: 0.7 });
1210
+ }
1211
+ if (act.mode === 'sleep' || tags.includes('rest') || tags.includes('reflection')) {
1212
+ regulate(p, minutes, tags.includes('reflection') ? 'reflection' : 'rest');
1213
+ }
1214
+ return { deed, tags };
1215
+ }
1216
+ /** Record relationships and character experience from a completed activity. */
1217
+ function recordParticipation(p, aff, tags, now, minutes) {
1218
+ const D = PERSON_DEFAULTS;
1219
+ if (p.character && (aff.with ?? []).some((o) => o !== p.id))
1220
+ noteCharacterSocial(p, minutes);
1221
+ const kind = tags.includes('work') ? 'shared-work' : 'chat';
1222
+ for (const other of aff.with ?? []) {
1223
+ if (other === p.id)
1224
+ continue;
1225
+ socialEvent(p, { at: now, kind, otherId: other, byMe: true, magnitude: D.participationMagnitude });
1226
+ }
1227
+ if (aff.targetId !== undefined && aff.targetId !== p.id) {
1228
+ const care = tags.includes('help') ? 'help' : tags.includes('gift') ? 'gift' : undefined;
1229
+ if (care)
1230
+ socialEvent(p, { at: now, kind: care, otherId: aff.targetId, byMe: true, magnitude: 0.7 });
1231
+ }
1232
+ }
1233
+ /** Learn from each advising voice after the activity's conscience and agenda results are known. */
1234
+ function learnActivityVoices(p, act, after, now, felt, broken, breached) {
1235
+ const aff = act.affordance;
1236
+ const D = PERSON_DEFAULTS;
1237
+ // Trust in every voice that pushed this activity: how it felt, minus bodily harm the host's deltas do not
1238
+ // show (needs that fell during the activity and are now urgent).
1239
+ const voices = act.suggestions ?? (act.suggestion ? [act.suggestion] : []);
1240
+ if (voices.length > 0) {
1241
+ let harm = 0;
1242
+ for (const id of ['food', 'water', 'sleep', 'rest']) {
1243
+ if (urgencyOf(after, id) < D.voiceHarmUrgency)
1244
+ continue;
1245
+ harm += Math.max(0, (act.needsAtStart[id] ?? levelOf(after, id)) - levelOf(after, id));
1246
+ }
1247
+ // Doing what a voice pushed broke a commitment or the person's own standards: that costs the voice as harm
1248
+ // does (review 2026-10-03: thirty broken fasts under insistence had moved trust only 0.50 -> 0.41). Omissions
1249
+ // the push caused later (a missed prayer during a pushed activity) are not attributed here.
1250
+ let breach = 0;
1251
+ for (const c of broken)
1252
+ breach += D.missedDistress * c.importance;
1253
+ breach += D.missedDistress * 0.5 * breached;
1254
+ for (const res of voices) {
1255
+ const protest = res.verdict === 'complied' || res.verdict === 'commanded';
1256
+ const reason = breach > 0
1257
+ ? protest
1258
+ ? 'breach-under-protest'
1259
+ : 'breach'
1260
+ : harm > 0
1261
+ ? protest
1262
+ ? 'harm-under-protest'
1263
+ : 'harm'
1264
+ : felt >= 0
1265
+ ? 'went-well'
1266
+ : 'went-badly';
1267
+ learnFromVoice(p, res, clampSigned(felt - D.voiceHarmGain * harm - breach), {
1268
+ at: now,
1269
+ action: aff.action,
1270
+ reason,
1271
+ });
1272
+ }
1273
+ }
1274
+ }
1139
1275
  /**
1140
1276
  * Apply what happened. Physiological deltas go to the body, psychological ones to the needs, then skills,
1141
1277
  * habits, memory (expectation learning and an episode), conscience (deed, breaches, repair), agenda,
@@ -1179,20 +1315,7 @@ export function finish(p, outcome, opts = {}) {
1179
1315
  // Realized deltas: what the body actually took in for food/water/relief (a drink at full hydration
1180
1316
  // realizes nothing), host values for psychological needs, and measured since the start for sleep/rest.
1181
1317
  const after = readNeeds(p, readBodyOf(p));
1182
- const realized = {};
1183
- for (const id of [...PHYSIOLOGICAL_NEEDS, ...PSYCHOLOGICAL_NEEDS]) {
1184
- if (given[id] === undefined)
1185
- continue;
1186
- realized[id] =
1187
- id === 'food' || id === 'water' || id === 'relief'
1188
- ? levelOf(after, id) - levelOf(before, id)
1189
- : given[id];
1190
- }
1191
- for (const id of ['sleep', 'rest']) {
1192
- if (aff.advertises[id] !== undefined && given[id] === undefined) {
1193
- realized[id] = levelOf(after, id) - (act.needsAtStart[id] ?? levelOf(before, id));
1194
- }
1195
- }
1318
+ const realized = realizedNeedDeltas(act, given, before, after);
1196
1319
  // Felt valence.
1197
1320
  let felt = 0;
1198
1321
  for (const id of Object.keys(realized)) {
@@ -1255,32 +1378,7 @@ export function finish(p, outcome, opts = {}) {
1255
1378
  episode.voiceId = act.suggestion.voiceId;
1256
1379
  remember(p, episode);
1257
1380
  // Conscience.
1258
- const deed = recordDeed(p, aff, act.intention, now, completed, act.necessity ? { necessity: true } : {});
1259
- for (const ev of deed.appraisal)
1260
- appraise(p, ev);
1261
- const tags = aff.tags ?? [];
1262
- if (completed && (tags.includes('worship') || tags.includes('repent'))) {
1263
- // Worship closes breaches against God alone; guilt toward a wronged person waits for repair.
1264
- const repented = new Set();
1265
- for (const b of p.conscience.breaches) {
1266
- if (!b.repaired && b.victimId === undefined && repent(p, b.id, now))
1267
- repented.add(b.normId);
1268
- }
1269
- for (const b of p.conscience.breaches)
1270
- if (!b.repaired && b.victimId !== undefined)
1271
- repented.delete(b.normId);
1272
- for (const normId of [...repented].sort())
1273
- release(p, { id: 'guilt', causePrefix: `breach:${normId}` }, 0.6);
1274
- regulate(p, minutes, 'worship');
1275
- }
1276
- if (completed && (tags.includes('repair') || tags.includes('apologize')) && aff.targetId !== undefined) {
1277
- recordRepair(p, aff.targetId, now);
1278
- release(p, { id: 'guilt', targetId: aff.targetId });
1279
- socialEvent(p, { at: now, kind: 'apology', otherId: aff.targetId, byMe: true, magnitude: 0.7 });
1280
- }
1281
- if (act.mode === 'sleep' || tags.includes('rest') || tags.includes('reflection')) {
1282
- regulate(p, minutes, tags.includes('reflection') ? 'reflection' : 'rest');
1283
- }
1381
+ const { deed, tags } = respondToConscience(p, act, now, completed, minutes);
1284
1382
  // Agenda.
1285
1383
  const routed = { ...outcome };
1286
1384
  if (routed.fulfills === undefined && aff.fulfills !== undefined)
@@ -1303,58 +1401,10 @@ export function finish(p, outcome, opts = {}) {
1303
1401
  }
1304
1402
  }
1305
1403
  // Relationships from taking part together.
1306
- if (completed) {
1307
- if (p.character && (aff.with ?? []).some((o) => o !== p.id))
1308
- noteCharacterSocial(p, minutes);
1309
- const kind = tags.includes('work') ? 'shared-work' : 'chat';
1310
- for (const other of aff.with ?? []) {
1311
- if (other === p.id)
1312
- continue;
1313
- socialEvent(p, { at: now, kind, otherId: other, byMe: true, magnitude: D.participationMagnitude });
1314
- }
1315
- if (aff.targetId !== undefined && aff.targetId !== p.id) {
1316
- const care = tags.includes('help') ? 'help' : tags.includes('gift') ? 'gift' : undefined;
1317
- if (care)
1318
- socialEvent(p, { at: now, kind: care, otherId: aff.targetId, byMe: true, magnitude: 0.7 });
1319
- }
1320
- }
1321
- // Trust in every voice that pushed this activity: how it felt, minus bodily harm the host's deltas do not
1322
- // show (needs that fell during the activity and are now urgent).
1323
- const voices = act.suggestions ?? (act.suggestion ? [act.suggestion] : []);
1324
- if (voices.length > 0) {
1325
- let harm = 0;
1326
- for (const id of ['food', 'water', 'sleep', 'rest']) {
1327
- if (urgencyOf(after, id) < D.voiceHarmUrgency)
1328
- continue;
1329
- harm += Math.max(0, (act.needsAtStart[id] ?? levelOf(after, id)) - levelOf(after, id));
1330
- }
1331
- // Doing what a voice pushed broke a commitment or the person's own standards: that costs the voice as harm
1332
- // does (review 2026-10-03: thirty broken fasts under insistence had moved trust only 0.50 -> 0.41). Omissions
1333
- // the push caused later (a missed prayer during a pushed activity) are not attributed here.
1334
- let breach = 0;
1335
- for (const c of broken)
1336
- breach += D.missedDistress * c.importance;
1337
- breach += D.missedDistress * 0.5 * deed.breached.length;
1338
- for (const res of voices) {
1339
- const protest = res.verdict === 'complied' || res.verdict === 'commanded';
1340
- const reason = breach > 0
1341
- ? protest
1342
- ? 'breach-under-protest'
1343
- : 'breach'
1344
- : harm > 0
1345
- ? protest
1346
- ? 'harm-under-protest'
1347
- : 'harm'
1348
- : felt >= 0
1349
- ? 'went-well'
1350
- : 'went-badly';
1351
- learnFromVoice(p, res, clampSigned(felt - D.voiceHarmGain * harm - breach), {
1352
- at: now,
1353
- action: aff.action,
1354
- reason,
1355
- });
1356
- }
1357
- }
1404
+ if (completed)
1405
+ recordParticipation(p, aff, tags, now, minutes);
1406
+ // Voice learning follows both conscience and agenda, so it includes their breaches.
1407
+ learnActivityVoices(p, act, after, now, felt, broken, deed.breached.length);
1358
1408
  // Chronicle, while the activity is still current.
1359
1409
  const noted = { kept: kept.map((c) => c.id) };
1360
1410
  if (act.suggestion)
@@ -260,9 +260,15 @@ export function sanitizeProspection(input, now, personId) {
260
260
  !minute(r.receivedAt) ||
261
261
  r.receivedAt > now)
262
262
  continue;
263
- if (revisions.some((old) => identity(old, r)))
263
+ const validated = {
264
+ sourceId: r.sourceId,
265
+ id: r.id,
266
+ revision: r.revision,
267
+ receivedAt: r.receivedAt,
268
+ };
269
+ if (revisions.some((old) => identity(old, validated)))
264
270
  return undefined;
265
- revisions.push({ sourceId: r.sourceId, id: r.id, revision: r.revision, receivedAt: r.receivedAt });
271
+ revisions.push(validated);
266
272
  }
267
273
  const valid = [];
268
274
  for (const e of entries) {
package/dist/restore.d.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  import type { Person } from './types.ts';
2
2
  /** A deep copy of the person as plain JSON (what a host saves). */
3
3
  export declare function snapshot(p: Person): Person;
4
+ /**
5
+ * A deterministic 16-hex-digit digest of the canonical saved person. Canonicalization runs the saved form
6
+ * through `restore` (including migrations, defaults, bounds and optional-slice sanitizers), then sorts every
7
+ * object's keys recursively; array order stays significant. Thus key insertion order and empty optional slices
8
+ * that `restore` drops do not change the digest. Includes the engine stamp. Reads only; never mutates `p`.
9
+ * Uses 64-bit FNV-1a over JSON's UTF-16 code units, with no platform or crypto dependency. This is a change
10
+ * detector, not a cryptographic integrity check; collisions are possible. Invalid saves throw as in `restore`.
11
+ */
12
+ export declare function fingerprint(p: Person): string;
4
13
  /**
5
14
  * Validate a saved person and fill defaults for missing or mistyped fields, slice by slice and field by
6
15
  * field. A save from an earlier supported engine is upgraded by `migrate` first. Throws on a wrong schema, a
package/dist/restore.js CHANGED
@@ -8,6 +8,7 @@
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';
@@ -31,6 +32,26 @@ import { sanitizeWill } from './will/index.js';
31
32
  export function snapshot(p) {
32
33
  return structuredClone(p);
33
34
  }
35
+ /**
36
+ * A deterministic 16-hex-digit digest of the canonical saved person. Canonicalization runs the saved form
37
+ * through `restore` (including migrations, defaults, bounds and optional-slice sanitizers), then sorts every
38
+ * object's keys recursively; array order stays significant. Thus key insertion order and empty optional slices
39
+ * that `restore` drops do not change the digest. Includes the engine stamp. Reads only; never mutates `p`.
40
+ * Uses 64-bit FNV-1a over JSON's UTF-16 code units, with no platform or crypto dependency. This is a change
41
+ * detector, not a cryptographic integrity check; collisions are possible. Invalid saves throw as in `restore`.
42
+ */
43
+ export function fingerprint(p) {
44
+ const json = JSON.stringify(restore(snapshot(p)), (_key, value) => {
45
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
46
+ return value;
47
+ return Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
48
+ });
49
+ let hash = 0xcbf29ce484222325n;
50
+ for (let i = 0; i < json.length; i++) {
51
+ hash = BigInt.asUintN(64, (hash ^ BigInt(json.charCodeAt(i))) * 0x100000001b3n);
52
+ }
53
+ return hash.toString(16).padStart(16, '0');
54
+ }
34
55
  // Unlike core `isObj`, arrays pass: a required slice saved as an array is filled from defaults, not refused.
35
56
  const isObject = (x) => typeof x === 'object' && x !== null;
36
57
  const isMinute = (x) => isNum(x) && Math.abs(x) <= MAX_MINUTE;
@@ -168,6 +189,7 @@ export function restore(input) {
168
189
  sanitizeCrisis(out.affect);
169
190
  sanitizeAgenda(out.agenda);
170
191
  sanitizeGists(out.memory);
192
+ sanitizeBeliefMeta(out.memory);
171
193
  // Optional top-level slices (1.8.0 onward), each checked by its owner.
172
194
  keepValid(out, 'chronicleYears', sanitizeYears);
173
195
  keepValid(out, 'character', sanitizeCharacter);
package/dist/sim/sim.d.ts CHANGED
@@ -225,6 +225,7 @@ export declare function communityState(c: Community): CommunityState;
225
225
  /**
226
226
  * A community over `people`. With `prior` (from `communityState`, possibly through JSON), the host-side state is
227
227
  * resumed from it, so a run restored from people + community + world state continues exactly as it would have.
228
+ * `prior` is copied: after stepping, write back `communityState(c)`; the old `prior` stays stale.
228
229
  */
229
230
  export declare function createCommunity(people: Person[], prior?: CommunityState): Community;
230
231
  /** Add a person to a community (a newborn, an arrival); keeps the id order the driver relies on. */
@@ -237,9 +238,13 @@ export declare function birth(c: Community, a: Person, b: Person, spec: ChildSpe
237
238
  /**
238
239
  * Tell `dead`'s death at `at` to everyone alive in the community who has a tie to them (1.8.0): a told `death`
239
240
  * percept through `perceive`, so they grieve, the tie is kept and marked, and a spouse is widowed. Each listener's
240
- * clock must be at or before `at`; the drivers call it when they notice the death.
241
+ * receipt time is at least their own `now`. `late: 'now'` explicitly permits listeners past `at`, as does the
242
+ * existing default. `late: 'throw'` requires all eligible listeners to be at or before `at` and throws before
243
+ * delivering any news if one is ahead. This changes no listener's clock.
241
244
  */
242
- export declare function tellDeath(c: Community, dead: Person, at: Minute): void;
245
+ export declare function tellDeath(c: Community, dead: Person, at: Minute, opts?: {
246
+ late?: 'throw' | 'now';
247
+ }): void;
243
248
  /** Next minute at which this person needs attention. */
244
249
  export declare function nextEventAt(c: Community, p: Person): Minute;
245
250
  /**