@adam0white/human-framework 2.2.1 → 2.4.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 +99 -0
- package/dist/agenda/agenda.js +45 -0
- package/dist/cognition/cognition.d.ts +3 -0
- package/dist/cognition/cognition.js +59 -21
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -1
- package/dist/migrate.js +11 -1
- package/dist/person.d.ts +6 -0
- package/dist/person.js +30 -2
- package/dist/promptings/index.d.ts +1 -0
- package/dist/promptings/index.js +1 -0
- package/dist/promptings/promptings.d.ts +100 -0
- package/dist/promptings/promptings.js +348 -0
- package/dist/prospection/index.d.ts +1 -0
- package/dist/prospection/index.js +1 -0
- package/dist/prospection/prospection.d.ts +23 -0
- package/dist/prospection/prospection.js +278 -0
- package/dist/restore.js +6 -0
- package/dist/types.d.ts +117 -3
- package/dist/types.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -123,6 +123,48 @@ The framework has no world of its own. The host supplies three things and receiv
|
|
|
123
123
|
- **Decisions** (`DecisionRecord`) come back: the chosen offer, the top `considered` options with every
|
|
124
124
|
utility term, the suggestion verdict(s), a private `intention`, and a `narration`.
|
|
125
125
|
|
|
126
|
+
### Expected benefits and observed outcomes
|
|
127
|
+
|
|
128
|
+
An offer describes expected effects. An outcome reports what happened. The host resolves space,
|
|
129
|
+
contact, inventory and payment; HF uses the reported outcome to update the person. Do not copy
|
|
130
|
+
`Affordance.advertises` into `Outcome.needs` as a general completion rule.
|
|
131
|
+
|
|
132
|
+
| Host situation | Offer | Completion report |
|
|
133
|
+
|---|---|---|
|
|
134
|
+
| A person sleeps for part of the planned night. | Advertise typical sleep/rest benefits and set `mode: 'sleep'`. | Leave `sleep` and `rest` absent from `Outcome.needs`. `finish` advances the body to the actual end time and measures their change since the activity began. Explicit values replace that measurement; copying the advertisement can overstate the realized recovery reported to learning. |
|
|
135
|
+
| A task promises four coins but actually pays two. | Set expected `Affordance.material` to four host units. | Update the host ledger by two and report `Outcome.material: 2`. A completed task does not imply the promised payment arrived. Report zero for no payment and a negative value for an actual cost. HF does not maintain the host's money ledger. |
|
|
136
|
+
| A person can talk with someone who is present. | Tag the offer `social` when it really involves social contact; put participants in `with`. | Resolve actual participation. An empty room, a sleeping neighbour or someone who arrives after the conversation ends is not a completed encounter. Do not award contact or belonging merely because the activity uses a shared place. |
|
|
137
|
+
|
|
138
|
+
For in-person activities, the host must establish co-presence and overlapping participation. HF has
|
|
139
|
+
no map and does not discover companions. `with` names people taking part, not everyone assigned to a
|
|
140
|
+
place. A `social` tag is also a claim about the offered activity, not a request to find someone. If
|
|
141
|
+
contact becomes impossible, interrupt or replan the activity instead of completing the declared
|
|
142
|
+
encounter. Report only the need effects that the actual interaction supports.
|
|
143
|
+
|
|
144
|
+
The current behavior is defined in [`Affordance` and `Outcome`](src/types.ts) and
|
|
145
|
+
[`finish`](src/person.ts). Existing controls cover [body advancement and waking after sleep](test/person.test.ts),
|
|
146
|
+
[body recovery](test/body.test.ts), [material valuation under scarcity](test/cognition-scarcity.test.ts),
|
|
147
|
+
[actual material in day records](test/chronicle.test.ts), and [relationship-aware social terms](test/social.test.ts).
|
|
148
|
+
These boundaries require correct host reports; they do not imply a framework behavior change.
|
|
149
|
+
|
|
150
|
+
### Scheduled activity reviews
|
|
151
|
+
|
|
152
|
+
A custom host must process `activity.reviewAt`, including during sleep. `tick` advances the person;
|
|
153
|
+
it does not dispatch a decision or automatically end the host's job. An `activity.interrupt` is also
|
|
154
|
+
a request to review, not an instruction to cancel every activity.
|
|
155
|
+
|
|
156
|
+
At a due review, call `decide` with physically feasible alternatives. It includes the current activity
|
|
157
|
+
with its remaining duration. If that activity is chosen again, call `reviewed(p)` and keep its original
|
|
158
|
+
job, arrival time and reservations. If another activity wins, reconcile the old physical job, call
|
|
159
|
+
`finish` with an interrupted outcome, then `begin` the chosen activity with its decision record.
|
|
160
|
+
Do not restart a continued activity or refund its resources. Hard capacity vetoes still apply.
|
|
161
|
+
|
|
162
|
+
Sleep reviews can move earlier for ordinary pending appointments. The appointment's time pressure
|
|
163
|
+
can permit waking; an obligatory norm is not required. A host that ignores the scheduled review can
|
|
164
|
+
make someone miss an obligation HF was ready to reconsider. This does not imply advance planning
|
|
165
|
+
for unavailable future actions. See [`reviewed` and the decision lifecycle](src/person.ts) and
|
|
166
|
+
[`wakeReviewAt` and commitment-based waking](src/will/will.ts).
|
|
167
|
+
|
|
126
168
|
You can drive a person by hand: `decide(p, offers, { suggestion })` → `begin(p, offer, record)` →
|
|
127
169
|
`tick(p, now)` → `finish(p, outcome)`, with `perceive(p, percepts)` and `interrupt(p, now, reason)` between.
|
|
128
170
|
Or implement `World` and let `stepCommunity` sequence everyone in deterministic (time, id) order:
|
|
@@ -302,3 +344,60 @@ clearMethodContext(learner); // when leaving or switching the task
|
|
|
302
344
|
```
|
|
303
345
|
|
|
304
346
|
Import these functions from the package. Rules/evidence live in optional `Person.methods` and survive snapshots. Current cues expire after 120 minutes. A stable demonstration id avoids counting the same example twice within the retained 256-example history, even after its rule or evidence is evicted. Once an id has left both that history and retained rule evidence, it can be admitted again. Missing observations cannot satisfy a condition. Failed demonstrations reduce support for that action. Successful alternatives retain their own support; ordinary cognition weighs the supported offers alongside other motives. A successful lecture does not certify reliable execution. [The routing control](examples/method-control.ts) demonstrates changed teaching, withheld teaching, a scalar-skill control and acquired-state ablation with two methods and an exception. [The alternatives control](examples/method-alternatives-control.ts) demonstrates two independently successful procedures under the same observations, failed practice, hunger and exact restore.
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
## Receiving a possible future route
|
|
350
|
+
|
|
351
|
+
HF 2.3.0 lets an attended explanation connect one current action to one possible successor. The host supplies the explanation the person received, not a private success bonus.
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
import {
|
|
355
|
+
type Affordance, type ReceivedConsequence,
|
|
356
|
+
consequenceEvidence, createPerson, decide, perceive,
|
|
357
|
+
} from '@adam0white/human-framework';
|
|
358
|
+
|
|
359
|
+
const person = createPerson({
|
|
360
|
+
id: 'courier', name: 'Courier', seed: 19, now: 600,
|
|
361
|
+
bornAt: -30 * 365 * 1440, sex: 'female',
|
|
362
|
+
goals: [{
|
|
363
|
+
id: 'delivery', label: 'Deliver the parcel', serves: ['achievement'],
|
|
364
|
+
advancedBy: [{ action: 'deliver', amount: 1 }], importance: 0.8,
|
|
365
|
+
temporal: { halfLifeMinutes: 30 },
|
|
366
|
+
}],
|
|
367
|
+
});
|
|
368
|
+
const route: ReceivedConsequence = {
|
|
369
|
+
id: 'access-window', revision: 0,
|
|
370
|
+
trigger: { action: 'ask-access', targetId: 'keeper' },
|
|
371
|
+
successor: { action: 'deliver', duration: 20, effort: 0.1 },
|
|
372
|
+
availableFrom: 620, availableUntil: 680, expiresAt: 800,
|
|
373
|
+
chance: 0.8, confidence: 0.9,
|
|
374
|
+
};
|
|
375
|
+
perceive(person, [{
|
|
376
|
+
at: 600, channel: 'told', kind: 'explanation', actorId: 'keeper',
|
|
377
|
+
targetId: person.id, salience: 1,
|
|
378
|
+
summary: 'If your request succeeds, this route may become available.',
|
|
379
|
+
consequences: [route],
|
|
380
|
+
}]);
|
|
381
|
+
const request: Affordance = {
|
|
382
|
+
id: 'ask', action: 'ask-access', label: 'Request access', targetId: 'keeper',
|
|
383
|
+
duration: 10, effort: 0.1, advertises: {},
|
|
384
|
+
};
|
|
385
|
+
const offers: Affordance[] = [request,
|
|
386
|
+
{ id: 'direct', action: 'deliver', label: 'Deliver directly',
|
|
387
|
+
duration: 180, effort: 0.1, advertises: {} },
|
|
388
|
+
];
|
|
389
|
+
const decision = decide(person, offers);
|
|
390
|
+
const evidence = consequenceEvidence(person, request); // pure; no RNG consumed
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
`consequences` and `withdrawConsequences: [{ id, revision }]` are accepted only on attended `told` percepts whose explicit `actorId` names someone else. `Percept.targetId` may identify this person or be omitted for a broadcast; testimony explicitly addressed to another recipient is rejected. A recipient-specific opportunity must be addressed to that recipient. Trigger action, target and place match exactly; an omitted target/place matches only an omitted value. `availableUntil` is the latest successor **completion**, inclusive. The whole successor must fit after the current action and `availableFrom`.
|
|
394
|
+
|
|
395
|
+
`Goal.temporal.halfLifeMinutes` must be finite and positive. It enables expected completion valuation for direct actions **as well as** received successors: existing goal value × estimated action success × time discount. Existing `deadline` remains urgency, never a hard cutoff. Keep the same temporal goal in knowledge-intact, withheld and removed-knowledge controls; compare temporal-off separately. Legacy goals retain their prior formula. A received successor serves only goals listing its action in `Goal.advancedBy`; it does not inherit an `Affordance.advances` hint from a current offer.
|
|
396
|
+
|
|
397
|
+
The received access estimate is current-action success × `chance` × `confidence` × current source trust. `chance` means access conditional on successful asking; it does not establish consent. One coherent route may contribute to goals. Expected successor effort and material costs use the same readers as direct actions, are charged once, and remain undiscounted and unscaled by maturity. A favorable explanation need not win against other motives.
|
|
398
|
+
|
|
399
|
+
Only actual terminal outcomes advance terminal goals. Finishing one's own request consumes matching received opportunities even if a refusal has not been heard. It grants no access and executes no successor. The host still decides physical availability, reports the other person's actual response, and filters repeated or impossible requests. Do not mark asking as `advances` merely to make the route attractive.
|
|
400
|
+
|
|
401
|
+
Knowledge lives in optional `Person.prospection` and survives snapshot/restore. A percept admits at most four additions/withdrawals combined, atomically. Invalid entries or duplicate source/id identities reject the entire batch. Equal/lower revisions do not reinforce or refresh; a higher revision replaces. There are at most 16 entries and 64 revision records. Consumed entries are evicted first, then the oldest remaining entries, while their revision records remain; a batch requiring more still-valid revision records is rejected. Entries and revision memory expire within seven days. Deduplication is bounded, not a durable audit. Capacity is shared across sources and admission does not allocate by trust. One prolific source can occupy the finite entry/revision capacity, causing other new ids to be rejected until revision space expires; this is bounded memory, not a source-fairness or trust firewall. Hosts must bound the premises they deliver. The pure query exposes retained entries, present feasibility and retained revisions.
|
|
402
|
+
|
|
403
|
+
[The frozen control](examples/received-consequences.ts) publishes useful, late, costly, uncertain, withheld, removed-knowledge, removed-goal, temporal-off and refused arms in two renamed domains. This faculty does not search action graphs, predict future body state, price all waiting opportunities or guarantee consent or success. Its numerical bounds and control parameters are engineering assumptions.
|
package/dist/agenda/agenda.js
CHANGED
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
// The bundled catalog is only the default; a host with its own catalog passes it (`World.catalog`).
|
|
49
49
|
import { DEFAULT_NORMS } from '../conscience/catalog.js';
|
|
50
50
|
import { clamp01, dayOf, dpow, hasNumbers, isObj } from '../core/index.js';
|
|
51
|
+
import { expectedEffect } from '../memory/index.js';
|
|
51
52
|
import { MINUTES_PER_DAY } from '../types.js';
|
|
52
53
|
export const AGENDA_DEFAULTS = {
|
|
53
54
|
/** Minutes before a window opens during which a commitment already pulls (to get there). */
|
|
@@ -141,8 +142,23 @@ function copyArrays(c) {
|
|
|
141
142
|
* Spec commitments keep a given id; entries without one get a free `c<n>` id after all given ids are placed, so
|
|
142
143
|
* `prayerWindows()` output can be passed straight into `PersonSpec.commitments`.
|
|
143
144
|
*/
|
|
145
|
+
/** Copy valid temporal settings and drop invalid ones at every agenda entry point. */
|
|
146
|
+
function sanitizeGoalTemporal(g) {
|
|
147
|
+
const t = g.temporal;
|
|
148
|
+
if (t !== undefined &&
|
|
149
|
+
!(isObj(t) &&
|
|
150
|
+
typeof t.halfLifeMinutes === 'number' &&
|
|
151
|
+
Number.isFinite(t.halfLifeMinutes) &&
|
|
152
|
+
t.halfLifeMinutes > 0))
|
|
153
|
+
delete g.temporal;
|
|
154
|
+
else if (g.temporal)
|
|
155
|
+
g.temporal = { halfLifeMinutes: g.temporal.halfLifeMinutes };
|
|
156
|
+
}
|
|
144
157
|
/** Restore-time check of `agenda.lapse` (1.7.0), in place: a malformed one is dropped (absent means none). @internal */
|
|
145
158
|
export function sanitizeAgenda(a) {
|
|
159
|
+
for (const g of a.goals)
|
|
160
|
+
if (isObj(g))
|
|
161
|
+
sanitizeGoalTemporal(g);
|
|
146
162
|
const lapse = a.lapse;
|
|
147
163
|
if (lapse !== undefined && !(isObj(lapse) && hasNumbers(lapse, 'since', 'missed')))
|
|
148
164
|
delete a.lapse;
|
|
@@ -177,6 +193,7 @@ export function createAgenda(spec, now) {
|
|
|
177
193
|
lastAdvancedAt: now,
|
|
178
194
|
});
|
|
179
195
|
}
|
|
196
|
+
sanitizeAgenda(state);
|
|
180
197
|
return state;
|
|
181
198
|
}
|
|
182
199
|
export function promise(p, c) {
|
|
@@ -471,7 +488,27 @@ function deadlinePressure(g, now) {
|
|
|
471
488
|
const span = Math.max(1, g.deadline - g.adoptedAt);
|
|
472
489
|
return 1 + AGENDA_DEFAULTS.goalDeadlineBoost * clamp01((now - g.adoptedAt) / span);
|
|
473
490
|
}
|
|
491
|
+
/** Shared existing amount and urgency reader; no future knowledge is consulted here. @internal */
|
|
492
|
+
export function goalValue(g, aff, now) {
|
|
493
|
+
const amount = goalAmount(g, aff);
|
|
494
|
+
return amount > 0
|
|
495
|
+
? g.importance * clamp01(amount * AGENDA_DEFAULTS.goalAmountGain) * deadlinePressure(g, now)
|
|
496
|
+
: 0;
|
|
497
|
+
}
|
|
498
|
+
/** Opt-in completion-time and success estimate, shared by direct and received routes. @internal */
|
|
499
|
+
export function temporalGoalValue(p, g, aff, now, completion) {
|
|
500
|
+
const base = goalValue(g, aff, now);
|
|
501
|
+
return g.temporal
|
|
502
|
+
? base *
|
|
503
|
+
expectedEffect(p, aff).successRate *
|
|
504
|
+
dpow(0.5, Math.max(0, completion - now) / g.temporal.halfLifeMinutes)
|
|
505
|
+
: base;
|
|
506
|
+
}
|
|
474
507
|
export function agendaTerms(p, aff, now) {
|
|
508
|
+
return agendaTermsWithGoalEstimates(p, aff, now);
|
|
509
|
+
}
|
|
510
|
+
/** Internal route integration; hosts cannot provide goal overrides through the public API. @internal */
|
|
511
|
+
export function agendaTermsWithGoalEstimates(p, aff, now, goalOverrides) {
|
|
475
512
|
const d = AGENDA_DEFAULTS;
|
|
476
513
|
const terms = [];
|
|
477
514
|
for (const c of p.agenda.commitments) {
|
|
@@ -506,6 +543,13 @@ export function agendaTerms(p, aff, now) {
|
|
|
506
543
|
for (const g of p.agenda.goals) {
|
|
507
544
|
if (g.status !== 'active')
|
|
508
545
|
continue;
|
|
546
|
+
const override = goalOverrides?.get(g.id);
|
|
547
|
+
if (override !== undefined || g.temporal) {
|
|
548
|
+
const value = override ?? temporalGoalValue(p, g, aff, now, now + Math.max(0, aff.duration));
|
|
549
|
+
if (value > 0)
|
|
550
|
+
terms.push({ source: `goal:${g.id}`, value });
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
509
553
|
const amount = goalAmount(g, aff);
|
|
510
554
|
if (amount <= 0)
|
|
511
555
|
continue;
|
|
@@ -632,6 +676,7 @@ export function adoptGoal(p, g, now) {
|
|
|
632
676
|
adoptedAt: now,
|
|
633
677
|
lastAdvancedAt: now,
|
|
634
678
|
};
|
|
679
|
+
sanitizeGoalTemporal(goal);
|
|
635
680
|
p.agenda.goals.push(goal);
|
|
636
681
|
return goal;
|
|
637
682
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type HabitContext } from '../habits/index.ts';
|
|
2
|
+
import { type PromptingSeen } from '../promptings/index.ts';
|
|
2
3
|
import type { Affordance, BodyReadout, Command, Considered, DecisionRecord, LifeModifiers, Minute, NeedReading, Person, Suggestion, Term, Unit } from '../types.ts';
|
|
3
4
|
import { type CommandOutcome, type WillContext } from '../will/index.ts';
|
|
4
5
|
export declare const COGNITION_DEFAULTS: {
|
|
@@ -132,6 +133,8 @@ export interface Decision {
|
|
|
132
133
|
needDeltas: Partial<Record<'autonomy', number>>;
|
|
133
134
|
/** How a command in force fared (absent when none). */
|
|
134
135
|
command?: CommandOutcome;
|
|
136
|
+
/** Prompts noticed at this decision point (only with `p.promptings`); the composite records them. */
|
|
137
|
+
prompts?: PromptingSeen[];
|
|
135
138
|
}
|
|
136
139
|
/** Highest physiological urgency: the input to the necessity exception. */
|
|
137
140
|
export declare function desperationOf(needs: NeedReading[]): number;
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* `safety` need: `security` is a value, not a need, and no need is added.
|
|
26
26
|
*/
|
|
27
27
|
import { emotionLevel, inBreak } from '../affect/index.js';
|
|
28
|
-
import {
|
|
28
|
+
import { agendaTermsWithGoalEstimates } from '../agenda/index.js';
|
|
29
29
|
import { trustOf } from '../beliefs/index.js';
|
|
30
30
|
import { normTerms } from '../conscience/index.js';
|
|
31
31
|
import { clamp01, cmpStr, dexp, minuteOfDay, round } from '../core/index.js';
|
|
@@ -34,6 +34,8 @@ import { expectedEffect } from '../memory/index.js';
|
|
|
34
34
|
import { methodEvidence } from '../methods/index.js';
|
|
35
35
|
import { urgencyOf } from '../needs/index.js';
|
|
36
36
|
import { partneringTerms } from '../partnering/index.js';
|
|
37
|
+
import { promptIn, promptingTerms } from '../promptings/index.js';
|
|
38
|
+
import { receivedGoalRoute } from '../prospection/index.js';
|
|
37
39
|
import { companionSteadiness, socialTerms } from '../social/index.js';
|
|
38
40
|
import { PHYSIOLOGICAL_NEEDS, VALUE_KEYS } from '../types.js';
|
|
39
41
|
import { adviceWeight, resolveChoice, safeUtility, standingAdvice, suggestionTargets, voicesIn, } from '../will/index.js';
|
|
@@ -200,6 +202,29 @@ export function socialContext(ctx) {
|
|
|
200
202
|
satiation: K.socialSatiationFloor + (1 - K.socialSatiationFloor) * (1 - clamp01(belongingLevel)),
|
|
201
203
|
};
|
|
202
204
|
}
|
|
205
|
+
/** Same perceived-body effort estimate for actual offers and received successors. @internal */
|
|
206
|
+
export function effortValue(p, aff, ctx) {
|
|
207
|
+
const K = COGNITION_DEFAULTS;
|
|
208
|
+
const per = ctx.body.perceived;
|
|
209
|
+
const effort = clamp01(aff.effort);
|
|
210
|
+
const focus = clamp01(aff.focus ?? 0);
|
|
211
|
+
// A cued habit eases initiation and attention (N4: the depleted fall back on habits), not physical exertion.
|
|
212
|
+
const ease = p.habits.length === 0
|
|
213
|
+
? 1
|
|
214
|
+
: habitEase(p, aff, { ...ctx.habit, placeId: aff.placeId ?? ctx.habit.placeId });
|
|
215
|
+
const effortCost = (effort * K.effortBase + focus * per.sleepiness) * ease +
|
|
216
|
+
effort * per.fatigue * (1 - clamp01(p.body.fitness));
|
|
217
|
+
return -K.effortScale * effortCost;
|
|
218
|
+
}
|
|
219
|
+
/** Same material valuation for actual offers and received successor costs. @internal */
|
|
220
|
+
export function materialValue(p, material, scarcity) {
|
|
221
|
+
const K = COGNITION_DEFAULTS;
|
|
222
|
+
const v = p.values;
|
|
223
|
+
const weight = (v.security + v.achievement + v.power) / 3;
|
|
224
|
+
const scale = K.materialScale * (1 - K.scarcityScaleShrink * scarcity);
|
|
225
|
+
const sat = Math.sign(material) * (1 - dexp(-Math.abs(material) / scale));
|
|
226
|
+
return K.materialWeight * sat * 2 * weight * (1 + K.scarcityMaterialGain * scarcity);
|
|
227
|
+
}
|
|
203
228
|
/** Score one option. Pure apart from reads; terms are rounded for stable snapshots. */
|
|
204
229
|
export function consider(p, aff, ctx) {
|
|
205
230
|
const K = COGNITION_DEFAULTS;
|
|
@@ -264,8 +289,13 @@ export function consider(p, aff, ctx) {
|
|
|
264
289
|
}
|
|
265
290
|
}
|
|
266
291
|
const maturity = clamp01(ctx.mods.maturity);
|
|
267
|
-
|
|
268
|
-
|
|
292
|
+
const maturityScale = 0.5 + 0.5 * maturity;
|
|
293
|
+
const received = receivedGoalRoute(p, aff, ctx.now, maturityScale, (successor) => effortValue(p, successor, ctx) +
|
|
294
|
+
(successor.material ? materialValue(p, successor.material, scarcity) : 0));
|
|
295
|
+
for (const t of agendaTermsWithGoalEstimates(p, aff, ctx.now, received?.goals))
|
|
296
|
+
push(t.source, t.value * maturityScale);
|
|
297
|
+
if (received)
|
|
298
|
+
push(received.cost.source, received.cost.value);
|
|
269
299
|
// Habit: cue-matched automaticity (cued by the option's own place), stronger before planning matures.
|
|
270
300
|
const habit = p.habits.length === 0
|
|
271
301
|
? 0
|
|
@@ -316,17 +346,9 @@ export function consider(p, aff, ctx) {
|
|
|
316
346
|
}
|
|
317
347
|
}
|
|
318
348
|
// Effort and mental load, read from the perceived body.
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// A cued habit eases initiation and attention (N4: the depleted fall back on habits), not physical exertion.
|
|
323
|
-
const ease = p.habits.length === 0
|
|
324
|
-
? 1
|
|
325
|
-
: habitEase(p, aff, { ...ctx.habit, placeId: aff.placeId ?? ctx.habit.placeId });
|
|
326
|
-
const effortCost = (effort * K.effortBase + focus * per.sleepiness) * ease +
|
|
327
|
-
effort * per.fatigue * (1 - clamp01(p.body.fitness));
|
|
328
|
-
if (effortCost > 0)
|
|
329
|
-
push('effort', -K.effortScale * effortCost);
|
|
349
|
+
const effort = effortValue(p, aff, ctx);
|
|
350
|
+
if (effort < 0)
|
|
351
|
+
push('effort', effort);
|
|
330
352
|
// Risk: chance × severity × (fear + emotionality).
|
|
331
353
|
if (aff.risk && aff.risk.chance > 0 && aff.risk.severity > 0) {
|
|
332
354
|
const fear = emotionLevel(p, 'fear');
|
|
@@ -340,13 +362,8 @@ export function consider(p, aff, ctx) {
|
|
|
340
362
|
}
|
|
341
363
|
}
|
|
342
364
|
// Material: saturating value of host units, scaled by security/achievement/power.
|
|
343
|
-
if (aff.material)
|
|
344
|
-
|
|
345
|
-
const weight = (v.security + v.achievement + v.power) / 3;
|
|
346
|
-
const scale = K.materialScale * (1 - K.scarcityScaleShrink * scarcity);
|
|
347
|
-
const sat = Math.sign(aff.material) * (1 - dexp(-Math.abs(aff.material) / scale));
|
|
348
|
-
push('material', K.materialWeight * sat * 2 * weight * (1 + K.scarcityMaterialGain * scarcity));
|
|
349
|
-
}
|
|
365
|
+
if (aff.material)
|
|
366
|
+
push('material', materialValue(p, aff.material, scarcity));
|
|
350
367
|
// Precommitments inside their daily window.
|
|
351
368
|
const mod = minuteOfDay(ctx.now);
|
|
352
369
|
for (const pc of p.will.precommitments) {
|
|
@@ -382,6 +399,10 @@ export function consider(p, aff, ctx) {
|
|
|
382
399
|
if (!inBreak(p))
|
|
383
400
|
for (const t of rememberedTerms(p, aff, ctx.now, voices))
|
|
384
401
|
push(t.source, t.value);
|
|
402
|
+
// Promptings (2.4.0, opt-in): a recurrence pull under `fault` and a practised turning-away from a noticed prompt.
|
|
403
|
+
if (p.promptings)
|
|
404
|
+
for (const t of promptingTerms(p, aff.action, terms, aff.tags, ctx.now, ctx.desperation))
|
|
405
|
+
push(t.source, t.value);
|
|
385
406
|
let utility = 0;
|
|
386
407
|
for (const t of terms)
|
|
387
408
|
utility += t.value;
|
|
@@ -455,5 +476,22 @@ export function decide(p, affordances, ctx) {
|
|
|
455
476
|
const out = { record, needDeltas };
|
|
456
477
|
if (res.command)
|
|
457
478
|
out.command = res.command;
|
|
479
|
+
if (p.promptings) {
|
|
480
|
+
const prompts = [];
|
|
481
|
+
const tagsOf = new Map(sorted.map((a) => [a.id, a.tags]));
|
|
482
|
+
for (const c of res.considered) {
|
|
483
|
+
const seen = promptIn(c.terms, tagsOf.get(c.affordanceId));
|
|
484
|
+
if (seen)
|
|
485
|
+
prompts.push({
|
|
486
|
+
affordanceId: c.affordanceId,
|
|
487
|
+
action: c.action,
|
|
488
|
+
...seen,
|
|
489
|
+
// A vetoed option was not turned from: the will blocked it, so the response earns no practice.
|
|
490
|
+
turnApplied: c.vetoed === undefined && c.terms.some((t) => t.source === 'promptings:turn'),
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
if (prompts.length > 0)
|
|
494
|
+
out.prompts = prompts;
|
|
495
|
+
}
|
|
458
496
|
return out;
|
|
459
497
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Human Framework v1 public surface. */
|
|
2
|
-
export declare const FRAMEWORK_VERSION = "2.
|
|
2
|
+
export declare const FRAMEWORK_VERSION = "2.4.0";
|
|
3
3
|
export type { CrisisEvent, PracticeKind } from './affect/index.ts';
|
|
4
4
|
export { AFFECT_DEFAULTS, actionTendencies, advanceAffect, appraise, breakAllows, breakBehaviour, breakHazard, CRISIS_DEFAULTS, checkCrisis, createAffect, easeBreak, emotionLevel, enableBreaks, feel, inBreak, readAffect, regulate, release as releaseEmotion, skipCrisis, strain, tendencyEmotions, } from './affect/index.ts';
|
|
5
5
|
export type { GoalTemplate, PrayerCalendar, PrayerSchedule, PrayerTimes, Retimer, } from './agenda/index.ts';
|
|
@@ -28,6 +28,9 @@ export * from './narrate/index.ts';
|
|
|
28
28
|
export * from './needs/index.ts';
|
|
29
29
|
export * from './partnering/index.ts';
|
|
30
30
|
export * from './person.ts';
|
|
31
|
+
export * from './promptings/index.ts';
|
|
32
|
+
export type { ConsequenceEvidence } from './prospection/index.ts';
|
|
33
|
+
export { consequenceEvidence, PROSPECTION_DEFAULTS } from './prospection/index.ts';
|
|
31
34
|
export * from './restore.ts';
|
|
32
35
|
export * from './scenarios/index.ts';
|
|
33
36
|
export * from './sim/index.ts';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Human Framework v1 public surface. */
|
|
2
|
-
export const FRAMEWORK_VERSION = '2.
|
|
2
|
+
export const FRAMEWORK_VERSION = '2.4.0';
|
|
3
3
|
export { AFFECT_DEFAULTS, actionTendencies, advanceAffect, appraise, breakAllows, breakBehaviour, breakHazard, CRISIS_DEFAULTS, checkCrisis, createAffect, easeBreak, emotionLevel, enableBreaks, feel, inBreak, readAffect, regulate, release as releaseEmotion, skipCrisis, strain, tendencyEmotions, } from './affect/index.js';
|
|
4
4
|
// `agenda.release` collides with `affect.release`; the agenda one is re-exported under a clearer name.
|
|
5
5
|
export { AGENDA_DEFAULTS, abandonGoal, adoptGoal, advanceAgenda, agendaTerms, applyExemptions, CARE_DEFAULTS, calendarRetimer, careDuty, commitmentPressure, createAgenda, DEFAULT_PRAYER_TIMES, EID_PRAYER_IMPORTANCE, EXEMPTION_DEFAULTS, eidPrayer, eidWindow, fastWindow, iftarWindow, inMakruhTime, isUnderWay, MAKRUH_DEFAULTS, makruhWindows, missedExcuse, onFinished, owedMakeUps, PRAYER_IMPORTANCE, PURPOSE_DEFAULTS, prayerWindows, pressureReachedAt, promise, proposeGoals, RAMADAN_DEFAULTS, ramadanFast, ramadanMeals, release as releaseCommitment, retimeCommitments, revisePurposes, scheduleMakeUp, spanMeetsWindow, suhoorWindow, timesFor, violatesAbstention, } from './agenda/index.js';
|
|
@@ -25,6 +25,8 @@ export * from './narrate/index.js';
|
|
|
25
25
|
export * from './needs/index.js';
|
|
26
26
|
export * from './partnering/index.js';
|
|
27
27
|
export * from './person.js';
|
|
28
|
+
export * from './promptings/index.js';
|
|
29
|
+
export { consequenceEvidence, PROSPECTION_DEFAULTS } from './prospection/index.js';
|
|
28
30
|
export * from './restore.js';
|
|
29
31
|
export * from './scenarios/index.js';
|
|
30
32
|
export * from './sim/index.js';
|
package/dist/migrate.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* field of the optional surroundings slice (`ambient.now` → `ambient.percept`) and changes no behaviour (the optional `social.impressions` and `social.reserve`, like `retention`, are optional and start
|
|
9
9
|
* absent in a migrated save); `restore` then fills and sanitizes
|
|
10
10
|
* the slices as for any save. 2.0.0 to 2.1.0 adds optional conditional method state, absent
|
|
11
|
-
* until explicitly used, so existing hosts retain their previous behavior; that step stamps the version. 2.1.0 to 2.2.0 preserves optional method records and stamps the version; successful alternatives now retain independent support. A migrated save continues under the current engine's rules: it restores and runs,
|
|
11
|
+
* until explicitly used, so existing hosts retain their previous behavior; that step stamps the version. 2.1.0 to 2.2.0 preserves optional method records and stamps the version; successful alternatives now retain independent support. 2.2.0 to 2.3.0 stamps the version; received consequences and temporal goals remain absent unless explicitly supplied. 2.3.0 to 2.4.0 stamps the version; promptings remain absent unless enabled. A migrated save continues under the current engine's rules: it restores and runs,
|
|
12
12
|
* but it does not reproduce what the old engine would have done next where the rules changed (1.4.0 to 1.5.0
|
|
13
13
|
* changed how standing advice is heard; 1.8.0 to 1.9.0 changed the omission rule). Saves older than 1.4.0 and unknown versions are refused with an error.
|
|
14
14
|
*
|
|
@@ -58,6 +58,16 @@ export const MIGRATIONS = {
|
|
|
58
58
|
note: 'the surroundings field `ambient.now` is renamed `ambient.percept`; per-person retention is optional and starts absent; behaviour unchanged',
|
|
59
59
|
apply: renameAmbient,
|
|
60
60
|
},
|
|
61
|
+
'2.3.0': {
|
|
62
|
+
to: '2.4.0',
|
|
63
|
+
note: 'promptings are optional and start absent; existing hosts retain exact behavior',
|
|
64
|
+
apply: stamp,
|
|
65
|
+
},
|
|
66
|
+
'2.2.0': {
|
|
67
|
+
to: '2.3.0',
|
|
68
|
+
note: 'received consequences and temporal goals are optional; existing hosts retain exact behavior',
|
|
69
|
+
apply: stamp,
|
|
70
|
+
},
|
|
61
71
|
'2.1.0': {
|
|
62
72
|
to: '2.2.0',
|
|
63
73
|
note: 'person shape unchanged; method evidence now grades each action independently, preserving successful alternatives',
|
package/dist/person.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type NafsPattern } from './promptings/index.ts';
|
|
1
2
|
import { type SkillTransfer } from './skills/index.ts';
|
|
2
3
|
import type { Activity, Affordance, BodyLoad, BodyReadout, Command, Commitment, DecisionRecord, LearningDomain, Minute, NeedId, NeedReading, NormDefinition, Outcome, Percept, Person, PersonSpec, Retention, Suggestion, SuggestionResolution, Unit } from './types.ts';
|
|
3
4
|
import { type CommandOutcome } from './will/index.ts';
|
|
@@ -230,3 +231,8 @@ export declare function finish(p: Person, outcome: Outcome, opts?: FinishOptions
|
|
|
230
231
|
* entries are ignored; an empty or wholly invalid retention restores the defaults. Changes no decision.
|
|
231
232
|
*/
|
|
232
233
|
export declare function setRetention(p: Person, retention: Retention): void;
|
|
234
|
+
/**
|
|
235
|
+
* The nafs lens (2.4.0, read-only; see `promptings/` SCOPE): the three Qur'anic descriptions of one self (12:53,
|
|
236
|
+
* 75:2, 89:27), each with the matching evidence in existing state. Descriptions, never a level or a rank.
|
|
237
|
+
*/
|
|
238
|
+
export declare function nafsReading(p: Person): NafsPattern[];
|
package/dist/person.js
CHANGED
|
@@ -26,7 +26,7 @@ import { advanceBody, BODY_DEFAULTS, checkDowned, clearDowned, consume, createBo
|
|
|
26
26
|
import { ageCharacter, enableCharacterChange, noteCharacterDay, noteCharacterSocial, } from './character/index.js';
|
|
27
27
|
import { closeDay, enableYearbook, endDay, noteAnswer, noteCommitments, noteDecision, noteMood, noteOutcome, trimChronicle, } from './chronicle/index.js';
|
|
28
28
|
import { decide as cognitionDecide, desperationOf, scoreAll } from './cognition/index.js';
|
|
29
|
-
import { createConscience, heldNorms, normVeto, recordDeed, recordRepair, repent, } from './conscience/index.js';
|
|
29
|
+
import { createConscience, guiltLoad, heldNorms, normVeto, recordDeed, recordRepair, repent, } from './conscience/index.js';
|
|
30
30
|
import { clamp01, clampSigned, createRng, dayOf, isObj, minuteOfDay } from './core/index.js';
|
|
31
31
|
import { ambientBodyParams, ambientModifiers, ambientMood, ambientNeeds } from './environment/index.js';
|
|
32
32
|
import { aptitudeOf, createFamily, pregnancyModifiers } from './family/index.js';
|
|
@@ -36,6 +36,8 @@ import { advanceMemory, createMemory, enableGists, foldGists, learnOutcome, reca
|
|
|
36
36
|
import { intentionFor, narrateDecision, voiceLine } from './narrate/index.js';
|
|
37
37
|
import { advanceNeeds, createNeeds, levelOf, meanSatisfaction, readNeeds, satisfy, urgencyOf, } from './needs/index.js';
|
|
38
38
|
import { widow } from './partnering/index.js';
|
|
39
|
+
import { enablePromptings, nafsPatterns, notePromptings } from './promptings/index.js';
|
|
40
|
+
import { consumeConsequences, pruneConsequences, receiveConsequences } from './prospection/index.js';
|
|
39
41
|
import { enableSkillRetention, learnByWatching, practise, seedSkills, } from './skills/index.js';
|
|
40
42
|
import { advanceSocial, applyReputationBelief, careFor, closeness, judge, markDeceased, relationshipWith, seedRelationships, socialEvent, threatAppraisal, } from './social/index.js';
|
|
41
43
|
import { ENGINE_VERSION, MAX_MINUTE, PERSON_SCHEMA, PHYSIOLOGICAL_NEEDS, PSYCHOLOGICAL_NEEDS, TRAIT_KEYS, VALUE_KEYS, } from './types.js';
|
|
@@ -152,6 +154,8 @@ export function createPerson(spec) {
|
|
|
152
154
|
enableCharacterChange(p);
|
|
153
155
|
if (on?.skillRetention)
|
|
154
156
|
enableSkillRetention(p);
|
|
157
|
+
if (on?.promptings)
|
|
158
|
+
enablePromptings(p, on.promptings);
|
|
155
159
|
return p;
|
|
156
160
|
}
|
|
157
161
|
/**
|
|
@@ -353,6 +357,7 @@ export function tick(p, now) {
|
|
|
353
357
|
return;
|
|
354
358
|
if (!p.body.alive) {
|
|
355
359
|
p.now = now;
|
|
360
|
+
pruneConsequences(p);
|
|
356
361
|
return;
|
|
357
362
|
}
|
|
358
363
|
const seg = PERSON_DEFAULTS.maxSegment;
|
|
@@ -366,13 +371,16 @@ export function tick(p, now) {
|
|
|
366
371
|
// A minute so large that the grid step rounds away cannot advance (security review H2): stop, never spin.
|
|
367
372
|
if (!(end > p.now)) {
|
|
368
373
|
p.now = now;
|
|
374
|
+
pruneConsequences(p);
|
|
369
375
|
return;
|
|
370
376
|
}
|
|
371
377
|
advanceSegment(p, end - p.now, load, active && act ? act.affordance : undefined);
|
|
372
378
|
p.now = end;
|
|
379
|
+
pruneConsequences(p);
|
|
373
380
|
if (!p.body.alive) {
|
|
374
381
|
p.activity = null;
|
|
375
382
|
p.now = now;
|
|
383
|
+
pruneConsequences(p);
|
|
376
384
|
return;
|
|
377
385
|
}
|
|
378
386
|
// Discrete bookkeeping runs only on grid boundaries, so it happens at the same minutes whatever the
|
|
@@ -517,6 +525,7 @@ export function skip(p, to) {
|
|
|
517
525
|
p.activity = null;
|
|
518
526
|
skipBody(p, to);
|
|
519
527
|
p.now = to;
|
|
528
|
+
pruneConsequences(p);
|
|
520
529
|
skipAffect(p, to);
|
|
521
530
|
advanceAgenda(p, to);
|
|
522
531
|
// Lasting gists (1.8.0, opt-in): episodes now past the horizon fold into gists.
|
|
@@ -535,6 +544,7 @@ function socialKindOf(kind) {
|
|
|
535
544
|
return SOCIAL_ALIASES[kind];
|
|
536
545
|
}
|
|
537
546
|
function perceiveOne(p, pc) {
|
|
547
|
+
receiveConsequences(p, pc);
|
|
538
548
|
const at = Math.min(Math.max(pc.at, p.affect.lastUpdated), p.now);
|
|
539
549
|
const actor = pc.actorId;
|
|
540
550
|
const target = pc.targetId;
|
|
@@ -810,7 +820,7 @@ export function decide(p, affordances, opts = {}) {
|
|
|
810
820
|
const review = p.activity !== null;
|
|
811
821
|
if (review)
|
|
812
822
|
ctx.quiet = true;
|
|
813
|
-
const { record, needDeltas, command: co } = cognitionDecide(p, list, ctx);
|
|
823
|
+
const { record, needDeltas, command: co, prompts } = cognitionDecide(p, list, ctx);
|
|
814
824
|
if (review)
|
|
815
825
|
record.review = true;
|
|
816
826
|
const interruptReason = opts.interrupt ?? p.activity?.interrupt?.reason;
|
|
@@ -824,6 +834,12 @@ export function decide(p, affordances, opts = {}) {
|
|
|
824
834
|
record.narration = narrateDecision(p, record);
|
|
825
835
|
const resolutions = resolutionsOf(record);
|
|
826
836
|
const continuing = review && (record.chosenAffordanceId === null || record.chosenAffordanceId === p.activity?.affordanceId);
|
|
837
|
+
// Promptings (opt-in): a review that keeps the running activity is not a new decision point for a prompt.
|
|
838
|
+
if (prompts && !continuing) {
|
|
839
|
+
const noted = notePromptings(p, prompts, record.chosenAffordanceId, p.now);
|
|
840
|
+
if (noted.guilt > 0)
|
|
841
|
+
feel(p, 'guilt', noted.guilt, 'prompting', p.now);
|
|
842
|
+
}
|
|
827
843
|
for (const res of resolutions) {
|
|
828
844
|
res.says = voiceLine(p, res, record.id);
|
|
829
845
|
if (res.verdict === 'complied' && !continuing) {
|
|
@@ -1191,6 +1207,7 @@ export function finish(p, outcome, opts = {}) {
|
|
|
1191
1207
|
}
|
|
1192
1208
|
const prev = lastAction(p);
|
|
1193
1209
|
if (completed) {
|
|
1210
|
+
consumeConsequences(p, aff);
|
|
1194
1211
|
const hctx = { now };
|
|
1195
1212
|
if (aff.placeId !== undefined)
|
|
1196
1213
|
hctx.placeId = aff.placeId;
|
|
@@ -1382,3 +1399,14 @@ export function setRetention(p, retention) {
|
|
|
1382
1399
|
p.trace.splice(0, p.trace.length - keep);
|
|
1383
1400
|
trimChronicle(p);
|
|
1384
1401
|
}
|
|
1402
|
+
/**
|
|
1403
|
+
* The nafs lens (2.4.0, read-only; see `promptings/` SCOPE): the three Qur'anic descriptions of one self (12:53,
|
|
1404
|
+
* 75:2, 89:27), each with the matching evidence in existing state. Descriptions, never a level or a rank.
|
|
1405
|
+
*/
|
|
1406
|
+
export function nafsReading(p) {
|
|
1407
|
+
let guilt = 0;
|
|
1408
|
+
for (const e of p.affect.emotions)
|
|
1409
|
+
if (e.id === 'guilt' && e.cause !== 'prompting')
|
|
1410
|
+
guilt = Math.max(guilt, e.intensity);
|
|
1411
|
+
return nafsPatterns(p, { guiltLoad: guiltLoad(p), guilt });
|
|
1412
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './promptings.ts';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './promptings.js';
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Minute, Person, PromptingReading, PromptingRecord, PromptingResponse, PromptingResponseKind, PromptingsSpec, PromptingsState, Term, Unit } from '../types.ts';
|
|
2
|
+
export declare const PROMPTINGS_DEFAULTS: {
|
|
3
|
+
/** Pull (utility units) from which a pull against a held norm or goal is noticed. */
|
|
4
|
+
readonly noticeAt: 0.15;
|
|
5
|
+
/** Largest turning-away term at full practice (utility units). */
|
|
6
|
+
readonly turnMax: 0.8;
|
|
7
|
+
/** No turning-away term at or above this desperation (same scale as will's survival desperation). */
|
|
8
|
+
readonly survivalDesperation: 0.75;
|
|
9
|
+
/** Practice gained at a prompt where the response held (× (1 - practice)). */
|
|
10
|
+
readonly gainTurned: 0.12;
|
|
11
|
+
/** Practice gained at a prompt where the response was tried but the person assented. */
|
|
12
|
+
readonly gainAttempt: 0.04;
|
|
13
|
+
/** Practice halves over this many days without use. */
|
|
14
|
+
readonly practiceHalfLifeDays: 60;
|
|
15
|
+
/** Guilt intensity at a prompt under the `fault` reading. */
|
|
16
|
+
readonly faultGuilt: 0.15;
|
|
17
|
+
/** Recurrence pull at echo level 1 (utility units); only reached when a host sets `echoRate` > 0. */
|
|
18
|
+
readonly echoScale: 0.5;
|
|
19
|
+
readonly echoHalfLifeDays: 3;
|
|
20
|
+
readonly maxRecent: 64;
|
|
21
|
+
readonly maxResponses: 8;
|
|
22
|
+
readonly maxEcho: 32;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Turn the slice on, or reconfigure it: recent records and echo are kept, and a response whose id is already held
|
|
26
|
+
* keeps its practice and last use (the spec's `practice` only seeds new responses). Returns the state.
|
|
27
|
+
*/
|
|
28
|
+
export declare function enablePromptings(p: Person, spec?: PromptingsSpec): PromptingsState;
|
|
29
|
+
/** Practice of a response at `now`, after disuse decay. Pure. */
|
|
30
|
+
export declare function practiceAt(r: PromptingResponse, now: Minute): Unit;
|
|
31
|
+
/** Whether an option's terms carry a noticed prompt: its strongest pull and the opposing term. Pure. */
|
|
32
|
+
export declare function promptIn(terms: readonly Term[], tags: readonly string[] | undefined): {
|
|
33
|
+
source: string;
|
|
34
|
+
opposedBy: string;
|
|
35
|
+
} | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Extra terms for one option (cognition calls this before summing): the echo pull under `fault`, then the
|
|
38
|
+
* turning-away term when the option carries a prompt. Pure apart from reads; empty without the slice.
|
|
39
|
+
*/
|
|
40
|
+
export declare function promptingTerms(p: Person, action: string, terms: readonly Term[], tags: readonly string[] | undefined, now: Minute, desperation: number): Term[];
|
|
41
|
+
/** A prompt found at a decision point, handed from cognition to the composite. */
|
|
42
|
+
export interface PromptingSeen {
|
|
43
|
+
affordanceId: string;
|
|
44
|
+
action: string;
|
|
45
|
+
source: string;
|
|
46
|
+
opposedBy: string;
|
|
47
|
+
/** Whether a turning-away term was applied. */
|
|
48
|
+
turnApplied: boolean;
|
|
49
|
+
}
|
|
50
|
+
/** What `notePromptings` did, for the composite (the `fault` guilt goes through affect). */
|
|
51
|
+
export interface PromptingsNoted {
|
|
52
|
+
records: PromptingRecord[];
|
|
53
|
+
/** Guilt intensity to feel at the prompt (only under `fault`); 0 otherwise. */
|
|
54
|
+
guilt: Unit;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Record the prompts of one decision point and learn from them. `chosenId` is the chosen affordance (null when
|
|
58
|
+
* nothing was chosen). The only writer of `p.promptings` after `enablePromptings`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function notePromptings(p: Person, seen: readonly PromptingSeen[], chosenId: string | null, now: Minute): PromptingsNoted;
|
|
61
|
+
/** Vocabulary for one prompt under a held reading (narration only; it changes nothing). */
|
|
62
|
+
export declare function promptingLine(reading: PromptingReading, outcome: PromptingRecord['outcome'], kind?: PromptingResponseKind): string;
|
|
63
|
+
/** Read-only summary of recent prompts (a lens; computes nothing new about the person). */
|
|
64
|
+
export interface PromptingsReadingView {
|
|
65
|
+
reading: PromptingReading;
|
|
66
|
+
readingSource?: string;
|
|
67
|
+
prompts: number;
|
|
68
|
+
turned: number;
|
|
69
|
+
passed: number;
|
|
70
|
+
assented: number;
|
|
71
|
+
responses: {
|
|
72
|
+
id: string;
|
|
73
|
+
kind: PromptingResponseKind;
|
|
74
|
+
practice: Unit;
|
|
75
|
+
}[];
|
|
76
|
+
/** Vocabulary for the last few prompts, newest last. */
|
|
77
|
+
lines: string[];
|
|
78
|
+
}
|
|
79
|
+
export declare function promptingsReading(p: Person, now?: Minute): PromptingsReadingView | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* The nafs lens (reconciliation.md §3): the three Qur'anic descriptions of one self, each with the evidence in
|
|
82
|
+
* existing state that matches it. Descriptions, not a level, a ladder or three agents; several can hold at once,
|
|
83
|
+
* and none says anything about rank before God. Inputs are passed in by the composite (open-breach weight and
|
|
84
|
+
* felt guilt from conscience and affect), so this module reads nothing it does not own. Guilt that the `fault` reading
|
|
85
|
+
* feels at a prompt is excluded, so the scrupulous reading is not mapped onto self-reproach (75:2). These mappings
|
|
86
|
+
* from state to description are engineering conventions (C), not interpretations of the verses.
|
|
87
|
+
*/
|
|
88
|
+
export interface NafsPattern {
|
|
89
|
+
name: 'urging' | 'reproaching' | 'at rest';
|
|
90
|
+
/** Qur'anic description it borrows (12:53 ammara, 75:2 lawwama, 89:27 mutmaʾinna). */
|
|
91
|
+
ref: string;
|
|
92
|
+
evident: boolean;
|
|
93
|
+
evidence: string;
|
|
94
|
+
}
|
|
95
|
+
export declare function nafsPatterns(p: Person, inputs: {
|
|
96
|
+
guiltLoad: Unit;
|
|
97
|
+
guilt: Unit;
|
|
98
|
+
}): NafsPattern[];
|
|
99
|
+
/** Validate a saved slice; a malformed slice is dropped (undefined), malformed entries are skipped. */
|
|
100
|
+
export declare function sanitizePromptings(input: unknown, now: Minute): PromptingsState | undefined;
|