@adam0white/human-framework 2.16.0 → 2.17.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 +17 -0
- package/dist/agenda/agenda.js +4 -1
- package/dist/conversation/conversation.d.ts +13 -4
- package/dist/conversation/conversation.js +24 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/migrate.js +5 -0
- package/dist/narrate/lexicon.js +23 -0
- package/dist/narrate/narrate.d.ts +7 -1
- package/dist/narrate/narrate.js +17 -1
- package/dist/person.d.ts +6 -1
- package/dist/person.js +13 -8
- package/dist/persona.js +5 -2
- package/dist/projects/projects.d.ts +8 -2
- package/dist/projects/projects.js +142 -2
- package/dist/promptings/promptings.d.ts +3 -1
- package/dist/promptings/promptings.js +13 -1
- package/dist/prospection/prospection.js +15 -2
- package/dist/review/review.d.ts +4 -1
- package/dist/review/review.js +12 -1
- package/dist/types.d.ts +35 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -410,6 +410,7 @@ Knowledge lives in optional `Person.prospection` and survives snapshot/restore.
|
|
|
410
410
|
`projectPersona(source, mapping)` produces `PersonaProjectionSnapshot` (`hf-persona-state/1`).
|
|
411
411
|
`admitPersona(projection, targetOpening, { mappingVersion, sourceId, targetId })` returns a detached
|
|
412
412
|
`{ person, report }`. `validatePersonaProjection(unknown)` validates portable data without filling defaults.
|
|
413
|
+
Portable v1 accepts the published 2.14.1 source-engine stamp and the current engine; it preserves that source provenance. Unknown source engines remain rejected.
|
|
413
414
|
These functions never enter the actor loop and never mutate the source or the host opening.
|
|
414
415
|
|
|
415
416
|
The host supplies a versioned map of skill ids, selected episode ids and every continuing obligation.
|
|
@@ -459,3 +460,19 @@ returning a detached Person. The receiving calendar must be empty; authored role
|
|
|
459
460
|
admission, the host uses ordinary agenda APIs and its declared calendar. No duty is waived, no new ruling
|
|
460
461
|
is invented, and no obligation is converted to a score. Activities, projects and incompatible identity or
|
|
461
462
|
health remain unsupported. The original passport format and no-call continuation remain unchanged.
|
|
463
|
+
|
|
464
|
+
### Project protection and narration options
|
|
465
|
+
|
|
466
|
+
Projects can opt into undo protection with `enableProjects(person, { protectProgress: true })`.
|
|
467
|
+
Declare `diminishes: [prospectId]` on an offer or its described successor, or let the person
|
|
468
|
+
learn the exact action context from a perceived drop after its own completed act.
|
|
469
|
+
`adoptProjectTask(person, prospectId, task)` reuses an active project and its net credit
|
|
470
|
+
for a host task; an absent match returns `undefined`.
|
|
471
|
+
|
|
472
|
+
Narration remains unchanged unless requested. Set `person.lexicon.variety`,
|
|
473
|
+
`nounActions` or `materialMeaning` for the corresponding host wording. Use
|
|
474
|
+
`promptingLine(reading, outcome, kind, { voice: 'third' })` for plain third-person lines.
|
|
475
|
+
`converse` adds per-call `loyalWarning`, `confidentMin` and `phrase` options;
|
|
476
|
+
`perceive` adds a third `{ reputationTraits }` argument. See the architecture's
|
|
477
|
+
[narration and social host options](../../docs/framework.md#narration-and-social-host-options-opt-in)
|
|
478
|
+
for cause-aware reviews and actor-addressed recall. These options change no default continuation.
|
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 { diminishesProject } from '../projects/index.js';
|
|
52
53
|
import { MINUTES_PER_DAY } from '../types.js';
|
|
53
54
|
export const AGENDA_DEFAULTS = {
|
|
54
55
|
/** Minutes before a window opens during which a commitment already pulls (to get there). */
|
|
@@ -527,6 +528,8 @@ function goalValue(g, aff, now) {
|
|
|
527
528
|
}
|
|
528
529
|
/** Opt-in completion-time and success estimate, shared by direct and received routes. @internal */
|
|
529
530
|
export function temporalGoalValue(p, g, aff, now, completion) {
|
|
531
|
+
if (g.project && diminishesProject(p, aff, g.project.prospectId))
|
|
532
|
+
return 0;
|
|
530
533
|
const base = goalValue(g, aff, now);
|
|
531
534
|
return g.temporal
|
|
532
535
|
? base *
|
|
@@ -578,7 +581,7 @@ export function agendaTermsWithGoalEstimates(p, aff, now, goalOverrides) {
|
|
|
578
581
|
terms.push({ source: `commitment:${c.id}`, value: d.commitmentScale * c.importance * pressure });
|
|
579
582
|
}
|
|
580
583
|
for (const g of p.agenda.goals) {
|
|
581
|
-
if (g.status !== 'active')
|
|
584
|
+
if (g.status !== 'active' || (g.project && diminishesProject(p, aff, g.project.prospectId)))
|
|
582
585
|
continue;
|
|
583
586
|
const override = goalOverrides?.get(g.id);
|
|
584
587
|
if (override !== undefined || g.temporal) {
|
|
@@ -55,6 +55,12 @@ export interface ConverseContext {
|
|
|
55
55
|
traits?: Record<string, TraitEffect>;
|
|
56
56
|
/** Carry belief origin and opaque lineage into told claims (off by default). */
|
|
57
57
|
provenance?: boolean;
|
|
58
|
+
/** Relay beliefs this far from 0.5 (0..0.5); default 0.2. Invalid values use the default. */
|
|
59
|
+
confidentMin?: number;
|
|
60
|
+
/** Let a fond speaker warn the listener of an unfavourable character claim about them. */
|
|
61
|
+
loyalWarning?: boolean;
|
|
62
|
+
/** Presentation only. Receives a told claim or a withheld reason and its default summary; never changes selection. */
|
|
63
|
+
phrase?: (claim: Readonly<ToldClaim | WithheldClaim>, summary: string) => string;
|
|
58
64
|
}
|
|
59
65
|
/** Host-visible record of one claim. */
|
|
60
66
|
export interface ToldClaim {
|
|
@@ -80,6 +86,12 @@ export interface AdviceGiven {
|
|
|
80
86
|
/** For the listener's next decision; the voice is the speaker. */
|
|
81
87
|
suggestion: Suggestion;
|
|
82
88
|
}
|
|
89
|
+
export interface WithheldClaim {
|
|
90
|
+
prop: string;
|
|
91
|
+
reason: string;
|
|
92
|
+
/** Present only when a host phrasing callback rendered this reason. */
|
|
93
|
+
summary?: string;
|
|
94
|
+
}
|
|
83
95
|
export interface ConversationResult {
|
|
84
96
|
/** Testimony percepts for `perceive(listener, told)`. */
|
|
85
97
|
told: Percept[];
|
|
@@ -87,10 +99,7 @@ export interface ConversationResult {
|
|
|
87
99
|
advice: AdviceGiven[];
|
|
88
100
|
claims: ToldClaim[];
|
|
89
101
|
/** Candidate claims the speaker held back, and why (e.g. 'norm:backbiting', 'norm:lying'). */
|
|
90
|
-
withheld:
|
|
91
|
-
prop: string;
|
|
92
|
-
reason: string;
|
|
93
|
-
}[];
|
|
102
|
+
withheld: WithheldClaim[];
|
|
94
103
|
/** True when any told claim was a lie. */
|
|
95
104
|
deceit: boolean;
|
|
96
105
|
/** The speaker's own deed tags for conscience (e.g. backbiting/lying violated), deduplicated, sorted. */
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
* share of conversation about absent third parties that spreads reputational information (Dunbar 2004), and
|
|
22
22
|
* extraversion as talkativeness (how many claims are offered).
|
|
23
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
|
+
*
|
|
24
28
|
* It does NOT generate language, model turn-taking, persuasion beyond source trust, what the speaker believes
|
|
25
29
|
* the listener already knows, lies of omission, white lies told to spare feelings, or the speaker's fear of
|
|
26
30
|
* being found out. Coefficients are engineering defaults, not calibrated estimates. Nothing here judges the
|
|
@@ -150,12 +154,25 @@ export function converse(speaker, listener, ctx) {
|
|
|
150
154
|
if (b.prop.startsWith('advice:') || b.prop.includes(':should:'))
|
|
151
155
|
continue;
|
|
152
156
|
const subject = b.prop.split(':')[0];
|
|
153
|
-
if (subject ===
|
|
157
|
+
if (subject === speaker.id)
|
|
154
158
|
continue;
|
|
155
159
|
const cred = expit(b.logOdds);
|
|
156
|
-
|
|
160
|
+
const threshold = Number.isFinite(ctx.confidentMin) &&
|
|
161
|
+
ctx.confidentMin >= 0 &&
|
|
162
|
+
ctx.confidentMin <= 0.5
|
|
163
|
+
? ctx.confidentMin
|
|
164
|
+
: C.confidentMin;
|
|
165
|
+
if (Math.abs(cred - 0.5) < threshold)
|
|
157
166
|
continue;
|
|
158
167
|
const value = cred > 0.5;
|
|
168
|
+
if (subject === listener.id) {
|
|
169
|
+
const trait = parseTraitProp(b.prop, table)?.trait;
|
|
170
|
+
if (!ctx.loyalWarning ||
|
|
171
|
+
relationshipWith(speaker, listener.id).affection < 0.5 ||
|
|
172
|
+
trait === undefined ||
|
|
173
|
+
!isUnfavourableTrait(trait, value, table))
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
159
176
|
const confidence = value ? cred : 1 - cred;
|
|
160
177
|
const recency = 1 / (1 + Math.max(0, ctx.at - b.updatedAt) / C.recencyScale);
|
|
161
178
|
consider(b.prop, value, confidence, cred, false, R.confidence * (confidence - 0.5) * 2 + R.recency * recency);
|
|
@@ -231,6 +248,8 @@ export function converse(speaker, listener, ctx) {
|
|
|
231
248
|
? `${nameOf(speaker)} says ${c.about} is ${c.value ? '' : 'not '}${c.prop.split(':')[1] ?? ''}`
|
|
232
249
|
: `${nameOf(speaker)} says ${c.prop} is ${c.value ? 'so' : 'not so'}`,
|
|
233
250
|
};
|
|
251
|
+
if (ctx.phrase)
|
|
252
|
+
pc.summary = ctx.phrase(Object.freeze(structuredClone(c)), pc.summary ?? '');
|
|
234
253
|
const claim = pc.claims?.[0];
|
|
235
254
|
if (claim && c.origin !== undefined)
|
|
236
255
|
claim.origin = c.origin;
|
|
@@ -292,6 +311,9 @@ export function converse(speaker, listener, ctx) {
|
|
|
292
311
|
suggestion.appeal = 'benevolence';
|
|
293
312
|
out.advice.push({ action: a.action, percept, suggestion });
|
|
294
313
|
}
|
|
314
|
+
if (ctx.phrase)
|
|
315
|
+
for (const withheld of out.withheld)
|
|
316
|
+
withheld.summary = ctx.phrase(Object.freeze(structuredClone(withheld)), withheld.reason);
|
|
295
317
|
const magnitude = clamp01((ctx.magnitude ?? C.chatMagnitude) + C.chatPerClaim * out.told.length);
|
|
296
318
|
out.socialEvents.speaker.push({ at: ctx.at, kind: 'chat', otherId: listener.id, byMe: true, magnitude });
|
|
297
319
|
out.socialEvents.listener.push({ at: ctx.at, kind: 'chat', otherId: speaker.id, byMe: false, magnitude });
|
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.17.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';
|
|
@@ -32,7 +32,7 @@ export { enablePersistence, PERSISTENCE_DEFAULTS, pursuitOf } from './persistenc
|
|
|
32
32
|
export * from './person.ts';
|
|
33
33
|
export * from './persona.ts';
|
|
34
34
|
export * from './persona-calendar.ts';
|
|
35
|
-
export { considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal } from './projects/index.ts';
|
|
35
|
+
export { adoptProjectTask, considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal, } from './projects/index.ts';
|
|
36
36
|
export * from './promptings/index.ts';
|
|
37
37
|
export type { ConsequenceEvidence, PlanRoute, PlanStep } from './prospection/index.ts';
|
|
38
38
|
export { bodyForecastFor, bodyForecastTerm, consequenceEvidence, enablePlanning, FORECAST_DEFAULTS, forecastLine, forecastThresholds, PROSPECTION_DEFAULTS, seedConsequences, } from './prospection/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.17.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';
|
|
@@ -29,7 +29,7 @@ export { enablePersistence, PERSISTENCE_DEFAULTS, pursuitOf } from './persistenc
|
|
|
29
29
|
export * from './person.js';
|
|
30
30
|
export * from './persona.js';
|
|
31
31
|
export * from './persona-calendar.js';
|
|
32
|
-
export { considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal } from './projects/index.js';
|
|
32
|
+
export { adoptProjectTask, considerProjects, enableProjects, PROJECTS_DEFAULTS, projectAppeal, } from './projects/index.js';
|
|
33
33
|
export * from './promptings/index.js';
|
|
34
34
|
export { bodyForecastFor, bodyForecastTerm, consequenceEvidence, enablePlanning, FORECAST_DEFAULTS, forecastLine, forecastThresholds, PROSPECTION_DEFAULTS, seedConsequences, } from './prospection/index.js';
|
|
35
35
|
export { fingerprint, restore, snapshot } from './restore.js';
|
package/dist/migrate.js
CHANGED
|
@@ -71,6 +71,11 @@ export const MIGRATIONS = {
|
|
|
71
71
|
note: 'the surroundings field `ambient.now` is renamed `ambient.percept`; per-person retention is optional and starts absent; behaviour unchanged',
|
|
72
72
|
apply: renameAmbient,
|
|
73
73
|
},
|
|
74
|
+
'2.14.1': {
|
|
75
|
+
to: '2.17.0',
|
|
76
|
+
note: 'version stamp only; project protection and narration/social options require explicit opt-in, and existing enabled faculties retain exact behavior',
|
|
77
|
+
apply: stamp,
|
|
78
|
+
},
|
|
74
79
|
'2.14.0': {
|
|
75
80
|
to: '2.14.1',
|
|
76
81
|
note: 'version stamp only; restored projects and persistence retain 2.14.0 behaviour until explicitly re-enabled',
|
package/dist/narrate/lexicon.js
CHANGED
|
@@ -34,6 +34,23 @@ export const EN_LINES = {
|
|
|
34
34
|
// Decision lines by other dominant sources.
|
|
35
35
|
'source:habit': ['Same as always.', 'Out of habit, really.', "It's what I do at this hour."],
|
|
36
36
|
'source:effort': ['The easy thing, for now.', 'Nothing strenuous.'],
|
|
37
|
+
'review.cause': [
|
|
38
|
+
'After {cause}, {missed} remained undone. {next}',
|
|
39
|
+
'I left {missed} unfinished after {cause}. {next}',
|
|
40
|
+
'I was {cause}; {missed} went unfinished. {next}',
|
|
41
|
+
],
|
|
42
|
+
'review.accounting': [
|
|
43
|
+
"I didn't keep {missed}. Next time I'll be ready when it comes.",
|
|
44
|
+
'I left {missed} undone. I will prepare for the next occasion.',
|
|
45
|
+
'Today {missed} went unfinished. Next time I will make room for it.',
|
|
46
|
+
],
|
|
47
|
+
'review.ruminating': [
|
|
48
|
+
'I keep going back over {missed}.',
|
|
49
|
+
'My thoughts keep returning to {missed}.',
|
|
50
|
+
'I cannot stop turning {missed} over in my mind.',
|
|
51
|
+
],
|
|
52
|
+
'source:material.named': ['It brings {material}.', 'We can use the {material}.'],
|
|
53
|
+
'intention.material.named': ['for {material}'],
|
|
37
54
|
'source:material': ['There is money in it.', 'It pays.'],
|
|
38
55
|
'source:expectation': ['It went well last time.', 'I know how this goes.'],
|
|
39
56
|
'source:conscience:repair': ['I owe them that much.', 'I have to make this right.'],
|
|
@@ -363,6 +380,12 @@ function regularGerund(verb) {
|
|
|
363
380
|
export function actionForms(action, lexicon) {
|
|
364
381
|
const given = lexicon?.actions?.[action];
|
|
365
382
|
const base = given?.base ?? action.replace(/[-_]/g, ' ');
|
|
383
|
+
if (lexicon?.nounActions)
|
|
384
|
+
return {
|
|
385
|
+
base: given?.base ?? `do ${base}`,
|
|
386
|
+
past: given?.past ?? `did ${base}`,
|
|
387
|
+
gerund: given?.gerund ?? `doing ${base}`,
|
|
388
|
+
};
|
|
366
389
|
const [verb = base, ...rest] = base.split(' ');
|
|
367
390
|
const tail = rest.length > 0 ? ` ${rest.join(' ')}` : '';
|
|
368
391
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DecisionRecord, Lexicon, Person, SuggestionResolution } from '../types.ts';
|
|
1
|
+
import type { DecisionRecord, Episode, Lexicon, Person, SuggestionResolution } from '../types.ts';
|
|
2
2
|
/** Intention string for a decision: 'for Allah', 'to feed myself', 'to keep my promise to X', ... */
|
|
3
3
|
export declare function intentionFor(p: Person, record: DecisionRecord, lexicon?: Lexicon): string;
|
|
4
4
|
/** First-person narration of a decision record. Deterministic by the decision id. */
|
|
@@ -7,3 +7,9 @@ export declare function narrateDecision(p: Person, record: DecisionRecord, lexic
|
|
|
7
7
|
export declare function voiceLine(p: Person, res: SuggestionResolution, key?: string, lexicon?: Lexicon): string;
|
|
8
8
|
/** Short UI summary of a person's state. */
|
|
9
9
|
export declare function describePerson(p: Person, lexicon?: Lexicon): string;
|
|
10
|
+
/**
|
|
11
|
+
* SCOPE: a host-authored addressee form of an episode, selected only for its named actor.
|
|
12
|
+
* Free text cannot safely be inflected. With no authored form, return the original summary.
|
|
13
|
+
* This read-only helper neither recalls an episode nor changes its salience or RNG.
|
|
14
|
+
*/
|
|
15
|
+
export declare function recallLine(episode: Episode, toActor: string): string;
|
package/dist/narrate/narrate.js
CHANGED
|
@@ -93,7 +93,9 @@ export function intentionFor(p, record, lexicon) {
|
|
|
93
93
|
case 'emotion':
|
|
94
94
|
return phraseLine('intention.emotion', {}, lex);
|
|
95
95
|
case 'material':
|
|
96
|
-
return
|
|
96
|
+
return lex?.materialMeaning !== undefined
|
|
97
|
+
? phraseLine('intention.material.named', { material: lex.materialMeaning }, lex)
|
|
98
|
+
: phraseLine('intention.material', {}, lex);
|
|
97
99
|
case 'precommit':
|
|
98
100
|
return phraseLine('intention.precommit', {}, lex);
|
|
99
101
|
case 'expectation':
|
|
@@ -157,6 +159,8 @@ export function narrateDecision(p, record, lexicon) {
|
|
|
157
159
|
? pickLine(`emotion:${tag}`, id, {}, lex)
|
|
158
160
|
: pickLine('emotion:default', id, {}, lex));
|
|
159
161
|
}
|
|
162
|
+
else if (kind === 'material' && lex?.materialMeaning !== undefined)
|
|
163
|
+
parts.push(pickLine('source:material.named', id, { material: lex.materialMeaning }, lex));
|
|
160
164
|
else
|
|
161
165
|
parts.push(linesFor(`source:${source}`, lex).length > 0
|
|
162
166
|
? pickLine(`source:${source}`, id, {}, lex)
|
|
@@ -192,6 +196,8 @@ export function narrateDecision(p, record, lexicon) {
|
|
|
192
196
|
/** The person's spoken reply to a suggestion. */
|
|
193
197
|
export function voiceLine(p, res, key = res.reason, lexicon) {
|
|
194
198
|
const lex = lexicon ?? p.lexicon;
|
|
199
|
+
if (lex?.variety && (res.verdict === 'assented' || res.verdict === 'deferred'))
|
|
200
|
+
key = `${p.id}:${p.now}:${p.traits.extraversion}:${p.traits.honesty}:${key}`;
|
|
195
201
|
const trust = p.will.voices.find((v) => v.voiceId === res.voiceId)?.trust ?? 0.5;
|
|
196
202
|
switch (res.verdict) {
|
|
197
203
|
case 'assented':
|
|
@@ -293,3 +299,13 @@ export function describePerson(p, lexicon) {
|
|
|
293
299
|
const state = bits.length > 0 ? bits.join(', ') : phraseLine('describe.well', {}, lex);
|
|
294
300
|
return phraseLine('describe.line', { name: p.name, act, state }, lex);
|
|
295
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* SCOPE: a host-authored addressee form of an episode, selected only for its named actor.
|
|
304
|
+
* Free text cannot safely be inflected. With no authored form, return the original summary.
|
|
305
|
+
* This read-only helper neither recalls an episode nor changes its salience or RNG.
|
|
306
|
+
*/
|
|
307
|
+
export function recallLine(episode, toActor) {
|
|
308
|
+
return episode.actorId === toActor && episode.addressedSummary !== undefined
|
|
309
|
+
? episode.addressedSummary
|
|
310
|
+
: episode.summary;
|
|
311
|
+
}
|
package/dist/person.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type BodyForecastSpan } from './body/index.ts';
|
|
2
2
|
import { type NafsPattern } from './promptings/index.ts';
|
|
3
3
|
import { type SkillTransfer } from './skills/index.ts';
|
|
4
|
+
import { applyReputationBelief } from './social/index.ts';
|
|
4
5
|
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';
|
|
5
6
|
import { type CommandOutcome } from './will/index.ts';
|
|
6
7
|
export declare const PERSON_DEFAULTS: {
|
|
@@ -85,12 +86,16 @@ export declare function standUp(p: Person): boolean;
|
|
|
85
86
|
* only age (from `life.bornAt`) changes. Hosts that want the interval lived call `tick`.
|
|
86
87
|
*/
|
|
87
88
|
export declare function skip(p: Person, to: Minute): void;
|
|
89
|
+
export interface PerceiveOptions {
|
|
90
|
+
/** Complete host character-trait vocabulary; absent uses HF's default table. */
|
|
91
|
+
reputationTraits?: Parameters<typeof applyReputationBelief>[5];
|
|
92
|
+
}
|
|
88
93
|
/**
|
|
89
94
|
* Attend to percepts (limited capacity), then encode what got through: claims into beliefs, interactions
|
|
90
95
|
* into relationships, others' acts judged by my norms, appraisal into emotions, and an episode each.
|
|
91
96
|
* Returns the percepts that were attended to.
|
|
92
97
|
*/
|
|
93
|
-
export declare function perceive(p: Person, percepts: readonly Percept[]): Percept[];
|
|
98
|
+
export declare function perceive(p: Person, percepts: readonly Percept[], options?: PerceiveOptions): Percept[];
|
|
94
99
|
export interface DecideOptions {
|
|
95
100
|
suggestion?: Suggestion;
|
|
96
101
|
/** Several voices in one decision (N1); merged with `suggestion`, one per voice id. */
|
package/dist/person.js
CHANGED
|
@@ -38,7 +38,7 @@ import { intentionFor, narrateDecision, voiceLine } from './narrate/index.js';
|
|
|
38
38
|
import { advanceNeeds, createNeeds, levelOf, meanSatisfaction, readNeeds, satisfy, urgencyOf, } from './needs/index.js';
|
|
39
39
|
import { widow } from './partnering/index.js';
|
|
40
40
|
import { beginPursuit, enablePersistence, releasePursuit } from './persistence/index.js';
|
|
41
|
-
import { considerProjects, enableProjects, noteProjectStatus, perceiveProspect } from './projects/index.js';
|
|
41
|
+
import { considerProjects, enableProjects, noteProjectAct, noteProjectStatus, perceiveProspect, } from './projects/index.js';
|
|
42
42
|
import { enablePromptings, nafsPatterns, notePromptings } from './promptings/index.js';
|
|
43
43
|
import { bodyForecastFor, consumeConsequences, enablePlanning, forecastLine, forecastThresholds, pruneConsequences, receiveConsequences, } from './prospection/index.js';
|
|
44
44
|
import { enableReview, reviewIfDue } from './review/index.js';
|
|
@@ -585,10 +585,10 @@ function socialKindOf(kind) {
|
|
|
585
585
|
return kind;
|
|
586
586
|
return SOCIAL_ALIASES[kind];
|
|
587
587
|
}
|
|
588
|
-
function perceiveOne(p, pc) {
|
|
588
|
+
function perceiveOne(p, pc, options) {
|
|
589
589
|
receiveConsequences(p, pc);
|
|
590
590
|
if (p.projects && pc.prospect)
|
|
591
|
-
perceiveProspect(p, pc.prospect, pc.at);
|
|
591
|
+
perceiveProspect(p, pc.prospect, pc.at, (pc.channel === 'saw' || pc.channel === 'outcome') && (pc.actorId === undefined || pc.actorId === p.id));
|
|
592
592
|
const at = Math.min(Math.max(pc.at, p.affect.lastUpdated), p.now);
|
|
593
593
|
const actor = pc.actorId;
|
|
594
594
|
const target = pc.targetId;
|
|
@@ -606,7 +606,7 @@ function perceiveOne(p, pc) {
|
|
|
606
606
|
if (claim.origin !== undefined || claim.lineage !== undefined)
|
|
607
607
|
noteProvenance(p, claim, direct ? p.id : (actor ?? 'rumour'), at);
|
|
608
608
|
// Hearsay about someone's character moves my relationship with them (reputation).
|
|
609
|
-
applyReputationBelief(p, claim.prop, before, credence(p, claim.prop), at);
|
|
609
|
+
applyReputationBelief(p, claim.prop, before, credence(p, claim.prop), at, options.reputationTraits);
|
|
610
610
|
}
|
|
611
611
|
// Another source's account, reported by the teller (2.10.0): one source telling two people opposite things.
|
|
612
612
|
if (pc.channel === 'told' && actor !== undefined && Array.isArray(pc.reports)) {
|
|
@@ -724,8 +724,11 @@ function perceiveOne(p, pc) {
|
|
|
724
724
|
summary: pc.summary,
|
|
725
725
|
tags: [pc.kind, pc.channel, ...(isNight(at) ? ['night'] : []), ...(threat ? ['threat'] : [])],
|
|
726
726
|
};
|
|
727
|
-
if (actor !== undefined)
|
|
727
|
+
if (actor !== undefined) {
|
|
728
728
|
episode.actorId = actor;
|
|
729
|
+
if (pc.addressedSummary !== undefined)
|
|
730
|
+
episode.addressedSummary = pc.addressedSummary;
|
|
731
|
+
}
|
|
729
732
|
if (target !== undefined)
|
|
730
733
|
episode.targetId = target;
|
|
731
734
|
if (pc.placeId !== undefined)
|
|
@@ -748,7 +751,7 @@ function judgeDeed(p, actor, pc, at) {
|
|
|
748
751
|
* into relationships, others' acts judged by my norms, appraisal into emotions, and an episode each.
|
|
749
752
|
* Returns the percepts that were attended to.
|
|
750
753
|
*/
|
|
751
|
-
export function perceive(p, percepts) {
|
|
754
|
+
export function perceive(p, percepts, options = {}) {
|
|
752
755
|
if (percepts.length === 0 || !p.body.alive)
|
|
753
756
|
return [];
|
|
754
757
|
const body = readBodyOf(p);
|
|
@@ -758,7 +761,7 @@ export function perceive(p, percepts) {
|
|
|
758
761
|
fear: emotionLevel(p, 'fear'),
|
|
759
762
|
});
|
|
760
763
|
for (const pc of attended) {
|
|
761
|
-
perceiveOne(p, pc);
|
|
764
|
+
perceiveOne(p, pc, options);
|
|
762
765
|
// What I just noticed may bring a memory back (the cemetery road, her name on the phone).
|
|
763
766
|
const at = Math.min(Math.max(pc.at, p.affect.lastUpdated), p.now);
|
|
764
767
|
const cue = { at, action: pc.kind, tags: [pc.kind] };
|
|
@@ -1449,8 +1452,10 @@ export function finish(p, outcome, opts = {}) {
|
|
|
1449
1452
|
...(act.necessity ? { necessity: true } : {}),
|
|
1450
1453
|
...(opts.catalog ? { catalog: opts.catalog } : {}),
|
|
1451
1454
|
});
|
|
1452
|
-
if (p.projects)
|
|
1455
|
+
if (p.projects) {
|
|
1456
|
+
noteProjectAct(p, aff, completed, now);
|
|
1453
1457
|
noteProjectStatus(p, now);
|
|
1458
|
+
}
|
|
1454
1459
|
if (p.pursuit)
|
|
1455
1460
|
releasePursuit(p);
|
|
1456
1461
|
// A completed violating action inside an open abstention's window broke it (the fast, e.g. "no work after the evening prayer").
|
package/dist/persona.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* identity, age, family and health compatibility before returning a new Person. Neither input is mutated.
|
|
5
5
|
* This is not a world migration, identity registry, transport, hash, authentication or general calendar mapper.
|
|
6
6
|
* v1 admits zero-duration travel and healthy compatible bodies only; family/bonds remain host-authored and must
|
|
7
|
-
* match source provenance.
|
|
7
|
+
* match source provenance. Portable v1 accepts its published 2.14.1 stamp and the current engine;
|
|
8
|
+
* the 2.14.1 to 2.17.0 migration changes no portable field semantics. Pregnancy and nonempty temporal bonds are unsupported in v1. All other world-dependent stores stay with the source and are disclosed as omitted.
|
|
8
9
|
*/
|
|
9
10
|
import { hasUnresolvedMakeUps, sameChain } from './agenda/agenda.js';
|
|
10
11
|
import { createPerson } from './person.js';
|
|
@@ -185,7 +186,9 @@ export function validatePersonaProjection(input) {
|
|
|
185
186
|
text(input.mappingVersion);
|
|
186
187
|
text(input.name);
|
|
187
188
|
keys(input.source, ['schema', 'engine', 'id', 'now']);
|
|
188
|
-
|
|
189
|
+
// Both published passport releases used 2.14.1; the next engine upgrade is stamp-only here.
|
|
190
|
+
const compatibleEngine = input.source.engine === ENGINE_VERSION || input.source.engine === '2.14.1';
|
|
191
|
+
if (input.source.schema !== PERSON_SCHEMA || !compatibleEngine)
|
|
189
192
|
fail('unsupported source schema/engine; migrate the source before projection');
|
|
190
193
|
text(input.source.id);
|
|
191
194
|
num(input.source.now);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Minute, Person, ProjectsSpec, ProjectsState, Prospect } from '../types.ts';
|
|
1
|
+
import type { Goal, Minute, Person, ProjectsSpec, ProjectsState, Prospect } from '../types.ts';
|
|
2
2
|
export declare const PROJECTS_DEFAULTS: {
|
|
3
3
|
readonly maxProspects: 8;
|
|
4
4
|
readonly maxHistory: 8;
|
|
@@ -13,10 +13,16 @@ export declare const PROJECTS_DEFAULTS: {
|
|
|
13
13
|
};
|
|
14
14
|
/** Enable or reconfigure without discarding retained prospects and history. */
|
|
15
15
|
export declare function enableProjects(p: Person, spec?: ProjectsSpec): ProjectsState;
|
|
16
|
+
/** Turn an active held project into a host task. Undefined means there is no active held project.
|
|
17
|
+
* Reuses identity, adoption time, progress, peak and progress clock; never revives an abandoned project.
|
|
18
|
+
* Retains its temporal horizon unless the task supplies a valid replacement.
|
|
19
|
+
* The caller supplies task meaning and deadlines. No second goal or fresh progress is created.
|
|
20
|
+
*/
|
|
21
|
+
export declare function adoptProjectTask(p: Person, prospectId: string, task: Omit<Goal, 'id' | 'status' | 'progress' | 'adoptedAt' | 'project' | 'lastAdvancedAt'>, now?: Minute): Goal | undefined;
|
|
16
22
|
/** Read-only, deterministic appeal. Thresholds close the relaxed gate before the bounded bonus. */
|
|
17
23
|
export declare function projectAppeal(p: Person, prospect: Prospect, now?: Minute): number;
|
|
18
24
|
/** Only the composite calls this, after attention. Newest event minute wins; equal-minute input order wins. */
|
|
19
|
-
export declare function perceiveProspect(p: Person, input: Prospect, at: Minute): void;
|
|
25
|
+
export declare function perceiveProspect(p: Person, input: Prospect, at: Minute, attributeOwnAct?: boolean): void;
|
|
20
26
|
/** Called on each tick segment, including one-minute begin/tick/finish host loops. No RNG. */
|
|
21
27
|
export declare function considerProjects(p: Person, now: Minute): void;
|
|
22
28
|
/** Conservative restore: bounded fields, finite past timestamps, and no ambiguous duplicate ids. */
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
* goals. All kinds serve competence and leisure: making is chosen as a free-time pursuit, not duty.
|
|
10
10
|
* Behaviour 2 credits only new perceived highs; event-credited projects cannot observe reversals and stay unchanged.
|
|
11
11
|
* Restore recovers one active project, keeping the newest (id breaks ties) and abandoning extras.
|
|
12
|
-
*
|
|
12
|
+
* Explicit protectProgress blocks goal credit for host-declared reductions and exact contextual acts
|
|
13
|
+
* followed within five minutes by an attended drop after one's own completion. This is a bounded
|
|
14
|
+
* causal assumption, not proof of causation. Only direct saw/outcome readings without another actor
|
|
15
|
+
* can teach that link. It learns no effects from unattended world state or testimony.
|
|
16
|
+
* adoptProjectTask reuses an active project goal, preserving net credit and pursuit identity.
|
|
17
|
+
* No invented prospects, implicit host task interpretation, world search, persistence, shielding, faith lens,
|
|
13
18
|
* LLM or RNG. Without the slice, perception and minute bookkeeping write nothing.
|
|
14
19
|
*/
|
|
15
20
|
import { emotionLevel } from '../affect/index.js';
|
|
@@ -44,6 +49,8 @@ function cleanSpec(input) {
|
|
|
44
49
|
const out = {};
|
|
45
50
|
if (!isObj(input))
|
|
46
51
|
return out;
|
|
52
|
+
if (typeof input.protectProgress === 'boolean')
|
|
53
|
+
out.protectProgress = input.protectProgress;
|
|
47
54
|
for (const key of KEYS) {
|
|
48
55
|
const x = input[key];
|
|
49
56
|
const unit = key === 'calmUrgency' || key === 'fearBelow' || key === 'adoptAppeal';
|
|
@@ -90,6 +97,70 @@ export function enableProjects(p, spec = {}) {
|
|
|
90
97
|
p.projects = state;
|
|
91
98
|
return state;
|
|
92
99
|
}
|
|
100
|
+
/** Whether this offer is understood to undo this prospect. Read-only; exact context, no action-only generalisation. @internal */
|
|
101
|
+
export function diminishesProject(p, aff, prospectId) {
|
|
102
|
+
const s = p.projects;
|
|
103
|
+
return (s?.spec?.protectProgress === true &&
|
|
104
|
+
((aff.diminishes?.includes(prospectId) ?? false) ||
|
|
105
|
+
(s.setbacks?.some((x) => x.prospectId === prospectId &&
|
|
106
|
+
x.action === aff.action &&
|
|
107
|
+
x.targetId === aff.targetId &&
|
|
108
|
+
x.placeId === aff.placeId) ??
|
|
109
|
+
false)));
|
|
110
|
+
}
|
|
111
|
+
/** Own completed acts alone can explain the next attended drop. Failed/interrupted acts clear attribution. @internal */
|
|
112
|
+
export function noteProjectAct(p, aff, completed, at) {
|
|
113
|
+
const s = p.projects;
|
|
114
|
+
if (!s?.spec?.protectProgress)
|
|
115
|
+
return;
|
|
116
|
+
delete s.pendingAct;
|
|
117
|
+
if (!completed ||
|
|
118
|
+
!str(aff.action) ||
|
|
119
|
+
(aff.targetId !== undefined && !str(aff.targetId)) ||
|
|
120
|
+
(aff.placeId !== undefined && !str(aff.placeId)))
|
|
121
|
+
return;
|
|
122
|
+
const readings = s.prospects
|
|
123
|
+
.filter((x) => x.progress !== undefined &&
|
|
124
|
+
p.agenda.goals.some((g) => g.status === 'active' && g.project?.prospectId === x.id))
|
|
125
|
+
.map((x) => ({ prospectId: x.id, progress: x.progress }));
|
|
126
|
+
if (!readings.length)
|
|
127
|
+
return;
|
|
128
|
+
s.pendingAct = {
|
|
129
|
+
action: aff.action,
|
|
130
|
+
at,
|
|
131
|
+
readings,
|
|
132
|
+
...(aff.targetId !== undefined ? { targetId: aff.targetId } : {}),
|
|
133
|
+
...(aff.placeId !== undefined ? { placeId: aff.placeId } : {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** Turn an active held project into a host task. Undefined means there is no active held project.
|
|
137
|
+
* Reuses identity, adoption time, progress, peak and progress clock; never revives an abandoned project.
|
|
138
|
+
* Retains its temporal horizon unless the task supplies a valid replacement.
|
|
139
|
+
* The caller supplies task meaning and deadlines. No second goal or fresh progress is created.
|
|
140
|
+
*/
|
|
141
|
+
export function adoptProjectTask(p, prospectId, task, now = p.now) {
|
|
142
|
+
if (!p.projects || !minute(now, p.now))
|
|
143
|
+
return undefined;
|
|
144
|
+
const held = p.agenda.goals.find((g) => g.status === 'active' && g.project?.prospectId === prospectId);
|
|
145
|
+
if (!held)
|
|
146
|
+
return undefined;
|
|
147
|
+
// Use agenda's copying and sanitization, then retain the existing goal rather than allocating a new id.
|
|
148
|
+
const nextId = p.agenda.nextId;
|
|
149
|
+
const staged = adoptGoal(p, task, now);
|
|
150
|
+
p.agenda.goals.pop();
|
|
151
|
+
p.agenda.nextId = nextId;
|
|
152
|
+
held.label = staged.label;
|
|
153
|
+
held.serves = staged.serves;
|
|
154
|
+
held.advancedBy = staged.advancedBy;
|
|
155
|
+
held.importance = staged.importance;
|
|
156
|
+
held.baseImportance = staged.baseImportance;
|
|
157
|
+
delete held.deadline;
|
|
158
|
+
if (staged.deadline !== undefined)
|
|
159
|
+
held.deadline = staged.deadline;
|
|
160
|
+
if (staged.temporal !== undefined)
|
|
161
|
+
held.temporal = staged.temporal;
|
|
162
|
+
return held;
|
|
163
|
+
}
|
|
93
164
|
function relaxed(p) {
|
|
94
165
|
const needs = readPerson(p).needs;
|
|
95
166
|
return {
|
|
@@ -166,7 +237,7 @@ export function noteProjectStatus(p, now) {
|
|
|
166
237
|
recordProjectStatus(p, g, now);
|
|
167
238
|
}
|
|
168
239
|
/** Only the composite calls this, after attention. Newest event minute wins; equal-minute input order wins. */
|
|
169
|
-
export function perceiveProspect(p, input, at) {
|
|
240
|
+
export function perceiveProspect(p, input, at, attributeOwnAct = false) {
|
|
170
241
|
const s = p.projects;
|
|
171
242
|
if (!s || !minute(at, p.now))
|
|
172
243
|
return;
|
|
@@ -178,6 +249,32 @@ export function perceiveProspect(p, input, at) {
|
|
|
178
249
|
return;
|
|
179
250
|
if (p.now - at > setting(s, 'forgetMinutes'))
|
|
180
251
|
return;
|
|
252
|
+
const act = s.pendingAct;
|
|
253
|
+
if (s.spec?.protectProgress && act && at >= act.at) {
|
|
254
|
+
const before = act.readings.find((x) => x.prospectId === prospect.id);
|
|
255
|
+
if (before && prospect.progress !== undefined) {
|
|
256
|
+
if (attributeOwnAct && at - act.at <= 5 && prospect.progress < before.progress) {
|
|
257
|
+
const learned = {
|
|
258
|
+
prospectId: prospect.id,
|
|
259
|
+
action: act.action,
|
|
260
|
+
...(act.targetId !== undefined ? { targetId: act.targetId } : {}),
|
|
261
|
+
...(act.placeId !== undefined ? { placeId: act.placeId } : {}),
|
|
262
|
+
};
|
|
263
|
+
const list = s.setbacks ?? [];
|
|
264
|
+
if (!list.some((x) => x.prospectId === learned.prospectId &&
|
|
265
|
+
x.action === learned.action &&
|
|
266
|
+
x.targetId === learned.targetId &&
|
|
267
|
+
x.placeId === learned.placeId))
|
|
268
|
+
list.push(learned);
|
|
269
|
+
s.setbacks = list.slice(-16);
|
|
270
|
+
}
|
|
271
|
+
act.readings = act.readings.filter((x) => x.prospectId !== prospect.id);
|
|
272
|
+
if (!act.readings.length)
|
|
273
|
+
delete s.pendingAct;
|
|
274
|
+
}
|
|
275
|
+
if (at - act.at > 5)
|
|
276
|
+
delete s.pendingAct;
|
|
277
|
+
}
|
|
181
278
|
const held = { ...prospect, lastSeenAt: at };
|
|
182
279
|
if (old)
|
|
183
280
|
s.prospects[s.prospects.indexOf(old)] = held;
|
|
@@ -268,6 +365,49 @@ export function sanitizeProjects(input, now) {
|
|
|
268
365
|
return undefined;
|
|
269
366
|
out.currentGoalId = input.currentGoalId;
|
|
270
367
|
}
|
|
368
|
+
if (input.setbacks !== undefined) {
|
|
369
|
+
if (!Array.isArray(input.setbacks) || input.setbacks.length > 16)
|
|
370
|
+
return undefined;
|
|
371
|
+
out.setbacks = [];
|
|
372
|
+
for (const x of input.setbacks) {
|
|
373
|
+
if (!isObj(x) ||
|
|
374
|
+
!str(x.prospectId) ||
|
|
375
|
+
!str(x.action) ||
|
|
376
|
+
(x.targetId !== undefined && !str(x.targetId)) ||
|
|
377
|
+
(x.placeId !== undefined && !str(x.placeId)))
|
|
378
|
+
return undefined;
|
|
379
|
+
out.setbacks.push({
|
|
380
|
+
prospectId: x.prospectId,
|
|
381
|
+
action: x.action,
|
|
382
|
+
...(x.targetId !== undefined ? { targetId: x.targetId } : {}),
|
|
383
|
+
...(x.placeId !== undefined ? { placeId: x.placeId } : {}),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const act = input.pendingAct;
|
|
388
|
+
if (act !== undefined) {
|
|
389
|
+
if (!isObj(act) ||
|
|
390
|
+
!str(act.action) ||
|
|
391
|
+
!minute(act.at, now) ||
|
|
392
|
+
(act.targetId !== undefined && !str(act.targetId)) ||
|
|
393
|
+
(act.placeId !== undefined && !str(act.placeId)) ||
|
|
394
|
+
!Array.isArray(act.readings) ||
|
|
395
|
+
act.readings.length > PROJECTS_DEFAULTS.maxProspects)
|
|
396
|
+
return undefined;
|
|
397
|
+
const readings = [];
|
|
398
|
+
for (const x of act.readings) {
|
|
399
|
+
if (!isObj(x) || !str(x.prospectId) || !unit(x.progress))
|
|
400
|
+
return undefined;
|
|
401
|
+
readings.push({ prospectId: x.prospectId, progress: x.progress });
|
|
402
|
+
}
|
|
403
|
+
out.pendingAct = {
|
|
404
|
+
action: act.action,
|
|
405
|
+
at: act.at,
|
|
406
|
+
readings,
|
|
407
|
+
...(act.targetId !== undefined ? { targetId: act.targetId } : {}),
|
|
408
|
+
...(act.placeId !== undefined ? { placeId: act.placeId } : {}),
|
|
409
|
+
};
|
|
410
|
+
}
|
|
271
411
|
const seen = new Set();
|
|
272
412
|
for (const entry of input.prospects.slice(0, PROJECTS_DEFAULTS.maxProspects)) {
|
|
273
413
|
const prospect = cleanProspect(entry);
|
|
@@ -70,7 +70,9 @@ export interface PromptingsNoted {
|
|
|
70
70
|
*/
|
|
71
71
|
export declare function notePromptings(p: Person, seen: readonly PromptingSeen[], chosenId: string | null, now: Minute): PromptingsNoted;
|
|
72
72
|
/** Vocabulary for one prompt under a held reading (narration only; it changes nothing). */
|
|
73
|
-
export declare function promptingLine(reading: PromptingReading, outcome: PromptingRecord['outcome'], kind?: PromptingResponseKind
|
|
73
|
+
export declare function promptingLine(reading: PromptingReading, outcome: PromptingRecord['outcome'], kind?: PromptingResponseKind, options?: {
|
|
74
|
+
voice?: 'first' | 'third';
|
|
75
|
+
}): string;
|
|
74
76
|
/** Read-only summary of recent prompts (a lens; computes nothing new about the person). */
|
|
75
77
|
export interface PromptingsReadingView {
|
|
76
78
|
reading: PromptingReading;
|
|
@@ -256,7 +256,19 @@ export function notePromptings(p, seen, chosenId, now) {
|
|
|
256
256
|
return noted;
|
|
257
257
|
}
|
|
258
258
|
/** Vocabulary for one prompt under a held reading (narration only; it changes nothing). */
|
|
259
|
-
export function promptingLine(reading, outcome, kind) {
|
|
259
|
+
export function promptingLine(reading, outcome, kind, options = {}) {
|
|
260
|
+
// Plain third-person observations describe the response, without inferring the prompt's origin.
|
|
261
|
+
if (options.voice === 'third') {
|
|
262
|
+
if (outcome === 'assented')
|
|
263
|
+
return 'an urge came and was acted on';
|
|
264
|
+
if (outcome === 'turned')
|
|
265
|
+
return kind === 'refuge'
|
|
266
|
+
? 'an urge came; the person sought refuge and let it pass'
|
|
267
|
+
: kind === 'redirect'
|
|
268
|
+
? 'an urge came; the person turned to something else'
|
|
269
|
+
: 'an urge came; the person held back';
|
|
270
|
+
return 'an urge came and passed';
|
|
271
|
+
}
|
|
260
272
|
if (outcome === 'assented')
|
|
261
273
|
return reading === 'whisper'
|
|
262
274
|
? 'a whisper came and was followed'
|
|
@@ -17,6 +17,7 @@ import { temporalGoalValue } from '../agenda/agenda.js';
|
|
|
17
17
|
import { trustOf } from '../beliefs/index.js';
|
|
18
18
|
import { cmpStr, isNum, isObj, round } from '../core/index.js';
|
|
19
19
|
import { expectedEffect } from '../memory/index.js';
|
|
20
|
+
import { diminishesProject } from '../projects/index.js';
|
|
20
21
|
import { MAX_MINUTE, MINUTES_PER_DAY } from '../types.js';
|
|
21
22
|
export const PROSPECTION_DEFAULTS = {
|
|
22
23
|
maxEntries: 16,
|
|
@@ -92,6 +93,10 @@ function consequence(x, receivedAt) {
|
|
|
92
93
|
(!Array.isArray(s.advances) ||
|
|
93
94
|
s.advances.length > 8 ||
|
|
94
95
|
!s.advances.every((x) => typeof x === 'string' && x.length > 0 && x.length <= 512))) ||
|
|
96
|
+
(s.diminishes !== undefined &&
|
|
97
|
+
(!Array.isArray(s.diminishes) ||
|
|
98
|
+
s.diminishes.length > 8 ||
|
|
99
|
+
!s.diminishes.every((x) => typeof x === 'string' && x.length > 0 && x.length <= 512))) ||
|
|
95
100
|
(s.focus !== undefined && !unit(s.focus)) ||
|
|
96
101
|
(s.materialCost !== undefined &&
|
|
97
102
|
(!isNum(s.materialCost) || s.materialCost < 0 || s.materialCost > PROSPECTION_DEFAULTS.maxMaterialCost)))
|
|
@@ -115,6 +120,7 @@ function consequence(x, receivedAt) {
|
|
|
115
120
|
duration: s.duration,
|
|
116
121
|
effort: s.effort,
|
|
117
122
|
...(s.advances !== undefined ? { advances: [...s.advances] } : {}),
|
|
123
|
+
...(s.diminishes !== undefined ? { diminishes: [...s.diminishes] } : {}),
|
|
118
124
|
...(s.focus !== undefined ? { focus: s.focus } : {}),
|
|
119
125
|
...(s.materialCost !== undefined ? { materialCost: s.materialCost } : {}),
|
|
120
126
|
},
|
|
@@ -262,7 +268,9 @@ function successorOffer(e) {
|
|
|
262
268
|
}
|
|
263
269
|
/** Select a whole route in the exact rounded units cognition emits. @internal */
|
|
264
270
|
export function receivedGoalRoute(p, aff, now, maturityScale, costs) {
|
|
265
|
-
const goals = p.agenda.goals.filter((g) => g.status === 'active' &&
|
|
271
|
+
const goals = p.agenda.goals.filter((g) => g.status === 'active' &&
|
|
272
|
+
g.temporal !== undefined &&
|
|
273
|
+
!(g.project && diminishesProject(p, aff, g.project.prospectId)));
|
|
266
274
|
if (!goals.length || !p.prospection?.entries.length)
|
|
267
275
|
return undefined;
|
|
268
276
|
const direct = goals.map((g) => temporalGoalValue(p, g, aff, now, now + Math.max(0, aff.duration)));
|
|
@@ -297,7 +305,12 @@ export function receivedGoalRoute(p, aff, now, maturityScale, costs) {
|
|
|
297
305
|
entry.confidence *
|
|
298
306
|
reliability(p, entry);
|
|
299
307
|
const successor = successorOffer(entry);
|
|
300
|
-
const nextValues = goals.map((g, i) =>
|
|
308
|
+
const nextValues = goals.map((g, i) => {
|
|
309
|
+
const prospectId = g.project?.prospectId;
|
|
310
|
+
return prospectId && [...done, previous, successor].some((a) => diminishesProject(p, a, prospectId))
|
|
311
|
+
? 0
|
|
312
|
+
: Math.max(values[i] ?? 0, nextAccess * beta * temporalGoalValue(p, g, successor, now, end));
|
|
313
|
+
});
|
|
301
314
|
const nextCost = accumulatedCost + beta * nextAccess * Math.min(0, costs(successor));
|
|
302
315
|
const cost = round(nextCost);
|
|
303
316
|
const total = nextValues.reduce((sum, value) => sum + round(value * maturityScale), 0) + cost;
|
package/dist/review/review.d.ts
CHANGED
|
@@ -42,6 +42,9 @@ export declare function reviewIfDue(p: Person, now: Minute, recent: readonly Day
|
|
|
42
42
|
distress: number;
|
|
43
43
|
} | undefined;
|
|
44
44
|
/** The person's own words for their latest review, or undefined when they hold no review. */
|
|
45
|
-
export declare function reviewLine(p: Person
|
|
45
|
+
export declare function reviewLine(p: Person, options?: {
|
|
46
|
+
variety?: boolean;
|
|
47
|
+
cause?: 'ordered' | 'interrupted';
|
|
48
|
+
}): string | undefined;
|
|
46
49
|
/** Restore guard: drop malformed fields, keep valid resolves. */
|
|
47
50
|
export declare function sanitizeReview(x: unknown, now?: Minute): ReviewState | undefined;
|
package/dist/review/review.js
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* fast-forward) should still live, or release, that day's occasions, or the review counts each one as missed.
|
|
32
32
|
*/
|
|
33
33
|
import { clamp01, dexp, isObj, minuteOfDay, round } from '../core/index.js';
|
|
34
|
+
import { pickLine } from '../narrate/lexicon.js';
|
|
34
35
|
export const REVIEW_DEFAULTS = {
|
|
35
36
|
/** Default evening review time, minute of day. */
|
|
36
37
|
at: 21 * 60,
|
|
@@ -192,7 +193,7 @@ export function reviewIfDue(p, now, recent, closed) {
|
|
|
192
193
|
return { record, distress };
|
|
193
194
|
}
|
|
194
195
|
/** The person's own words for their latest review, or undefined when they hold no review. */
|
|
195
|
-
export function reviewLine(p) {
|
|
196
|
+
export function reviewLine(p, options = {}) {
|
|
196
197
|
const st = p.review;
|
|
197
198
|
const r = st?.last;
|
|
198
199
|
if (!st || st.style === 'none' || !r)
|
|
@@ -200,6 +201,16 @@ export function reviewLine(p) {
|
|
|
200
201
|
const list = (xs) => xs.length <= 1 ? (xs[0] ?? '') : `${xs.slice(0, -1).join(', ')} and ${xs.at(-1)}`;
|
|
201
202
|
if (r.missed.length === 0)
|
|
202
203
|
return r.kept.length > 0 ? `Today I kept ${list(r.kept)}.` : undefined;
|
|
204
|
+
if (options.cause) {
|
|
205
|
+
const cause = options.cause === 'ordered' ? 'following an order' : 'being interrupted';
|
|
206
|
+
const next = options.cause === 'ordered'
|
|
207
|
+
? 'Next time I will say what the order would put aside.'
|
|
208
|
+
: 'Next time I will make room to return to it.';
|
|
209
|
+
return pickLine('review.cause', `${p.id}:${r.day}`, { cause, missed: list(r.missed), next }, p.lexicon);
|
|
210
|
+
}
|
|
211
|
+
if (options.variety ?? p.lexicon?.variety) {
|
|
212
|
+
return pickLine(`review.${st.style}`, `${p.id}:${r.day}`, { missed: list(r.missed) }, p.lexicon);
|
|
213
|
+
}
|
|
203
214
|
if (st.style === 'ruminating')
|
|
204
215
|
return `I keep going back over ${list(r.missed)}.`;
|
|
205
216
|
return `I didn't keep ${list(r.missed)}. Next time I'll be ready when it comes.`;
|
package/dist/types.d.ts
CHANGED
|
@@ -373,6 +373,8 @@ export interface Episode {
|
|
|
373
373
|
/** 0..1 how memorable; decays and is boosted by recall. */
|
|
374
374
|
salience: Unit;
|
|
375
375
|
summary: string;
|
|
376
|
+
/** Host-authored summary spoken back to actorId (second person); never inferred from free text. */
|
|
377
|
+
addressedSummary?: string;
|
|
376
378
|
tags: string[];
|
|
377
379
|
/**
|
|
378
380
|
* The outside voice whose suggestion led to this episode (set on outcomes of assented/complied activities and
|
|
@@ -896,7 +898,7 @@ export declare const PERSON_SCHEMA = "human/person@1";
|
|
|
896
898
|
* and an activity that would cover a closing stretch is reviewed when the stretch begins (`isUnderWay`,
|
|
897
899
|
* `dutyReviewAt`). Person shape unchanged.
|
|
898
900
|
*/
|
|
899
|
-
export declare const ENGINE_VERSION = "2.
|
|
901
|
+
export declare const ENGINE_VERSION = "2.17.0";
|
|
900
902
|
/** A pregnancy carried by this person (host-driven; see `family/`). */
|
|
901
903
|
export interface Pregnancy {
|
|
902
904
|
fatherId: PersonId;
|
|
@@ -1013,6 +1015,8 @@ export interface ReceivedConsequence {
|
|
|
1013
1015
|
effort: Unit;
|
|
1014
1016
|
focus?: Unit;
|
|
1015
1017
|
materialCost?: number;
|
|
1018
|
+
/** Prospect ids this successor reduces; read only with projects protectProgress. */
|
|
1019
|
+
diminishes?: string[];
|
|
1016
1020
|
/** Goal or prospect ids this successor contributes to (bounded to 8). */
|
|
1017
1021
|
advances?: string[];
|
|
1018
1022
|
};
|
|
@@ -1186,6 +1190,8 @@ export interface Affordance {
|
|
|
1186
1190
|
* earliest-ending match, explicit or by action, as before.
|
|
1187
1191
|
*/
|
|
1188
1192
|
fulfills?: string[];
|
|
1193
|
+
/** Prospect ids this offer reduces; read only with projects protectProgress. */
|
|
1194
|
+
diminishes?: string[];
|
|
1189
1195
|
/** Goal ids this advances. */
|
|
1190
1196
|
advances?: string[];
|
|
1191
1197
|
/** Probability of harm and its severity, as advertised. */
|
|
@@ -1251,6 +1257,8 @@ export interface Percept {
|
|
|
1251
1257
|
aboutId?: PersonId;
|
|
1252
1258
|
};
|
|
1253
1259
|
summary: string;
|
|
1260
|
+
/** Host-authored summary spoken back to actorId (second person); never inferred from free text. */
|
|
1261
|
+
addressedSummary?: string;
|
|
1254
1262
|
/**
|
|
1255
1263
|
* Host flag for `sim/` interrupts. true: this percept is near the person (the framework has no spatial model),
|
|
1256
1264
|
* so a salient one forces re-decision even if it does not target them. false: never interrupts.
|
|
@@ -1759,6 +1767,12 @@ export interface VoiceConflict {
|
|
|
1759
1767
|
* slots documented next to each key.
|
|
1760
1768
|
*/
|
|
1761
1769
|
export interface Lexicon {
|
|
1770
|
+
/** Include person identity and stable temperament in assent/deferral selection (no RNG draws). */
|
|
1771
|
+
variety?: boolean;
|
|
1772
|
+
/** What material gains mean here, e.g. 'food', 'household keep' or 'resources'; absent keeps legacy money lines. */
|
|
1773
|
+
materialMeaning?: string;
|
|
1774
|
+
/** Treat action labels as noun phrases unless explicit verb forms are supplied. */
|
|
1775
|
+
nounActions?: boolean;
|
|
1762
1776
|
/** Informational locale tag, e.g. 'en', 'tr'. */
|
|
1763
1777
|
locale?: string;
|
|
1764
1778
|
/** Display names by entity id: { ana: 'Ana', ben: 'Ben' }. */
|
|
@@ -1987,6 +2001,8 @@ export interface Prospect {
|
|
|
1987
2001
|
progress?: Unit;
|
|
1988
2002
|
}
|
|
1989
2003
|
export interface ProjectsSpec {
|
|
2004
|
+
/** Learn and respect declared reductions of a held project. Absent preserves existing choice. */
|
|
2005
|
+
protectProgress?: boolean;
|
|
1990
2006
|
forgetMinutes?: number;
|
|
1991
2007
|
calmUrgency?: Unit;
|
|
1992
2008
|
fearBelow?: Unit;
|
|
@@ -2008,6 +2024,24 @@ export interface ProjectsState {
|
|
|
2008
2024
|
/** Absent retains 2.14.0 credit. Re-enabling, including on every load, opts into net credit. */
|
|
2009
2025
|
behaviour?: 2;
|
|
2010
2026
|
spec?: ProjectsSpec;
|
|
2027
|
+
/** Exact contextual acts learned to reduce a prospect; oldest entries are evicted. */
|
|
2028
|
+
setbacks?: {
|
|
2029
|
+
prospectId: string;
|
|
2030
|
+
action: string;
|
|
2031
|
+
targetId?: EntityId;
|
|
2032
|
+
placeId?: EntityId;
|
|
2033
|
+
}[];
|
|
2034
|
+
/** Last completed act and attended progress before it; consumed by the next matching perception. */
|
|
2035
|
+
pendingAct?: {
|
|
2036
|
+
action: string;
|
|
2037
|
+
targetId?: EntityId;
|
|
2038
|
+
placeId?: EntityId;
|
|
2039
|
+
at: Minute;
|
|
2040
|
+
readings: {
|
|
2041
|
+
prospectId: string;
|
|
2042
|
+
progress: Unit;
|
|
2043
|
+
}[];
|
|
2044
|
+
};
|
|
2011
2045
|
prospects: (Prospect & {
|
|
2012
2046
|
lastSeenAt: Minute;
|
|
2013
2047
|
})[];
|
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.17.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adam0white/human-framework",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.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",
|