@adam0white/human-framework 2.21.0 → 2.23.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 +8 -4
- package/dist/body/doze.d.ts +6 -3
- package/dist/body/doze.js +7 -4
- package/dist/conversation/conversation.d.ts +41 -0
- package/dist/conversation/conversation.js +6 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/migrate.js +5 -0
- package/dist/narrate/narrate.js +3 -1
- package/dist/observed-reputation.js +1 -1
- package/dist/person.d.ts +11 -3
- package/dist/person.js +20 -11
- package/dist/persona.js +1 -0
- package/dist/presentation.js +1 -0
- package/dist/promptings/promptings.js +2 -2
- package/dist/qalb.d.ts +0 -12
- package/dist/qalb.js +22 -8
- package/dist/sim/sim.js +9 -4
- package/dist/tick-input.d.ts +24 -0
- package/dist/tick-input.js +56 -0
- package/dist/types.d.ts +3 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -222,7 +222,7 @@ reference host: `createVillage`.
|
|
|
222
222
|
|
|
223
223
|
There are two version numbers, and they move independently.
|
|
224
224
|
|
|
225
|
-
- **The release version** is the package version (`package.json`, currently `2.
|
|
225
|
+
- **The release version** is the package version (`package.json`, currently `2.23.0`) and `FRAMEWORK_VERSION`, the same
|
|
226
226
|
string compiled into the build; a test keeps them equal. It versions the public API under
|
|
227
227
|
[semver](https://semver.org/): a breaking change to an exported name or signature bumps the major, a new
|
|
228
228
|
faculty or export the minor, a fix the patch. Each release is a tag `vX.Y.Z` and a GitHub release whose notes
|
|
@@ -236,15 +236,19 @@ There are two version numbers, and they move independently.
|
|
|
236
236
|
The root `package.json` version is the site's version (stamped into the site's `release.json`) and is not part of
|
|
237
237
|
this policy.
|
|
238
238
|
|
|
239
|
-
##
|
|
239
|
+
## Observation and inherited aptitude
|
|
240
240
|
|
|
241
|
-
`
|
|
241
|
+
`observeSkill(p, skill, minutes, modelLevel, domain, { inheritedAptitude: true })` applies the person's existing inherited skill factor to observation. An attended `Percept.demonstrates` can declare the same flag. Only literal `true` enables it. Without it, watching keeps its previous rate, including for people whose saved family already has aptitudes. This adds no Person state or RNG draw and does not change direct practice. Save the host's observation inputs and pin the package when opting in.
|
|
242
|
+
|
|
243
|
+
## Optional felt body (2.21 release)
|
|
244
|
+
|
|
245
|
+
`PersonSpec.enable.doze` accepts `{ posture: 'upright' | 'seated' | 'reclined', stimulation: number }`; `enableDoze` and `setDozeContext` are the explicit host calls. Doze onset is checked at integer-minute boundaries while awake. `readDoze` reports the current hazard; `nextDozeCheck` tells a manual host when to tick next. The opt-in costs one check per elapsed minute, including when onset gates are closed. It changes integration granularity, so enabled runs require a new replay pin. A dozed activity records `dozedAt`; the host resolves interrupted work with `finish`. Until that call, `begin` refuses every replacement, including voluntary sleep and work chosen after waking. Doze does not wake spontaneously. `perceive` wakes for a delivered percept stamped within the current doze span through `p.now`, including an alarm that occurred between community checkpoints, with `salience >= 0.8`, a heard/felt/social channel, detectable proximity (`near !== false`) and either `targetId === p.id` or `near === true`. The host must supply those alarms. Other delivered percepts pass through ordinary bounded attention, as during voluntary sleep. These hazard and salience values are engineering constants.
|
|
242
246
|
|
|
243
247
|
`estimateQuantity(p, { truth, difficulty })` is an explicit observation, not a display reader. Finite nonnegative truth and difficulty in `[0,1]` are required. A nonzero request consumes one draw from the person's saved seeded RNG; zero truth or zero difficulty returns truth with confidence 1 and consumes none. The result does not store a belief. Objective sleepiness widens its uniform error; perceived sleepiness determines confidence. Coefficients are engineering choices, not calibrated uncertainty intervals.
|
|
244
248
|
|
|
245
249
|
`PersonSpec.enable.bladder` or `enableBladder(p)` starts a normalized fill store. `readBladder` returns true and perceived urgency, equal in this first slice. Water intake adds delayed fill and a small base rate fills it over time. A host marks a toilet `Affordance` with `voidsBladder: true` to add urgency-based choice value. Actual successful urination is separate: a completed `Outcome` with `voidedBladder: true` empties it; `voidBladder(p)` is the direct host operation. An interrupted or failed outcome does not empty it. For an offer that declares voiding, expectation learning treats completion without actual voiding as failure of that purpose. Do not use `needs.relief` as a substitute: that field reduces pain. Filling rates, threshold and choice weight are engineering constants. The model has no continence, automatic toilet search or automatic waking.
|
|
246
250
|
|
|
247
|
-
With none of these options enabled or calls made, person state and seeded RNG continuation match HF 2.20.0 after normalizing the engine stamp only. HF 2.21.0
|
|
251
|
+
With none of these options enabled or calls made, person state and seeded RNG continuation match HF 2.20.0 after normalizing the engine stamp only. The bodily options were introduced in HF 2.21.0. Site games pin their adopted package separately from the current library; upgrading the library does not enable these faculties or silently change game runtime.
|
|
248
252
|
|
|
249
253
|
## Long runs (2.0)
|
|
250
254
|
|
package/dist/body/doze.d.ts
CHANGED
|
@@ -8,13 +8,16 @@ export declare const DOZE_DEFAULTS: Readonly<{
|
|
|
8
8
|
}>;
|
|
9
9
|
/**
|
|
10
10
|
* Enable involuntary doze without drawing RNG. Idempotent; off when absent.
|
|
11
|
-
* Hosts must deliver
|
|
11
|
+
* Hosts must deliver detectable heard/felt/social alarms with salience >= 0.8
|
|
12
12
|
* to this person (targetId, or near: true). There is no spontaneous wake: without such
|
|
13
|
-
* percepts sleep continues.
|
|
13
|
+
* percepts sleep continues. Alarm timestamps must be within the current doze span
|
|
14
|
+
* through p.now; delayed interval delivery can wake. Other delivered percepts still
|
|
15
|
+
* pass through ordinary bounded attention, as during voluntary sleep. The community driver checks percepts on its ordinary idle cadence.
|
|
14
16
|
* Enabled tick integration uses one-minute boundaries even with closed gates or during sleep;
|
|
15
17
|
* cost is O(elapsed minutes) and continuous state can differ from the absent opt-in arm.
|
|
16
18
|
* Closed gates draw no onset RNG; passive community checkpoints do not call choice.
|
|
17
|
-
* Activity.dozedAt pins stopped work until the host resolves it, even after an alarm;
|
|
19
|
+
* Activity.dozedAt pins stopped work until the host resolves it with finish, even after an alarm;
|
|
20
|
+
* begin refuses all replacements while that pin remains;
|
|
18
21
|
* the pending interrupt reason may retain an earlier host/percept cause.
|
|
19
22
|
*/
|
|
20
23
|
export declare function enableDoze(p: Person, input: DozeContext): void;
|
package/dist/body/doze.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SCOPE: opt-in minute-grid involuntary onset and
|
|
2
|
+
* SCOPE: opt-in minute-grid involuntary onset and interval-delivered urgent-percept waking. Cole 1989
|
|
3
3
|
* (PMID 2606843) supports posture context; auditory arousal studies (PMIDs 8776786,
|
|
4
4
|
* 8153254) distinguish arousal from attention. None calibrates these engineering gates,
|
|
5
5
|
* hazard weights or salience. No stages, inertia, spontaneous wake or clinical prediction.
|
|
@@ -24,13 +24,16 @@ function context(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Enable involuntary doze without drawing RNG. Idempotent; off when absent.
|
|
27
|
-
* Hosts must deliver
|
|
27
|
+
* Hosts must deliver detectable heard/felt/social alarms with salience >= 0.8
|
|
28
28
|
* to this person (targetId, or near: true). There is no spontaneous wake: without such
|
|
29
|
-
* percepts sleep continues.
|
|
29
|
+
* percepts sleep continues. Alarm timestamps must be within the current doze span
|
|
30
|
+
* through p.now; delayed interval delivery can wake. Other delivered percepts still
|
|
31
|
+
* pass through ordinary bounded attention, as during voluntary sleep. The community driver checks percepts on its ordinary idle cadence.
|
|
30
32
|
* Enabled tick integration uses one-minute boundaries even with closed gates or during sleep;
|
|
31
33
|
* cost is O(elapsed minutes) and continuous state can differ from the absent opt-in arm.
|
|
32
34
|
* Closed gates draw no onset RNG; passive community checkpoints do not call choice.
|
|
33
|
-
* Activity.dozedAt pins stopped work until the host resolves it, even after an alarm;
|
|
35
|
+
* Activity.dozedAt pins stopped work until the host resolves it with finish, even after an alarm;
|
|
36
|
+
* begin refuses all replacements while that pin remains;
|
|
34
37
|
* the pending interrupt reason may retain an earlier host/percept cause.
|
|
35
38
|
*/
|
|
36
39
|
export function enableDoze(p, input) {
|
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: one conversation turn between two people: what the speaker tells the listener, drawn from the
|
|
3
|
+
* speaker's own confident beliefs, and what advice they give, drawn from the speaker's beliefs about what the
|
|
4
|
+
* listener should do. Output is plain data: `told` percepts (testimony with claims at the speaker's credence)
|
|
5
|
+
* and advice percepts that the host delivers through the listener's `perceive`, a `Suggestion` per piece of
|
|
6
|
+
* advice for the listener's next decision, and social events for both sides. Reputation is nothing more than
|
|
7
|
+
* these claims spreading belief by belief (`social.applyReputationBelief` turns a changed character belief
|
|
8
|
+
* into a changed relationship).
|
|
9
|
+
*
|
|
10
|
+
* Choice of what to say is deterministic and explainable: each candidate claim gets a relevance score
|
|
11
|
+
* (confidence, recency, host topics, the speaker's feeling about its subject); an unfavourable claim about an
|
|
12
|
+
* absent third party is tagged with the catalog norm 'backbiting' (Qur'an 49:12, as catalogued in
|
|
13
|
+
* `conscience/catalog.ts`) and costs the speaker in proportion to their OWN held understanding of that norm and
|
|
14
|
+
* their honesty-humility, so a speaker who holds it firmly withholds it and one who does not tells it. The
|
|
15
|
+
* definition used here (an unfavourable character claim about someone not present) is an engineering
|
|
16
|
+
* assumption for the tag, not a ruling on what counts as backbiting. Deceit happens only when the host offers a
|
|
17
|
+
* temptation (the speaker gains if the listener believes something the speaker does not): temptation scales
|
|
18
|
+
* with (1 − honesty-humility) and is opposed by the speaker's held 'lying' norm. The lie is a host-visible flag
|
|
19
|
+
* (`ToldClaim.deceit`); the listener cannot see it. Named shapes: honesty-humility as the HEXACO factor most
|
|
20
|
+
* predictive of lower dishonesty (Hilbig & Zettler 2009; Heck et al. 2018 meta-analysis), gossip as a large
|
|
21
|
+
* share of conversation about absent third parties that spreads reputational information (Dunbar 2004), and
|
|
22
|
+
* extraversion as talkativeness (how many claims are offered).
|
|
23
|
+
*
|
|
24
|
+
* SCOPE (host options): `loyalWarning` admits only unfavourable listener claims when affection is at least
|
|
25
|
+
* 0.5 (an engineering threshold). `confidentMin` sets a per-call relay threshold. `phrase` renders selected
|
|
26
|
+
* testimony only; the host owns authored facts, delivery and subsequent disclosure. No hidden relay occurs.
|
|
27
|
+
*
|
|
28
|
+
* It does NOT generate language, model turn-taking, persuasion beyond source trust, what the speaker believes
|
|
29
|
+
* the listener already knows, lies of omission, white lies told to spare feelings, or the speaker's fear of
|
|
30
|
+
* being found out. Coefficients are engineering defaults, not calibrated estimates. Nothing here judges the
|
|
31
|
+
* worth of either person; norms are the people's own understandings with catalog provenance.
|
|
32
|
+
* Repetition (integration, 2026-10-03): a claim the listener already holds at the told confidence produces no
|
|
33
|
+
* percept (it is in `claims`, not `told`). A claim this listener already heard from this speaker, in the same
|
|
34
|
+
* 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
|
|
35
|
+
* belief stands in for it.
|
|
36
|
+
*
|
|
37
|
+
* SCOPE (Word and witness, 2.10.0): `provenance: true` carries each claim's first known origin and opaque
|
|
38
|
+
* lineage into testimony. Missing provenance names the speaker as origin. Metadata changes neither claim
|
|
39
|
+
* selection nor repetition rules; the host still owns the relay graph and disclosure. It does not establish
|
|
40
|
+
* that an origin is truthful, expose deceit to the listener, or infer who else heard a claim.
|
|
41
|
+
*/
|
|
1
42
|
import { type TraitEffect } from '../social/index.ts';
|
|
2
43
|
import type { EntityId, Minute, NormTag, Percept, Person, PersonId, SocialEvent, Suggestion, Unit } from '../types.ts';
|
|
3
44
|
export declare const CONVERSATION_DEFAULTS: {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* that an origin is truthful, expose deceit to the listener, or infer who else heard a claim.
|
|
41
41
|
*/
|
|
42
42
|
import { clamp01, expit, round } from '../core/index.js';
|
|
43
|
+
import { nameOf as listenerNameOf } from '../narrate/lexicon.js';
|
|
43
44
|
import { isDeceased, isUnfavourableTrait, parseTraitProp, REPUTATION_TRAITS, relationshipWith, } from '../social/index.js';
|
|
44
45
|
import { MINUTES_PER_DAY } from '../types.js';
|
|
45
46
|
export const CONVERSATION_DEFAULTS = {
|
|
@@ -244,10 +245,10 @@ export function converse(speaker, listener, ctx) {
|
|
|
244
245
|
actorId: speaker.id,
|
|
245
246
|
salience: round(0.3 + 0.4 * c.confidence),
|
|
246
247
|
claims: [{ prop: c.prop, value: c.value, confidence: c.confidence }],
|
|
247
|
-
summary:
|
|
248
|
+
summary: listener.lexicon?.plainEnglish
|
|
248
249
|
? c.about !== undefined
|
|
249
|
-
? `${nameOf(speaker)} says ${
|
|
250
|
-
: `${nameOf(speaker)} says this is ${c.value ? 'true' : 'false'}: ${c.prop}.`
|
|
250
|
+
? `${listener.lexicon.names?.[speaker.id] ?? nameOf(speaker)} says ${listenerNameOf(listener, c.about)} ${c.prop.split(':')[1] === 'owes' ? `${c.value ? 'owes' : 'does not owe'} money to ${listenerNameOf(listener, c.prop.split(':').slice(2).join(':'))}` : `is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`}.`
|
|
251
|
+
: `${listener.lexicon.names?.[speaker.id] ?? nameOf(speaker)} says this is ${c.value ? 'true' : 'false'}: ${c.prop}.`
|
|
251
252
|
: c.about !== undefined
|
|
252
253
|
? `${nameOf(speaker)} says ${c.about} is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`
|
|
253
254
|
: `${nameOf(speaker)} says ${c.prop} is ${c.value ? 'so' : 'not so'}`,
|
|
@@ -297,8 +298,8 @@ export function converse(speaker, listener, ctx) {
|
|
|
297
298
|
valence: 0.1,
|
|
298
299
|
salience: round(0.4 + 0.3 * a.strength),
|
|
299
300
|
claims: [{ prop: `advice:${a.action}`, value: true, confidence: round(a.cred) }],
|
|
300
|
-
summary:
|
|
301
|
-
? `${nameOf(speaker)} suggests this action: ${a.action.replace(/[-_]/g, ' ')}.`
|
|
301
|
+
summary: listener.lexicon?.plainEnglish
|
|
302
|
+
? `${listener.lexicon.names?.[speaker.id] ?? nameOf(speaker)} suggests this action: ${a.action.replace(/[-_]/g, ' ')}.`
|
|
302
303
|
: `${nameOf(speaker)} says I should ${a.action}`,
|
|
303
304
|
};
|
|
304
305
|
if (ctx.provenance) {
|
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.23.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';
|
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.23.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';
|
package/dist/migrate.js
CHANGED
|
@@ -41,6 +41,11 @@ function renameAmbient(json) {
|
|
|
41
41
|
}
|
|
42
42
|
/** The chain, keyed by the version each step upgrades from. */
|
|
43
43
|
export const MIGRATIONS = {
|
|
44
|
+
'2.21.0': {
|
|
45
|
+
to: '2.23.0',
|
|
46
|
+
note: 'shape unchanged; doze delivery and plain narration fixes apply to opted-in runs',
|
|
47
|
+
apply: stamp,
|
|
48
|
+
},
|
|
44
49
|
'2.17.0': {
|
|
45
50
|
to: '2.21.0',
|
|
46
51
|
note: 'doze and bladder state start absent; old callers retain their continuation',
|
package/dist/narrate/narrate.js
CHANGED
|
@@ -332,7 +332,9 @@ export function describePerson(p, lexicon) {
|
|
|
332
332
|
? `${p.activity.action}${p.activity.targetId ? ` (${p.activity.targetId})` : ''}`
|
|
333
333
|
: phraseLine('describe.idle', {}, lex);
|
|
334
334
|
if (lex?.plainEnglish) {
|
|
335
|
-
const state = bits.length >
|
|
335
|
+
const state = bits.length > 1
|
|
336
|
+
? `${bits.slice(0, -1).join(', ')} and ${bits.at(-1)}`
|
|
337
|
+
: (bits[0] ?? phraseLine('describe.well', {}, lex));
|
|
336
338
|
const activity = p.activity
|
|
337
339
|
? `${p.name} is doing this: ${p.activity.affordance.label}${p.activity.targetId ? ` (${nameOf(p, p.activity.targetId, lex)})` : ''}.`
|
|
338
340
|
: `${p.name} is ${phraseLine('describe.idle', {}, lex)}.`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SCOPE: opt-in composition of beliefs.confirm and social.applyReputationBelief, in that order.
|
|
3
|
-
* Owns no state or formula. This
|
|
3
|
+
* Owns no state or formula. This host composition handles observed-trait correction: direct observation corrects the belief,
|
|
4
4
|
* recalibrates its sources and applies the credence change to the subject's relationship. It does
|
|
5
5
|
* not infer opposite traits, accuse a source of lying, run attention or record a percept. Hosts
|
|
6
6
|
* supply only observations their person has access to. Existing callers remain unchanged.
|
package/dist/person.d.ts
CHANGED
|
@@ -196,7 +196,11 @@ export interface BeginOptions {
|
|
|
196
196
|
/** Joint-activity proposal id (set by `sim/` when both partners begin together). */
|
|
197
197
|
jointId?: string;
|
|
198
198
|
}
|
|
199
|
-
/**
|
|
199
|
+
/**
|
|
200
|
+
* Start an activity chosen by `record`. Returns null when dead, or when dozed work
|
|
201
|
+
* is unresolved. Call `finish` for the stopped activity before starting any replacement,
|
|
202
|
+
* including voluntary sleep or an activity chosen after a waking percept.
|
|
203
|
+
*/
|
|
200
204
|
export declare function begin(p: Person, aff: Affordance, record: DecisionRecord, opts?: BeginOptions): Activity | null;
|
|
201
205
|
/**
|
|
202
206
|
* Push the review time forward after a decision to continue the current activity, refresh the cached
|
|
@@ -229,9 +233,13 @@ export declare function learningFor(p: Person, domain?: LearningDomain): number;
|
|
|
229
233
|
/**
|
|
230
234
|
* Watch someone practise skill `skill` at `modelLevel` for `minutes` (observational learning, 1.8.0): the
|
|
231
235
|
* composite's side of `skills.learnByWatching` at the person's age-and-domain learning rate. A `Percept.demonstrates` on an
|
|
232
|
-
* attended percept does the same.
|
|
236
|
+
* attended percept does the same. `inheritedAptitude: true` applies the family's existing aptitude factor to
|
|
237
|
+
* watching. Absent or false keeps earlier observation learning, including for a person with an authored family.
|
|
238
|
+
* This changes no practice rule, draws no RNG, and stores no new state. Returns the level before and after.
|
|
233
239
|
*/
|
|
234
|
-
export declare function observeSkill(p: Person, skill: string, minutes: number, modelLevel: Unit, domain?: LearningDomain
|
|
240
|
+
export declare function observeSkill(p: Person, skill: string, minutes: number, modelLevel: Unit, domain?: LearningDomain, options?: {
|
|
241
|
+
inheritedAptitude?: boolean;
|
|
242
|
+
}): {
|
|
235
243
|
before: Unit;
|
|
236
244
|
after: Unit;
|
|
237
245
|
};
|
package/dist/person.js
CHANGED
|
@@ -748,7 +748,8 @@ function perceiveOne(p, pc, options) {
|
|
|
748
748
|
// Watching someone skilled at work teaches a little (1.8.0, observational learning).
|
|
749
749
|
const demo = pc.demonstrates;
|
|
750
750
|
if (demo && !byMe && demo.minutes > 0) {
|
|
751
|
-
|
|
751
|
+
const learning = learningFor(p, demo.domain);
|
|
752
|
+
learnByWatching(p, demo.skill, demo.minutes, demo.level, demo.inheritedAptitude === true ? learning * aptitudeOf(p, demo.skill) : learning, p.now);
|
|
752
753
|
}
|
|
753
754
|
// Relationships: interactions I took part in.
|
|
754
755
|
const social = socialKindOf(pc.kind);
|
|
@@ -864,17 +865,18 @@ function judgeDeed(p, actor, pc, at) {
|
|
|
864
865
|
export function perceive(p, percepts, options = {}) {
|
|
865
866
|
if (percepts.length === 0 || !p.body.alive)
|
|
866
867
|
return [];
|
|
867
|
-
|
|
868
|
+
const dozeSince = p.body.doze?.since;
|
|
869
|
+
if (dozeSince !== undefined) {
|
|
868
870
|
const wakes = percepts.some((pc) => Number.isFinite(pc.at) &&
|
|
869
|
-
pc.at
|
|
871
|
+
pc.at >= dozeSince &&
|
|
872
|
+
pc.at <= p.now &&
|
|
870
873
|
Number.isFinite(pc.salience) &&
|
|
871
874
|
pc.salience >= DOZE_DEFAULTS.wakeSalience &&
|
|
872
875
|
pc.near !== false &&
|
|
873
876
|
(pc.targetId === p.id || pc.near === true) &&
|
|
874
877
|
(pc.channel === 'heard' || pc.channel === 'felt' || pc.channel === 'social'));
|
|
875
|
-
if (
|
|
876
|
-
|
|
877
|
-
wakeDoze(p, p.now);
|
|
878
|
+
if (wakes)
|
|
879
|
+
wakeDoze(p, p.now);
|
|
878
880
|
}
|
|
879
881
|
const body = readBodyOf(p);
|
|
880
882
|
const attended = attend(p, [...percepts], {
|
|
@@ -1169,9 +1171,13 @@ function thresholdFor(p, load, review) {
|
|
|
1169
1171
|
const t = nextBodyThreshold(p, load, segmentModifiers(p), PERSON_DEFAULTS.interruptThresholds, review, p.ambient ? ambientBodyParams(p, BODY_DEFAULTS) : BODY_DEFAULTS, fastingCtx(p));
|
|
1170
1172
|
return Number.isFinite(t) ? p.now + t : undefined;
|
|
1171
1173
|
}
|
|
1172
|
-
/**
|
|
1174
|
+
/**
|
|
1175
|
+
* Start an activity chosen by `record`. Returns null when dead, or when dozed work
|
|
1176
|
+
* is unresolved. Call `finish` for the stopped activity before starting any replacement,
|
|
1177
|
+
* including voluntary sleep or an activity chosen after a waking percept.
|
|
1178
|
+
*/
|
|
1173
1179
|
export function begin(p, aff, record, opts = {}) {
|
|
1174
|
-
if (!p.body.alive)
|
|
1180
|
+
if (!p.body.alive || p.activity?.dozedAt !== undefined)
|
|
1175
1181
|
return null;
|
|
1176
1182
|
if (p.body.doze?.since !== undefined && aff.mode !== 'sleep')
|
|
1177
1183
|
return null;
|
|
@@ -1346,10 +1352,13 @@ export function learningFor(p, domain) {
|
|
|
1346
1352
|
/**
|
|
1347
1353
|
* Watch someone practise skill `skill` at `modelLevel` for `minutes` (observational learning, 1.8.0): the
|
|
1348
1354
|
* composite's side of `skills.learnByWatching` at the person's age-and-domain learning rate. A `Percept.demonstrates` on an
|
|
1349
|
-
* attended percept does the same.
|
|
1355
|
+
* attended percept does the same. `inheritedAptitude: true` applies the family's existing aptitude factor to
|
|
1356
|
+
* watching. Absent or false keeps earlier observation learning, including for a person with an authored family.
|
|
1357
|
+
* This changes no practice rule, draws no RNG, and stores no new state. Returns the level before and after.
|
|
1350
1358
|
*/
|
|
1351
|
-
export function observeSkill(p, skill, minutes, modelLevel, domain) {
|
|
1352
|
-
|
|
1359
|
+
export function observeSkill(p, skill, minutes, modelLevel, domain, options) {
|
|
1360
|
+
const learning = learningFor(p, domain);
|
|
1361
|
+
return learnByWatching(p, skill, minutes, modelLevel, options?.inheritedAptitude === true ? learning * aptitudeOf(p, skill) : learning, p.now);
|
|
1353
1362
|
}
|
|
1354
1363
|
/** Realized need changes, using readings captured before and after the outcome's deltas. */
|
|
1355
1364
|
function realizedNeedDeltas(act, given, before, after) {
|
package/dist/persona.js
CHANGED
|
@@ -188,6 +188,7 @@ export function validatePersonaProjection(input) {
|
|
|
188
188
|
keys(input.source, ['schema', 'engine', 'id', 'now']);
|
|
189
189
|
// The 2.14.1 and 2.17.0 portable fields retain their semantics across the bodily opt-ins.
|
|
190
190
|
const compatibleEngine = input.source.engine === ENGINE_VERSION ||
|
|
191
|
+
input.source.engine === '2.21.0' ||
|
|
191
192
|
input.source.engine === '2.17.0' ||
|
|
192
193
|
input.source.engine === '2.14.1';
|
|
193
194
|
if (input.source.schema !== PERSON_SCHEMA || !compatibleEngine)
|
package/dist/presentation.js
CHANGED
|
@@ -68,6 +68,7 @@ export function reasonLine(reason, lexicon) {
|
|
|
68
68
|
'need:water': 'I need to drink first.',
|
|
69
69
|
'need:sleep': 'I need to sleep first.',
|
|
70
70
|
'need:rest': 'I need to rest first.',
|
|
71
|
+
'need:bladder': 'I need to use the toilet first.',
|
|
71
72
|
'need:survival': 'I need to meet an urgent bodily need first.',
|
|
72
73
|
};
|
|
73
74
|
if (Object.hasOwn(lines, reason))
|
|
@@ -360,7 +360,7 @@ export function nafsPatterns(p, inputs) {
|
|
|
360
360
|
ref: 'Qur’an 12:53',
|
|
361
361
|
evident: assentedAgainstNorm.length > 0,
|
|
362
362
|
evidence: p.lexicon?.plainEnglish
|
|
363
|
-
? `The person acted on ${assentedAgainstNorm.length} recent urges despite a rule they hold.`
|
|
363
|
+
? `The person acted on ${assentedAgainstNorm.length} recent ${assentedAgainstNorm.length === 1 ? 'urge' : 'urges'} despite a rule they hold.`
|
|
364
364
|
: `${assentedAgainstNorm.length} recent prompts followed against a held norm`,
|
|
365
365
|
},
|
|
366
366
|
{
|
|
@@ -376,7 +376,7 @@ export function nafsPatterns(p, inputs) {
|
|
|
376
376
|
ref: 'Qur’an 89:27',
|
|
377
377
|
evident: letPass > 0 && assentedAgainstNorm.length === 0 && !reproach,
|
|
378
378
|
evidence: p.lexicon?.plainEnglish
|
|
379
|
-
? `${letPass} recent urges passed without action. ${assentedAgainstNorm.length} were acted on against a held rule. Current guilt or unresolved breach: ${reproach ? 'yes' : 'no'}.`
|
|
379
|
+
? `${letPass} recent ${letPass === 1 ? 'urge' : 'urges'} passed without action. ${assentedAgainstNorm.length} ${assentedAgainstNorm.length === 1 ? 'was' : 'were'} acted on against a held rule. Current guilt or unresolved breach: ${reproach ? 'yes' : 'no'}.`
|
|
380
380
|
: `${letPass} recent prompts let pass, none followed against a norm, no felt reproach`,
|
|
381
381
|
},
|
|
382
382
|
];
|
package/dist/qalb.d.ts
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SCOPE: a read-only narration lens over retained attention evidence, held norms and conviction,
|
|
3
|
-
* mood and the three distinct trust stores. Owns no state and supplies no decision input. It does
|
|
4
|
-
* not infer spiritual health, rank people, judge norms, measure the ruh or compute divine acceptance.
|
|
5
|
-
* Retained memories are evidence available for narration, not a complete attention history.
|
|
6
|
-
*
|
|
7
|
-
* Provenance: research/reconciliation.md §2–4, §7 (Q7: Qur'an 22:46; H9: Bukhari 52 /
|
|
8
|
-
* Muslim 1599a); research/islamic-foundations.md §2 (qalb as an overlapping interpretive view);
|
|
9
|
-
* research/decisions.md, “Qalb, nafs, fitra, ʿaql as state”. These support the lens, not a
|
|
10
|
-
* diagnostic mapping. Sign/zero categories below are engineering descriptions of stored values,
|
|
11
|
-
* not religious categories. Mood and interpersonal trust imply nothing about faith.
|
|
12
|
-
*/
|
|
13
1
|
import type { Person } from './types.ts';
|
|
14
2
|
export interface QalbDescription {
|
|
15
3
|
facet: 'attention' | 'held-norm' | 'mood' | 'trust';
|
package/dist/qalb.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: a read-only narration lens over retained attention evidence, held norms and conviction,
|
|
3
|
+
* mood and the three distinct trust stores. Owns no state and supplies no decision input. It does
|
|
4
|
+
* not infer spiritual health, rank people, judge norms, measure the ruh or compute divine acceptance.
|
|
5
|
+
* Retained memories are evidence available for narration, not a complete attention history.
|
|
6
|
+
*
|
|
7
|
+
* Provenance: research/reconciliation.md §2–4, §7 (Q7: Qur'an 22:46; H9: Bukhari 52 /
|
|
8
|
+
* Muslim 1599a); research/islamic-foundations.md §2 (qalb as an overlapping interpretive view);
|
|
9
|
+
* research/decisions.md, “Qalb, nafs, fitra, ʿaql as state”. These support the lens, not a
|
|
10
|
+
* diagnostic mapping. Sign/zero categories below are engineering descriptions of stored values,
|
|
11
|
+
* not religious categories. Mood and interpersonal trust imply nothing about faith.
|
|
12
|
+
*/
|
|
13
|
+
import { DEFAULT_NORMS } from './conscience/catalog.js';
|
|
14
|
+
import { nameOf } from './narrate/lexicon.js';
|
|
15
|
+
import { normLabel } from './presentation.js';
|
|
1
16
|
/**
|
|
2
17
|
* Plain English descriptions; no scalar summary, comparison or hidden update.
|
|
3
18
|
* Hosts must not present these records as spiritual health or a religious assessment.
|
|
@@ -19,18 +34,17 @@ export function qalbReading(p) {
|
|
|
19
34
|
if (!p.conscience.norms.length)
|
|
20
35
|
add('held-norm', 'unrecorded', 'Their records contain no held rules.', ['conscience.norms']);
|
|
21
36
|
for (const n of p.conscience.norms) {
|
|
37
|
+
const rule = normLabel(DEFAULT_NORMS.find((rule) => rule.id === n.normId) ?? { id: n.normId, label: n.normId }, { ...p.lexicon, plainEnglish: true });
|
|
22
38
|
add('held-norm', n.conviction === 0 ? 'no-conviction' : 'held-understanding', n.conviction === 0
|
|
23
|
-
?
|
|
24
|
-
:
|
|
39
|
+
? `They have a recorded rule, “${rule}”, with no stated conviction.`
|
|
40
|
+
: `They hold an understanding of the rule “${rule}”. This is their view.`, [`norm:${n.normId}`, `standing:${n.standing}`, 'conscience.norms.conviction']);
|
|
25
41
|
}
|
|
26
42
|
add('mood', p.affect.mood.valence < 0 ? 'unpleasant' : p.affect.mood.valence > 0 ? 'pleasant' : 'neutral', p.affect.mood.valence < 0
|
|
27
43
|
? 'Their recorded mood feels unpleasant.'
|
|
28
44
|
: p.affect.mood.valence > 0
|
|
29
45
|
? 'Their recorded mood feels pleasant.'
|
|
30
46
|
: 'Their recorded mood feels neutral.', ['affect.mood.valence']);
|
|
31
|
-
add('mood', p.affect.mood.arousal === 0 ? 'no-arousal' : 'some-arousal', p.affect.mood.arousal === 0
|
|
32
|
-
? 'Their recorded mood has no arousal.'
|
|
33
|
-
: 'Their recorded mood has some arousal.', ['affect.mood.arousal']);
|
|
47
|
+
add('mood', p.affect.mood.arousal === 0 ? 'no-arousal' : 'some-arousal', p.affect.mood.arousal === 0 ? 'Their recorded mood is calm.' : 'Their recorded mood is stirred.', ['affect.mood.arousal']);
|
|
34
48
|
// Keep stores distinct. Do not substitute relationship trust for source or advising-voice trust.
|
|
35
49
|
const ties = p.social.relationships.map((r) => ({
|
|
36
50
|
store: 'social.relationships',
|
|
@@ -53,10 +67,10 @@ export function qalbReading(p) {
|
|
|
53
67
|
for (const t of trusts) {
|
|
54
68
|
// 0.5 is the numeric range midpoint, not a faith threshold or personal baseline.
|
|
55
69
|
const subject = t.store === 'social.relationships'
|
|
56
|
-
?
|
|
70
|
+
? `the person ${nameOf(p, t.id)}`
|
|
57
71
|
: t.store === 'will.voices'
|
|
58
|
-
?
|
|
59
|
-
:
|
|
72
|
+
? `the advising voice ${nameOf(p, t.id)}`
|
|
73
|
+
: `the information source ${nameOf(p, t.id)}`;
|
|
60
74
|
add('trust', t.trust < 0.5 ? 'below-midpoint' : t.trust > 0.5 ? 'above-midpoint' : 'at-midpoint', t.trust < 0.5
|
|
61
75
|
? `Their trust in ${subject} is below the middle of its range.`
|
|
62
76
|
: t.trust > 0.5
|
package/dist/sim/sim.js
CHANGED
|
@@ -840,10 +840,15 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
840
840
|
at: t,
|
|
841
841
|
personId: p.id,
|
|
842
842
|
kind: 'finish',
|
|
843
|
-
detail:
|
|
843
|
+
detail: outcome.summary ??
|
|
844
|
+
(p.lexicon?.plainEnglish
|
|
845
|
+
? `${p.name} stopped the task “${act.affordance.label}” because they dozed off.`
|
|
846
|
+
: `${act.action} interrupted by doze`),
|
|
847
|
+
decisionId: act.decisionId,
|
|
848
|
+
affordanceId: act.affordanceId,
|
|
844
849
|
action: act.action,
|
|
845
850
|
status: outcome.status,
|
|
846
|
-
});
|
|
851
|
+
}, outcome.summary !== undefined);
|
|
847
852
|
}
|
|
848
853
|
c.idleUntil[p.id] = t + quantum;
|
|
849
854
|
if (!ordinaryDue)
|
|
@@ -995,12 +1000,12 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
995
1000
|
detail: p.lexicon?.plainEnglish
|
|
996
1001
|
? proposal.status === 'declined'
|
|
997
1002
|
? `The shared task “${chosen.label}” could not start because a partner was unavailable.`
|
|
998
|
-
: `${chosen.label} with ${proposal.partnerIds.map((id) => personById(c, id)?.name ?? id).join(', ')}
|
|
1003
|
+
: `${p.name} proposed the shared task “${chosen.label}” with ${proposal.partnerIds.map((id) => personById(c, id)?.name ?? id).join(', ')}.`
|
|
999
1004
|
: `${chosen.label} with ${proposal.partnerIds.join(', ')}`,
|
|
1000
1005
|
affordanceId: chosen.id,
|
|
1001
1006
|
action: chosen.action,
|
|
1002
1007
|
jointId: proposal.id,
|
|
1003
|
-
}, Boolean(p.lexicon?.plainEnglish
|
|
1008
|
+
}, Boolean(p.lexicon?.plainEnglish));
|
|
1004
1009
|
continue;
|
|
1005
1010
|
}
|
|
1006
1011
|
if (act) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCOPE: opt-in host input ordering for one declared logical tick. Waits for the
|
|
3
|
+
* caller's complete expected identity set, then sorts by sender and sequence.
|
|
4
|
+
* Lexical sender priority is deterministic host policy, not fairness or
|
|
5
|
+
* consensus. For conflicting same-voice requests, earlier senders take priority.
|
|
6
|
+
* Owns no Person, clock, queue or RNG state. Does not supply transport, consensus,
|
|
7
|
+
* payload validation, retries or snapshot storage. Payloads remain caller-owned;
|
|
8
|
+
* hosts must agree on their meaning and save pending input alongside host state.
|
|
9
|
+
*/
|
|
10
|
+
export interface TickInputKey {
|
|
11
|
+
sender: string;
|
|
12
|
+
sequence: number;
|
|
13
|
+
}
|
|
14
|
+
export interface TickInput<T> extends TickInputKey {
|
|
15
|
+
tick: number;
|
|
16
|
+
payload: T;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Returns undefined while a declared input is missing. Unexpected, duplicate or
|
|
20
|
+
* wrong-tick inputs throw. An empty expected set is a complete barrier. This
|
|
21
|
+
* reader never mutates input arrays or envelopes and never reads payload fields.
|
|
22
|
+
* Call only when explicitly opting into this host scheduling convention.
|
|
23
|
+
*/
|
|
24
|
+
export declare function canonicalTickInputs<T>(tick: number, inputs: readonly TickInput<T>[], expected: readonly TickInputKey[]): TickInput<T>[] | undefined;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const MAX_INPUTS = 256;
|
|
2
|
+
function identity(key) {
|
|
3
|
+
if (typeof key.sender !== 'string' ||
|
|
4
|
+
key.sender.length === 0 ||
|
|
5
|
+
key.sender.length > 128 ||
|
|
6
|
+
!Number.isSafeInteger(key.sequence) ||
|
|
7
|
+
key.sequence < 0) {
|
|
8
|
+
throw new Error('Invalid tick input identity');
|
|
9
|
+
}
|
|
10
|
+
return JSON.stringify([key.sender, key.sequence]);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Returns undefined while a declared input is missing. Unexpected, duplicate or
|
|
14
|
+
* wrong-tick inputs throw. An empty expected set is a complete barrier. This
|
|
15
|
+
* reader never mutates input arrays or envelopes and never reads payload fields.
|
|
16
|
+
* Call only when explicitly opting into this host scheduling convention.
|
|
17
|
+
*/
|
|
18
|
+
export function canonicalTickInputs(tick, inputs, expected) {
|
|
19
|
+
if (!Number.isSafeInteger(tick) || tick < 0)
|
|
20
|
+
throw new Error('Invalid logical tick');
|
|
21
|
+
if (inputs.length > MAX_INPUTS || expected.length > MAX_INPUTS) {
|
|
22
|
+
throw new Error('Tick input limit exceeded');
|
|
23
|
+
}
|
|
24
|
+
const wanted = new Set();
|
|
25
|
+
for (const key of expected) {
|
|
26
|
+
const id = identity(key);
|
|
27
|
+
if (wanted.has(id))
|
|
28
|
+
throw new Error('Duplicate expected tick input');
|
|
29
|
+
wanted.add(id);
|
|
30
|
+
}
|
|
31
|
+
const received = new Set();
|
|
32
|
+
for (const input of inputs) {
|
|
33
|
+
const id = identity(input);
|
|
34
|
+
if (input.tick !== tick)
|
|
35
|
+
throw new Error('Input belongs to another tick');
|
|
36
|
+
if (!wanted.has(id))
|
|
37
|
+
throw new Error('Unexpected tick input');
|
|
38
|
+
if (received.has(id))
|
|
39
|
+
throw new Error('Duplicate tick input');
|
|
40
|
+
received.add(id);
|
|
41
|
+
}
|
|
42
|
+
if (received.size !== wanted.size)
|
|
43
|
+
return undefined;
|
|
44
|
+
return inputs
|
|
45
|
+
.map((input) => ({
|
|
46
|
+
tick: input.tick,
|
|
47
|
+
sender: input.sender,
|
|
48
|
+
sequence: input.sequence,
|
|
49
|
+
payload: input.payload,
|
|
50
|
+
}))
|
|
51
|
+
.sort((a, b) => {
|
|
52
|
+
if (a.sender !== b.sender)
|
|
53
|
+
return a.sender < b.sender ? -1 : 1;
|
|
54
|
+
return a.sequence - b.sequence;
|
|
55
|
+
});
|
|
56
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -915,7 +915,7 @@ export declare const PERSON_SCHEMA = "human/person@1";
|
|
|
915
915
|
* and an activity that would cover a closing stretch is reviewed when the stretch begins (`isUnderWay`,
|
|
916
916
|
* `dutyReviewAt`). Person shape unchanged.
|
|
917
917
|
*/
|
|
918
|
-
export declare const ENGINE_VERSION = "2.
|
|
918
|
+
export declare const ENGINE_VERSION = "2.23.0";
|
|
919
919
|
/** A pregnancy carried by this person (host-driven; see `family/`). */
|
|
920
920
|
export interface Pregnancy {
|
|
921
921
|
fatherId: PersonId;
|
|
@@ -1301,6 +1301,8 @@ export interface Percept {
|
|
|
1301
1301
|
level: Unit;
|
|
1302
1302
|
minutes: number;
|
|
1303
1303
|
domain?: LearningDomain;
|
|
1304
|
+
/** Apply the watcher's inherited skill aptitude. Absent keeps earlier observation learning exactly. */
|
|
1305
|
+
inheritedAptitude?: boolean;
|
|
1304
1306
|
};
|
|
1305
1307
|
/** At most four additions/withdrawals combined; attended external testimony to this person or a broadcast. */
|
|
1306
1308
|
consequences?: readonly ReceivedConsequence[];
|
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.23.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adam0white/human-framework",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.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",
|