@adam0white/human-framework 2.18.0 → 2.20.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/README.md +51 -0
- package/dist/agenda/agenda.d.ts +47 -0
- package/dist/agenda/agenda.js +4 -1
- package/dist/body/body.d.ts +10 -0
- package/dist/body/body.js +15 -0
- package/dist/chronicle/story.js +3 -2
- package/dist/cognition/cognition.d.ts +37 -0
- package/dist/cognition/cognition.js +3 -2
- package/dist/conversation/conversation.js +10 -4
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/keys.d.ts +6 -0
- package/dist/core/keys.js +8 -0
- package/dist/family/family.js +5 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/longrun.js +1 -1
- package/dist/memory/memory.d.ts +26 -0
- package/dist/memory/memory.js +3 -9
- package/dist/narrate/lexicon.d.ts +2 -1
- package/dist/narrate/lexicon.js +2 -1
- package/dist/narrate/narrate.d.ts +14 -1
- package/dist/narrate/narrate.js +81 -39
- package/dist/narrate/plain.d.ts +7 -0
- package/dist/narrate/plain.js +293 -0
- package/dist/observed-reputation.d.ts +4 -0
- package/dist/observed-reputation.js +16 -0
- package/dist/partnering/partnering.js +1 -1
- package/dist/person.d.ts +5 -0
- package/dist/person.js +119 -11
- package/dist/presentation.d.ts +10 -0
- package/dist/presentation.js +88 -0
- package/dist/promptings/promptings.d.ts +1 -0
- package/dist/promptings/promptings.js +40 -4
- package/dist/prospection/forecast.d.ts +2 -0
- package/dist/prospection/forecast.js +15 -1
- package/dist/qalb.d.ts +28 -0
- package/dist/qalb.js +67 -0
- package/dist/review/review.d.ts +32 -0
- package/dist/review/review.js +6 -2
- package/dist/scenarios/village.js +63 -26
- package/dist/sim/longrun.js +20 -3
- package/dist/sim/sim.d.ts +1 -1
- package/dist/sim/sim.js +93 -15
- package/dist/social/social.js +12 -2
- package/dist/types.d.ts +6 -0
- package/dist/will/will.d.ts +12 -0
- package/dist/will/will.js +43 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -489,3 +489,54 @@ const returnInput = advanceChapterClock(clock, 48 * 3_600_000);
|
|
|
489
489
|
```
|
|
490
490
|
|
|
491
491
|
The host owns elapsed-time pacing, narrative interpretation, storage, consent and persona admission. A slot is an opportunity for coarse fiction. It is not proof of a lived day, settled duty, completed project or recovered body. Existing actor state and engine2.17.0 continuation remain unchanged unless the host explicitly uses the helper.
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
## Plain English (2.19.0)
|
|
495
|
+
|
|
496
|
+
Set `lexicon: { plainEnglish: true }` in `PersonSpec` for new people. Change an
|
|
497
|
+
existing person's choice only through an explicit recorded host action. Do not
|
|
498
|
+
overwrite a loaded run's saved choice. Built-in decision narration,
|
|
499
|
+
spoken replies, intentions and person summaries then use complete, literal English.
|
|
500
|
+
Keep this choice in the saved person and replay setup. It changes recorded prose.
|
|
501
|
+
Without the option, legacy wording and continuation stay exact. Engine2.17.0 remains
|
|
502
|
+
unchanged: this option changes presentation, not decisions or save structure.
|
|
503
|
+
|
|
504
|
+
Host `lines`, `extend`, names and action forms still apply. A host phrase pack owns
|
|
505
|
+
its own grammar. HF does not translate arbitrary host labels or memory summaries.
|
|
506
|
+
Short words used inside templates remain phrases, so they compose grammatically.
|
|
507
|
+
Pass the same lexicon to renderers without a Person, including chronicle renderers
|
|
508
|
+
and label/reason readers.
|
|
509
|
+
Use authored `actions` forms for domain actions that English cannot inflect from an id.
|
|
510
|
+
|
|
511
|
+
Run `node packages/human/examples/plain-narration-control.ts` to compare legacy and
|
|
512
|
+
plain replies without advancing the person's state or RNG.
|
|
513
|
+
|
|
514
|
+
`labelLine(label, lexicon)` and `commitmentLabel(commitment, lexicon)` explain built-in
|
|
515
|
+
prayer and purpose labels without changing stored identity keys. `normLabel(norm, lexicon)`
|
|
516
|
+
explains selected built-in norm labels. `reasonLine(code, lexicon)` gives a sentence
|
|
517
|
+
for a known reason; an unknown plain reason returns an empty string so a host can
|
|
518
|
+
supply its own explanation. Without the option, each reader returns the legacy text.
|
|
519
|
+
|
|
520
|
+
`promptingLine(..., { plainEnglish: true })` supports direct calls.
|
|
521
|
+
`promptingsReading`, `reviewLine` and `bodyForecast` read the saved person's choice.
|
|
522
|
+
A person’s held interpretation of a thought stays distinct from an observed response.
|
|
523
|
+
Neither wording nor a missed-duty explanation changes obligations or excuses.
|
|
524
|
+
|
|
525
|
+
For domain-specific conversation facts, supply `converse`'s `phrase` callback.
|
|
526
|
+
HF cannot infer the English meaning of an opaque proposition id. Without authored
|
|
527
|
+
wording, the generic fact sentence still includes that id so the claim is not hidden.
|
|
528
|
+
|
|
529
|
+
Stock village outcomes, birth/marriage memories, routine fallbacks and generated
|
|
530
|
+
community log details use the same per-person option. World-supplied outcome
|
|
531
|
+
summaries and routine summaries remain exact.
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
### Authored setup and explicit host effects
|
|
535
|
+
|
|
536
|
+
Hosts may pass `habits: Habit[]` and `expectations: ActionExpectation[]` to `createPerson`. The framework copies records and their nested objects in order. Invalid or over-bound setup throws `RangeError`; omission preserves prior state. Duplicates remain authored records, with the existing matcher semantics.
|
|
537
|
+
|
|
538
|
+
Use `gestureVoiceTrust(p, voiceId, amount, { reason, at?, action? })` for an explicit signed gesture toward an existing advising voice. It records the realized delta in bounded history and returns `undefined` for a missing voice. Reasons must be nonempty strings; optional action must be a string. Use `addTiredness(p, { sleepPressure?, exertion? })` for positive host-caused costs. Neither advances time or draws randomness. Host magnitudes are engineering premises.
|
|
539
|
+
|
|
540
|
+
`confirmObservedReputation(p, prop, truth, now, table?)` replaces the manual `credence → confirm → applyReputationBelief` sequence. Supply only a fact the person could observe. It does not run attention or invent a moral judgement.
|
|
541
|
+
|
|
542
|
+
`qalbReading(p)` is a read-only, sourced narration lens over existing records. Its detached categorical rows are this person's recorded understanding and evidence. They are never a spiritual health score, a ruling, a rank, or a decision input. Do not present mood, low social trust or missing memories as evidence of poor faith.
|
package/dist/agenda/agenda.d.ts
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: The agenda holds a person's commitments (promises, duties, appointments, worship windows, jobs)
|
|
3
|
+
* and goals, turns them into utility terms, closes them when kept or broken, recurs periodic ones, and
|
|
4
|
+
* proposes spontaneous purposes when a psychological need goes unserved. Shapes borrowed: deadline
|
|
5
|
+
* pressure rising toward the end of a window (temporal-motivation style, monotone and saturating, not
|
|
6
|
+
* hyperbolic-discounting fitted values), SDT-style needs (belonging, competence, meaning) as the sources
|
|
7
|
+
* of spontaneous goals. Breaking a commitment is reported to the composite, which routes any linked norm
|
|
8
|
+
* through conscience; the agenda itself does not judge. It does not plan multi-step routes, model
|
|
9
|
+
* implementation intentions, or claim calibrated procrastination curves.
|
|
10
|
+
*
|
|
11
|
+
* SCOPE (abstention, lane agenda+conscience): an `abstain` commitment (a fast, e.g. "no work after the evening prayer") is kept by
|
|
12
|
+
* not completing any `violatedBy` action whose span overlaps the window, and broken by the first one that does.
|
|
13
|
+
* While open it pushes against violating options with a constant negative term (importance x held conviction);
|
|
14
|
+
* it does not rise with deadline pressure, because the cost of breaking does not grow toward the end. Shape:
|
|
15
|
+
* self-imposed restraint as a standing cost on the tempting option (precommitment, cf. Ariely & Wertenbroch 2002
|
|
16
|
+
* for the qualitative effect only; no fitted values). Interrupted outcomes do not break it (hosts report a
|
|
17
|
+
* partial meal as completed if it should). Exemptions (illness, travel) come from the norm catalog's
|
|
18
|
+
* `exemptions` with their provenance, release that day's instance and record an owed make-up; they are not the
|
|
19
|
+
* necessity exception of Qur'an 2:173. The illness threshold is an engineering stand-in for the person's own
|
|
20
|
+
* judgment that they are ill; the fidya provision of 2:184 and any expiation (kaffara) for deliberate breaking are
|
|
21
|
+
* not modelled (kaffara is disputed: research/fasting-sources.md §1). A deliberately broken obligatory fast is a
|
|
22
|
+
* breach and also leaves a make-up owed (1.7.0; research/decisions.md, fasting-sources.md §1: the day is made up).
|
|
23
|
+
* A break under necessity is excused with a make-up owed (see the necessity-break SCOPE below).
|
|
24
|
+
*
|
|
25
|
+
* SCOPE (missed duties and make-up debt, engine 1.7.0; research/decisions.md, research/capacity-and-excuse-sources.md):
|
|
26
|
+
* the sources keep blame (a breach) apart from debt (qada) (capacity-and-excuse-sources.md §0). When a `worship`
|
|
27
|
+
* window linked to a norm the person holds as obligatory closes unkept, or an obligatory `abstain` is broken, an
|
|
28
|
+
* `OwedMakeUp` is recorded in `agenda.owed` whatever the cause; what the cause decides is the blame:
|
|
29
|
+
* - Asleep throughout (§4: sleep lifts blame, not the debt): the window opened while the person was already
|
|
30
|
+
* asleep and they woke no earlier than `wakeGrace` minutes before it closed (read from `body.since` and
|
|
31
|
+
* `body.lastSleep`). Sleep begun inside the open window is not excused: whether the sleeper expected to wake is
|
|
32
|
+
* not represented, so the framework takes the stricter reading.
|
|
33
|
+
* - Downed when it closed (§2, unconsciousness; `body.downed`, `body.lastDowned`): blame lifted, debt owed, but
|
|
34
|
+
* once more than `lapseWaiver` (5) windows have closed within one downing, that stretch's debt drops and no more
|
|
35
|
+
* is added (the Hanafi count by prayer times, kept as the default by decisions.md; Maliki and Shafi'i drop it
|
|
36
|
+
* sooner, Hanbali never). Downing is involuntary, so going down after the window opened still excuses it.
|
|
37
|
+
* - Excused instances close `released` with `exempt.reason` 'sleep' or 'unconscious', so the composite books no
|
|
38
|
+
* breach, distress or esteem cost; the chain recurs as for any exemption.
|
|
39
|
+
* - A mental break (`affect/crisis.ts`, reason intact) is not an excuse (§6: the test is retained capacity, and
|
|
40
|
+
* motivation is irrelevant): the window closes broken, with its breach, and the debt is owed (reason 'missed').
|
|
41
|
+
* - Forgetting lifts blame in the sources (§4) but the framework has no state for having forgotten a duty, and no
|
|
42
|
+
* state for loss of reason (junun, §1) or intoxication (§3); none of them is detected. Coercion and commands are
|
|
43
|
+
* not excuses here (§7).
|
|
44
|
+
* A make-up commitment (`makeUpOf`, see `scheduleMakeUp`) that closes unkept, or is broken, carries no blame: qada
|
|
45
|
+
* has no fixed time in the sources, so it closes `released` and its debt is open again for the host to reschedule.
|
|
46
|
+
* The framework never computes whether a prayer or a make-up is accepted.
|
|
47
|
+
*/
|
|
1
48
|
import type { Affordance, AgendaState, Commitment, Episode, Goal, Minute, MissedExcuse, NeedId, NeedReading, NormDefinition, Outcome, OwedMakeUp, Person, PersonId, PsychologicalNeed, Term } from '../types.ts';
|
|
2
49
|
export interface GoalTemplate {
|
|
3
50
|
label: string;
|
package/dist/agenda/agenda.js
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
import { DEFAULT_NORMS } from '../conscience/catalog.js';
|
|
50
50
|
import { clamp01, dayOf, dpow, hasNumbers, isNum, isObj } from '../core/index.js';
|
|
51
51
|
import { expectedEffect } from '../memory/index.js';
|
|
52
|
+
import { labelLine } from '../presentation.js';
|
|
52
53
|
import { diminishesProject } from '../projects/index.js';
|
|
53
54
|
import { MINUTES_PER_DAY } from '../types.js';
|
|
54
55
|
export const AGENDA_DEFAULTS = {
|
|
@@ -1015,7 +1016,9 @@ export function revisePurposes(p, now) {
|
|
|
1015
1016
|
kind: 'abandoned',
|
|
1016
1017
|
actorId: p.id,
|
|
1017
1018
|
valence: -0.3 * g.baseImportance,
|
|
1018
|
-
summary:
|
|
1019
|
+
summary: p.lexicon?.plainEnglish
|
|
1020
|
+
? `I stopped trying to reach this goal: ${labelLine(g.label, p.lexicon)}.`
|
|
1021
|
+
: `gave up on ${g.label}`,
|
|
1019
1022
|
tags: ['abandoned', 'goal', g.label, ...g.serves],
|
|
1020
1023
|
});
|
|
1021
1024
|
}
|
package/dist/body/body.d.ts
CHANGED
|
@@ -154,6 +154,16 @@ export declare function fastingDamping(b: Pick<BodyState, 'satiety' | 'hydration
|
|
|
154
154
|
* fast: perceived hunger is damped (see SCOPE, N2b); true values, capacity and health are unchanged.
|
|
155
155
|
*/
|
|
156
156
|
export declare function readBody(p: Person, params?: BodyParams, ctx?: BodyPerceptionContext): BodyReadout;
|
|
157
|
+
/**
|
|
158
|
+
* SCOPE (host-caused tiredness): add nonnegative sleep-pressure and exertion costs, clamped to 0..1.
|
|
159
|
+
* The host supplies magnitudes; these are not calibrated claims. Absent, negative and non-finite costs
|
|
160
|
+
* do nothing. A dead body is unchanged, as with consume. Writes only these two body fields: no ticking,
|
|
161
|
+
* sleep transition, depletion, memory, RNG or other effects. Recovery remains with the existing body APIs.
|
|
162
|
+
*/
|
|
163
|
+
export declare function addTiredness(p: Person, costs: {
|
|
164
|
+
sleepPressure?: number;
|
|
165
|
+
exertion?: number;
|
|
166
|
+
}): void;
|
|
157
167
|
/** Eat, drink or take pain relief. `relief` lowers current pain; it then relaxes back toward its sources. */
|
|
158
168
|
export declare function consume(p: Person, deltas: {
|
|
159
169
|
food?: number;
|
package/dist/body/body.js
CHANGED
|
@@ -578,6 +578,21 @@ export function readBody(p, params = BODY_DEFAULTS, ctx) {
|
|
|
578
578
|
},
|
|
579
579
|
};
|
|
580
580
|
}
|
|
581
|
+
/**
|
|
582
|
+
* SCOPE (host-caused tiredness): add nonnegative sleep-pressure and exertion costs, clamped to 0..1.
|
|
583
|
+
* The host supplies magnitudes; these are not calibrated claims. Absent, negative and non-finite costs
|
|
584
|
+
* do nothing. A dead body is unchanged, as with consume. Writes only these two body fields: no ticking,
|
|
585
|
+
* sleep transition, depletion, memory, RNG or other effects. Recovery remains with the existing body APIs.
|
|
586
|
+
*/
|
|
587
|
+
export function addTiredness(p, costs) {
|
|
588
|
+
if (!p.body.alive)
|
|
589
|
+
return;
|
|
590
|
+
for (const key of ['sleepPressure', 'exertion']) {
|
|
591
|
+
const amount = costs[key];
|
|
592
|
+
if (amount !== undefined && Number.isFinite(amount) && amount > 0)
|
|
593
|
+
p.body[key] = clamp01(p.body[key] + amount);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
581
596
|
/** Eat, drink or take pain relief. `relief` lowers current pain; it then relaxes back toward its sources. */
|
|
582
597
|
export function consume(p, deltas) {
|
|
583
598
|
const b = p.body;
|
package/dist/chronicle/story.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* activity. Lines report behaviour and stated relationships; they never grade devotion, worth or acceptance.
|
|
9
9
|
*/
|
|
10
10
|
import { actionForms, capitalize, displayName, nameOf, phraseLine } from '../narrate/lexicon.js';
|
|
11
|
+
import { labelLine } from '../presentation.js';
|
|
11
12
|
/** Thresholds for `diffChronicle` (share of days). */
|
|
12
13
|
export const STORY_DEFAULTS = {
|
|
13
14
|
/** An item done unprompted on at least this share of days counts as done "without being told". */
|
|
@@ -97,7 +98,7 @@ function worshipActions(records) {
|
|
|
97
98
|
}
|
|
98
99
|
function formsOf(item, lex) {
|
|
99
100
|
if (item.startsWith('prayer:')) {
|
|
100
|
-
const label = item.slice('prayer:'.length);
|
|
101
|
+
const label = labelLine(item.slice('prayer:'.length), lex);
|
|
101
102
|
return {
|
|
102
103
|
base: phraseLine('prayer.base', { label }, lex),
|
|
103
104
|
past: phraseLine('prayer.past', { label }, lex),
|
|
@@ -138,7 +139,7 @@ export function narrateChronicle(chronicle, opts = {}) {
|
|
|
138
139
|
? 'chronicle.prayer.one'
|
|
139
140
|
: 'chronicle.prayer.all'
|
|
140
141
|
: 'chronicle.prayer';
|
|
141
|
-
raw.push({ key, vars: { label, n, days } });
|
|
142
|
+
raw.push({ key, vars: { label: labelLine(label, lex), n, days } });
|
|
142
143
|
}
|
|
143
144
|
// Commitments kept by kind (worship is covered above).
|
|
144
145
|
const kept = {};
|
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: scoring of offered options. `consider` sums the utility terms listed in docs/framework.md
|
|
3
|
+
* ("Utility of an option"): need terms use the person's need urgencies times the gain they *believe* an
|
|
4
|
+
* option gives (the advertisement blended with learned expectation, times believed success); norm,
|
|
5
|
+
* commitment/goal, habit, emotion-tendency, social, effort, risk, material, precommitment and suggestion
|
|
6
|
+
* terms come from the owning faculties or from simple coefficient forms. Traits and values only scale
|
|
7
|
+
* terms. `decide` processes affordances in stable id order, scores them all and hands the list to `will`
|
|
8
|
+
* for vetoes and selection. Borrowed shapes: additive multi-attribute utility with a saturating money
|
|
9
|
+
* term (diminishing marginal value of material gain), prediction-error-adjusted action values
|
|
10
|
+
* (model-free cached values alongside the host's model-based advertisement). It does NOT claim calibrated
|
|
11
|
+
* weights, rational-choice optimality, or that a weighted sum exhausts human deliberation; every term is
|
|
12
|
+
* kept in the trace so the UI can show why.
|
|
13
|
+
*
|
|
14
|
+
* SCOPE (lane will+cognition, 2026-10-03): several voices add one `suggestion:<voiceId>` term each, with
|
|
15
|
+
* reactance summed into one `autonomy` term; remembered advice adds `suggestion:remembered:<source>` terms
|
|
16
|
+
* (see `rememberedTerms` and `will.rememberAdvice`). Scarcity (N13): a host-supplied 0..1 shortfall raises the
|
|
17
|
+
* weight of the material term, shrinks its saturation scale so small sums still count, and raises the
|
|
18
|
+
* `need:safety` term. Borrowed shapes: diminishing marginal utility of wealth (the same sum weighs more to
|
|
19
|
+
* someone who has less) and the scarcity account of Mullainathan & Shafir, where a shortfall captures
|
|
20
|
+
* attention and raises the felt value of the scarce resource. Neither source is catalogued in
|
|
21
|
+
* research/empirical-models.md yet; the coefficients are engineering defaults. It does not model the claimed
|
|
22
|
+
* cognitive cost of scarcity ("bandwidth tax"), debt itself (a host ledger plus commitments), or present
|
|
23
|
+
* bias; and because losses are amplified too, a host must carry the benefit of paying a debt (a commitment,
|
|
24
|
+
* an advertised `safety` gain) or paying looks only like a loss. The spec's "security need" is read as the
|
|
25
|
+
* `safety` need: `security` is a value, not a need, and no need is added.
|
|
26
|
+
*
|
|
27
|
+
* SCOPE (2.8.0, both additive): an offer naming `alongside` partners (people already taking that course) gets one
|
|
28
|
+
* `together:<id>` term per known, living partner, `togetherScale` × closeness, without belonging satiation: the
|
|
29
|
+
* pull is not to be left behind, which stays when the pair is together and belonging is full (The Rope's resting
|
|
30
|
+
* partner, 2026-10-18 reproduction: belonging terms near 0.01 against inertia of 0.07-0.15). Shape only, after
|
|
31
|
+
* joint commitment and the sense of commitment in joint action (Gilbert 1990; Michael and Pacherie 2015; cited unaudited, research/empirical-models.md §6b); it does
|
|
32
|
+
* not model who leads, negotiation, or obligations between the partners. With opt-in `body.fullness`, an offer
|
|
33
|
+
* that advertises food, water or relief while that need is past its comfort threshold gets `full:<need>` =
|
|
34
|
+
* -fullnessScale × gain × over²: eating or drinking when full is mildly unpleasant (negative alliesthesia, unaudited citation, Cabanac
|
|
35
|
+
* 1971), so idle choice stops cycling short consumption at full hydration (The Rope, owner Kestrel export
|
|
36
|
+
* 2026-10-19). It models no bladder, sensory-specific satiety by food, or overeating for comfort.
|
|
37
|
+
*/
|
|
1
38
|
import type { BodyPerceptionContext } from '../body/index.ts';
|
|
2
39
|
import { type HabitContext } from '../habits/index.ts';
|
|
3
40
|
import { type PromptingSeen } from '../promptings/index.ts';
|
|
@@ -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 { keysOf } from '../core/keys.js';
|
|
43
44
|
import { curiosityTerms } from '../curiosity/index.js';
|
|
44
45
|
import { habitEase, habitPull } from '../habits/index.js';
|
|
45
46
|
import { expectedEffect } from '../memory/index.js';
|
|
@@ -270,7 +271,7 @@ export function consider(p, aff, ctx) {
|
|
|
270
271
|
};
|
|
271
272
|
// Needs: urgency × believed gain × believed success.
|
|
272
273
|
const expected = expectedEffect(p, aff);
|
|
273
|
-
for (const id of
|
|
274
|
+
for (const id of keysOf(expected.needs)) {
|
|
274
275
|
const gain = expected.needs[id] ?? 0;
|
|
275
276
|
if (gain === 0)
|
|
276
277
|
continue;
|
|
@@ -296,7 +297,7 @@ export function consider(p, aff, ctx) {
|
|
|
296
297
|
let relevance = 1;
|
|
297
298
|
if (expected.valence > 0) {
|
|
298
299
|
let served = -1;
|
|
299
|
-
for (const id of
|
|
300
|
+
for (const id of keysOf(expected.needs))
|
|
300
301
|
if ((expected.needs[id] ?? 0) > 0)
|
|
301
302
|
served = Math.max(served, urgencyOf(ctx.needs, id));
|
|
302
303
|
if (served >= 0)
|
|
@@ -244,9 +244,13 @@ export function converse(speaker, listener, ctx) {
|
|
|
244
244
|
actorId: speaker.id,
|
|
245
245
|
salience: round(0.3 + 0.4 * c.confidence),
|
|
246
246
|
claims: [{ prop: c.prop, value: c.value, confidence: c.confidence }],
|
|
247
|
-
summary:
|
|
248
|
-
?
|
|
249
|
-
|
|
247
|
+
summary: speaker.lexicon?.plainEnglish
|
|
248
|
+
? c.about !== undefined
|
|
249
|
+
? `${nameOf(speaker)} says ${speaker.lexicon.names?.[c.about] ?? c.about} ${c.prop.split(':')[1] === 'owes' ? `${c.value ? 'owes' : 'does not owe'} money to ${speaker.lexicon.names?.[c.prop.split(':').slice(2).join(':')] ?? c.prop.split(':').slice(2).join(':')}` : `is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`}.`
|
|
250
|
+
: `${nameOf(speaker)} says this is ${c.value ? 'true' : 'false'}: ${c.prop}.`
|
|
251
|
+
: c.about !== undefined
|
|
252
|
+
? `${nameOf(speaker)} says ${c.about} is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`
|
|
253
|
+
: `${nameOf(speaker)} says ${c.prop} is ${c.value ? 'so' : 'not so'}`,
|
|
250
254
|
};
|
|
251
255
|
if (ctx.phrase)
|
|
252
256
|
pc.summary = ctx.phrase(Object.freeze(structuredClone(c)), pc.summary ?? '');
|
|
@@ -293,7 +297,9 @@ export function converse(speaker, listener, ctx) {
|
|
|
293
297
|
valence: 0.1,
|
|
294
298
|
salience: round(0.4 + 0.3 * a.strength),
|
|
295
299
|
claims: [{ prop: `advice:${a.action}`, value: true, confidence: round(a.cred) }],
|
|
296
|
-
summary:
|
|
300
|
+
summary: speaker.lexicon?.plainEnglish
|
|
301
|
+
? `${nameOf(speaker)} suggests this action: ${a.action.replace(/[-_]/g, ' ')}.`
|
|
302
|
+
: `${nameOf(speaker)} says I should ${a.action}`,
|
|
297
303
|
};
|
|
298
304
|
if (ctx.provenance) {
|
|
299
305
|
const meta = beliefs.find((b) => b.prop === `${prefix}${a.action}`)?.provenance;
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Own enumerable string keys of a typed record, in Object.keys order.
|
|
3
|
+
* Callers must supply records whose runtime keys match their declared key type.
|
|
4
|
+
* This only types enumeration; it does not sort, validate keys or change state.
|
|
5
|
+
*/
|
|
6
|
+
export declare function keysOf<K extends string>(record: Partial<Record<K, unknown>>): K[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Own enumerable string keys of a typed record, in Object.keys order.
|
|
3
|
+
* Callers must supply records whose runtime keys match their declared key type.
|
|
4
|
+
* This only types enumeration; it does not sort, validate keys or change state.
|
|
5
|
+
*/
|
|
6
|
+
export function keysOf(record) {
|
|
7
|
+
return Object.keys(record);
|
|
8
|
+
}
|
package/dist/family/family.js
CHANGED
|
@@ -88,7 +88,7 @@ function sanitizeAptitudes(x) {
|
|
|
88
88
|
out[k] = clamp(v, 0.25, 4);
|
|
89
89
|
return Object.keys(out).length > 0 ? out : undefined;
|
|
90
90
|
}
|
|
91
|
-
/** Validate a saved family slice;
|
|
91
|
+
/** Validate a saved family slice; drop malformed pregnancies, keep usable fields. Undefined when none remain. @internal */
|
|
92
92
|
export function sanitizeFamily(x) {
|
|
93
93
|
if (!isObj(x))
|
|
94
94
|
return undefined;
|
|
@@ -101,9 +101,9 @@ export function sanitizeFamily(x) {
|
|
|
101
101
|
if (pg &&
|
|
102
102
|
typeof pg === 'object' &&
|
|
103
103
|
typeof pg.fatherId === 'string' &&
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
isNum(pg.conceivedAt) &&
|
|
105
|
+
isNum(pg.dueAt) &&
|
|
106
|
+
isNum(pg.seed))
|
|
107
107
|
out.pregnancy = { fatherId: pg.fatherId, conceivedAt: pg.conceivedAt, dueAt: pg.dueAt, seed: pg.seed };
|
|
108
108
|
if (isNum(f.attachment))
|
|
109
109
|
out.attachment = clamp01(f.attachment);
|
|
@@ -215,7 +215,7 @@ export function deliver(mother, child, at = mother.now) {
|
|
|
215
215
|
targetId: child.id,
|
|
216
216
|
valence: 0.9,
|
|
217
217
|
salience: 0.95,
|
|
218
|
-
summary: 'gave birth',
|
|
218
|
+
summary: mother.lexicon?.plainEnglish ? 'I gave birth.' : 'gave birth',
|
|
219
219
|
tags: ['birth', 'family'],
|
|
220
220
|
});
|
|
221
221
|
return { id: child.id, name: child.name ?? child.id, seed: pg.seed, bornAt: at, fatherId: pg.fatherId };
|
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.20.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, MakeUpReceipt, PrayerCalendar, PrayerSchedule, PrayerTimes, Retimer, } from './agenda/index.ts';
|
|
@@ -28,15 +28,18 @@ export type { MigrationStep } from './migrate.ts';
|
|
|
28
28
|
export { MIGRATIONS, migratableVersions, migrate } from './migrate.ts';
|
|
29
29
|
export * from './narrate/index.ts';
|
|
30
30
|
export * from './needs/index.ts';
|
|
31
|
+
export { confirmObservedReputation } from './observed-reputation.ts';
|
|
31
32
|
export * from './partnering/index.ts';
|
|
32
33
|
export { enablePersistence, PERSISTENCE_DEFAULTS, pursuitOf } from './persistence/index.ts';
|
|
33
34
|
export * from './person.ts';
|
|
34
35
|
export * from './persona.ts';
|
|
35
36
|
export * from './persona-calendar.ts';
|
|
37
|
+
export * from './presentation.ts';
|
|
36
38
|
export { adoptProjectTask, considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal, } from './projects/index.ts';
|
|
37
39
|
export * from './promptings/index.ts';
|
|
38
40
|
export type { ConsequenceEvidence, PlanRoute, PlanStep } from './prospection/index.ts';
|
|
39
41
|
export { bodyForecastFor, bodyForecastTerm, consequenceEvidence, enablePlanning, FORECAST_DEFAULTS, forecastLine, forecastThresholds, PROSPECTION_DEFAULTS, seedConsequences, } from './prospection/index.ts';
|
|
42
|
+
export { type QalbDescription, qalbReading } from './qalb.ts';
|
|
40
43
|
export { fingerprint, restore, snapshot } from './restore.ts';
|
|
41
44
|
export * from './review/index.ts';
|
|
42
45
|
export * from './scenarios/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.20.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, isContinuingCommitment, isUnderWay, MAKRUH_DEFAULTS, makruhWindows, missedExcuse, onFinished, owedMakeUps, PRAYER_IMPORTANCE, PURPOSE_DEFAULTS, prayerWindows, pressureReachedAt, promise, proposeGoals, RAMADAN_DEFAULTS, ramadanFast, ramadanMeals, release as releaseCommitment, retimeCommitments, retirePaidMakeUps, revisePurposes, scheduleMakeUp, spanMeetsWindow, suhoorWindow, timesFor, violatesAbstention, } from './agenda/index.js';
|
|
@@ -25,14 +25,17 @@ export { clearMethodContext, demonstrateMethod, METHOD_DEFAULTS, methodEvidence,
|
|
|
25
25
|
export { MIGRATIONS, migratableVersions, migrate } from './migrate.js';
|
|
26
26
|
export * from './narrate/index.js';
|
|
27
27
|
export * from './needs/index.js';
|
|
28
|
+
export { confirmObservedReputation } from './observed-reputation.js';
|
|
28
29
|
export * from './partnering/index.js';
|
|
29
30
|
export { enablePersistence, PERSISTENCE_DEFAULTS, pursuitOf } from './persistence/index.js';
|
|
30
31
|
export * from './person.js';
|
|
31
32
|
export * from './persona.js';
|
|
32
33
|
export * from './persona-calendar.js';
|
|
34
|
+
export * from './presentation.js';
|
|
33
35
|
export { adoptProjectTask, considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal, } from './projects/index.js';
|
|
34
36
|
export * from './promptings/index.js';
|
|
35
37
|
export { bodyForecastFor, bodyForecastTerm, consequenceEvidence, enablePlanning, FORECAST_DEFAULTS, forecastLine, forecastThresholds, PROSPECTION_DEFAULTS, seedConsequences, } from './prospection/index.js';
|
|
38
|
+
export { qalbReading } from './qalb.js';
|
|
36
39
|
export { fingerprint, restore, snapshot } from './restore.js';
|
|
37
40
|
export * from './review/index.js';
|
|
38
41
|
export * from './scenarios/index.js';
|
package/dist/longrun.js
CHANGED
|
@@ -108,7 +108,7 @@ export function routineDay(p, routine, opts = {}) {
|
|
|
108
108
|
action: a.action,
|
|
109
109
|
actorId: p.id,
|
|
110
110
|
valence: clampSigned(a.valence ?? 0),
|
|
111
|
-
summary: a.summary ?? a.action,
|
|
111
|
+
summary: a.summary ?? (p.lexicon?.plainEnglish ? `I completed the activity “${a.action}”.` : a.action),
|
|
112
112
|
tags: [...(a.tags ?? []), 'routine'],
|
|
113
113
|
};
|
|
114
114
|
if (companions[0] !== undefined)
|
package/dist/memory/memory.d.ts
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: episodic memory and learned action expectations. Episodes carry a salience that decays
|
|
3
|
+
* exponentially (Ebbinghaus-style forgetting curve, modelled as a single exponential per episode) with a
|
|
4
|
+
* half-life that lengthens for emotionally strong episodes (emotional-enhancement of memory), and recall
|
|
5
|
+
* slightly raises the salience of what is retrieved (testing effect). Expectations are cached action
|
|
6
|
+
* values learned by a delta rule (prediction-error learning, Rescorla-Wagner shape) with a minimum
|
|
7
|
+
* learning rate and a clamped step, blended with the host's advertisement by sample-based confidence.
|
|
8
|
+
* It does NOT model reconsolidation, false memory, interference between similar episodes, sleep-dependent
|
|
9
|
+
* consolidation, semantic abstraction, or model-based planning; parameters are engineering defaults for
|
|
10
|
+
* game time scales, not calibrated estimates.
|
|
11
|
+
*
|
|
12
|
+
* SCOPE (lasting gists, 1.8.0, opt-in per person with `enableGists`): episodic detail does not last a life, but the
|
|
13
|
+
* gist of what mattered does. An episode that leaves the episode list (evicted beyond the bound, or older than
|
|
14
|
+
* `GIST_DEFAULTS.horizon` at a `foldGists` call, which the composite makes at each day's close) is folded into the
|
|
15
|
+
* gist with the same kind, action, people and place when it was emotional enough (|valence| ≥ `minValence`, or a
|
|
16
|
+
* loss); weaker ones are simply forgotten. A gist keeps the encoding-weighted mean valence, a count and the summary of
|
|
17
|
+
* its strongest episode, and fades with half-lives of years (`halfLifeNeutral`..`halfLifeEmotional` by |valence|).
|
|
18
|
+
* Gists answer `recall` and `recallByCue` like episodes (so a remembered fear or loss still comes back at its place,
|
|
19
|
+
* person or anniversary), and `expectedEffect` reports the gists about an option's action or place, which cognition
|
|
20
|
+
* weighs as a `memory` term that fades as fresh experience of the option accumulates. So a fear learned at 20 still
|
|
21
|
+
* shapes choices at 40 after its episodes are gone, and enough good later visits outweigh it. Named shapes: the
|
|
22
|
+
* fading of episodic detail while gist persists (fuzzy-trace theory, Brainerd & Reyna) and the long retention of
|
|
23
|
+
* emotional autobiographical memories; the horizon, thresholds, half-lives and the bound of 64 are engineering
|
|
24
|
+
* choices. It does not model reconstruction errors in gists, deliberate rehearsal beyond retelling (`recall`
|
|
25
|
+
* boosts), or semantic knowledge abstracted from gists.
|
|
26
|
+
*/
|
|
1
27
|
import type { Affordance, AppraisalEvent, EntityId, Episode, Gist, MemoryState, Minute, NeedId, Outcome, Person, Signed, Unit } from '../types.ts';
|
|
2
28
|
export declare const MEMORY_DEFAULTS: {
|
|
3
29
|
maxEpisodes: number;
|
package/dist/memory/memory.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* boosts), or semantic knowledge abstracted from gists.
|
|
26
26
|
*/
|
|
27
27
|
import { clamp, clamp01, clampSigned, decay, hourOf, isNum, isObj, lerp, runningMean, } from '../core/index.js';
|
|
28
|
+
import { keysOf } from '../core/keys.js';
|
|
28
29
|
import { DAYS_PER_YEAR, MINUTES_PER_DAY, MINUTES_PER_YEAR } from '../types.js';
|
|
29
30
|
export const MEMORY_DEFAULTS = {
|
|
30
31
|
maxEpisodes: 200,
|
|
@@ -511,10 +512,7 @@ export function expectedEffect(p, aff) {
|
|
|
511
512
|
const samples = learned?.samples ?? 0;
|
|
512
513
|
const confidence = samples / (samples + MEMORY_DEFAULTS.confidenceK);
|
|
513
514
|
const needs = {};
|
|
514
|
-
const keys = new Set([
|
|
515
|
-
...Object.keys(aff.advertises),
|
|
516
|
-
...(learned ? Object.keys(learned.needs) : []),
|
|
517
|
-
]);
|
|
515
|
+
const keys = new Set([...keysOf(aff.advertises), ...(learned ? keysOf(learned.needs) : [])]);
|
|
518
516
|
for (const k of [...keys].sort()) {
|
|
519
517
|
const adv = aff.advertises[k] ?? 0;
|
|
520
518
|
const lv = learned?.needs[k];
|
|
@@ -563,11 +561,7 @@ function updateExpectation(p, key, aff, success, realized, felt) {
|
|
|
563
561
|
}
|
|
564
562
|
x.samples += 1;
|
|
565
563
|
const n = x.samples;
|
|
566
|
-
const keys = new Set([
|
|
567
|
-
...Object.keys(x.needs),
|
|
568
|
-
...Object.keys(aff.advertises),
|
|
569
|
-
...Object.keys(realized),
|
|
570
|
-
]);
|
|
564
|
+
const keys = new Set([...keysOf(x.needs), ...keysOf(aff.advertises), ...keysOf(realized)]);
|
|
571
565
|
for (const k of keys) {
|
|
572
566
|
const prior = x.needs[k] ?? aff.advertises[k] ?? 0;
|
|
573
567
|
x.needs[k] = step(prior, realized[k] ?? 0, n);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SCOPE: keyed narration templates, host phrase packs and name/role lookup (N6). Every English sentence the
|
|
3
|
-
* framework narrates lives in `EN_LINES` under a stable key;
|
|
3
|
+
* framework narrates lives in `EN_LINES` under a stable key; `Lexicon.plainEnglish` selects the matching
|
|
4
|
+
* literal English pack while preserving default templates and host overrides. A host `Lexicon` replaces (`lines`) or extends
|
|
4
5
|
* (`extend`) any key, names entities ("Ada") and maps relationship roles to nouns ("daughter") so narration
|
|
5
6
|
* can say "my daughter" or "his daughter". Selection stays deterministic: a template is picked by an FNV hash of
|
|
6
7
|
* a caller key (a decision id, a day index), never by the person's RNG. This is presentation only. It is
|
package/dist/narrate/lexicon.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PLAIN_EN_LINES } from './plain.js';
|
|
1
2
|
/** FNV-1a 32-bit hash for deterministic template selection. */
|
|
2
3
|
export function hashString(s) {
|
|
3
4
|
let h = 0x811c9dc5;
|
|
@@ -264,7 +265,7 @@ export const EN_LINES = {
|
|
|
264
265
|
};
|
|
265
266
|
/** Templates for a key after the lexicon's replacements and extensions; empty when unknown. */
|
|
266
267
|
export function linesFor(key, lex) {
|
|
267
|
-
const base = lex?.lines?.[key] ?? EN_LINES[key] ?? [];
|
|
268
|
+
const base = lex?.lines?.[key] ?? (lex?.plainEnglish ? PLAIN_EN_LINES[key] : undefined) ?? EN_LINES[key] ?? [];
|
|
268
269
|
const more = lex?.extend?.[key];
|
|
269
270
|
return more && more.length > 0 ? [...base, ...more] : base;
|
|
270
271
|
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: templated first-person language for decisions and suggestion verdicts, plus a short summary of a
|
|
3
|
+
* person for UI. Sentences are selected from keyed template sets (`EN_LINES`, overridable per key by a host
|
|
4
|
+
* `Lexicon`, N6) by a hash of the decision id, so the same record always narrates the same way without
|
|
5
|
+
* consuming the person's RNG. When memory shaped an option (the record's `recalled` episodes), the narration
|
|
6
|
+
* cites the episode summary. People are named through `nameOf` (lexicon name, role phrase, or id). This is
|
|
7
|
+
* presentation only: nothing here reads back into the simulation, and no claim is made that the templates
|
|
8
|
+
* reflect how people actually explain themselves. `Lexicon.plainEnglish` opts into sentence intentions
|
|
9
|
+
* and summaries; arbitrary host prose is not translated or grammatically inferred.
|
|
10
|
+
*/
|
|
1
11
|
import type { DecisionRecord, Episode, Lexicon, Person, SuggestionResolution } from '../types.ts';
|
|
2
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* A plain intention is a sentence; legacy intentions remain phrases for existing hosts.
|
|
14
|
+
* Host-authored sentences pass through. This renderer never changes the record or RNG.
|
|
15
|
+
*/
|
|
3
16
|
export declare function intentionFor(p: Person, record: DecisionRecord, lexicon?: Lexicon): string;
|
|
4
17
|
/** First-person narration of a decision record. Deterministic by the decision id. */
|
|
5
18
|
export declare function narrateDecision(p: Person, record: DecisionRecord, lexicon?: Lexicon): string;
|