@adam0white/human-framework 2.18.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/agenda/agenda.d.ts +47 -0
- package/dist/agenda/agenda.js +4 -1
- package/dist/body/body.d.ts +10 -0
- package/dist/body/body.js +15 -0
- package/dist/chronicle/story.js +3 -2
- package/dist/cognition/cognition.d.ts +37 -0
- package/dist/cognition/cognition.js +3 -2
- package/dist/conversation/conversation.js +10 -4
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/keys.d.ts +6 -0
- package/dist/core/keys.js +8 -0
- package/dist/family/family.js +5 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/longrun.js +1 -1
- package/dist/memory/memory.d.ts +26 -0
- package/dist/memory/memory.js +3 -9
- package/dist/narrate/lexicon.d.ts +2 -1
- package/dist/narrate/lexicon.js +2 -1
- package/dist/narrate/narrate.d.ts +14 -1
- package/dist/narrate/narrate.js +81 -39
- package/dist/narrate/plain.d.ts +7 -0
- package/dist/narrate/plain.js +293 -0
- package/dist/observed-reputation.d.ts +4 -0
- package/dist/observed-reputation.js +16 -0
- package/dist/partnering/partnering.js +1 -1
- package/dist/person.d.ts +5 -0
- package/dist/person.js +119 -11
- package/dist/presentation.d.ts +10 -0
- package/dist/presentation.js +88 -0
- package/dist/promptings/promptings.d.ts +1 -0
- package/dist/promptings/promptings.js +40 -4
- package/dist/prospection/forecast.d.ts +2 -0
- package/dist/prospection/forecast.js +15 -1
- package/dist/qalb.d.ts +28 -0
- package/dist/qalb.js +67 -0
- package/dist/review/review.d.ts +32 -0
- package/dist/review/review.js +6 -2
- package/dist/scenarios/village.js +63 -26
- package/dist/sim/longrun.js +20 -3
- package/dist/sim/sim.d.ts +1 -1
- package/dist/sim/sim.js +93 -15
- package/dist/social/social.js +12 -2
- package/dist/types.d.ts +6 -0
- package/dist/will/will.d.ts +12 -0
- package/dist/will/will.js +43 -4
- package/package.json +1 -1
|
@@ -257,6 +257,7 @@ export function createVillage(people, opts) {
|
|
|
257
257
|
return out;
|
|
258
258
|
};
|
|
259
259
|
const resolve = (p, act, reason) => {
|
|
260
|
+
const say = (legacy, plain) => (p.lexicon?.plainEnglish ? plain : legacy);
|
|
260
261
|
const now = p.now;
|
|
261
262
|
state.now = Math.max(state.now, now);
|
|
262
263
|
const aff = act.affordance;
|
|
@@ -272,12 +273,16 @@ export function createVillage(people, opts) {
|
|
|
272
273
|
// Partial work counts: a field shift left early yields and pays pro rata for the time put in.
|
|
273
274
|
const span = Math.max(1, act.endsAt - act.startedAt);
|
|
274
275
|
const progress = clamp01((now - act.startedAt) / span);
|
|
275
|
-
const out = {
|
|
276
|
+
const out = {
|
|
277
|
+
...base,
|
|
278
|
+
status: 'interrupted',
|
|
279
|
+
summary: say(`${aff.label}: interrupted`, `I was interrupted during the task “${aff.label}”.`),
|
|
280
|
+
};
|
|
276
281
|
if (act.action === 'work-field' && progress > 0) {
|
|
277
282
|
state.food += V.fieldYield * progress;
|
|
278
283
|
out.material = V.wage * progress;
|
|
279
284
|
out.needs = { competence: 0.1 * progress };
|
|
280
|
-
out.summary = `${aff.label}: stopped ${Math.round(progress * 100)}% of the way through
|
|
285
|
+
out.summary = say(`${aff.label}: stopped ${Math.round(progress * 100)}% of the way through`, `I stopped the task “${aff.label}” after completing ${Math.round(progress * 100)}% of it.`);
|
|
281
286
|
}
|
|
282
287
|
return out;
|
|
283
288
|
}
|
|
@@ -291,17 +296,29 @@ export function createVillage(people, opts) {
|
|
|
291
296
|
case 'eat':
|
|
292
297
|
if (state.food >= 1) {
|
|
293
298
|
state.food -= 1;
|
|
294
|
-
return done({
|
|
299
|
+
return done({
|
|
300
|
+
...base,
|
|
301
|
+
needs: { food: 0.6 },
|
|
302
|
+
summary: say('ate at the kitchen', 'I ate at the kitchen.'),
|
|
303
|
+
});
|
|
295
304
|
}
|
|
296
|
-
return {
|
|
305
|
+
return {
|
|
306
|
+
...base,
|
|
307
|
+
status: 'failed',
|
|
308
|
+
summary: say('found the kitchen empty', 'I found the kitchen empty.'),
|
|
309
|
+
};
|
|
297
310
|
case 'drink':
|
|
298
|
-
return done({
|
|
311
|
+
return done({
|
|
312
|
+
...base,
|
|
313
|
+
needs: { water: 0.6 },
|
|
314
|
+
summary: say('drank at the well', 'I drank at the well.'),
|
|
315
|
+
});
|
|
299
316
|
case 'sleep':
|
|
300
|
-
return done({ ...base, summary: 'slept' });
|
|
317
|
+
return done({ ...base, summary: say('slept', 'I slept.') });
|
|
301
318
|
case 'rest':
|
|
302
|
-
return done({ ...base, needs: { leisure: 0.1 }, summary: 'rested' });
|
|
319
|
+
return done({ ...base, needs: { leisure: 0.1 }, summary: say('rested', 'I rested.') });
|
|
303
320
|
case 'wait':
|
|
304
|
-
return done({ ...base, summary: 'waited' });
|
|
321
|
+
return done({ ...base, summary: say('waited', 'I waited.') });
|
|
305
322
|
case 'work-field': {
|
|
306
323
|
const ok = chance(state.rng, successChance(p, 'farming', 0.3, capacity));
|
|
307
324
|
if (ok) {
|
|
@@ -310,7 +327,7 @@ export function createVillage(people, opts) {
|
|
|
310
327
|
...base,
|
|
311
328
|
needs: { competence: 0.1 },
|
|
312
329
|
material: V.wage,
|
|
313
|
-
summary: 'worked the field; a good shift',
|
|
330
|
+
summary: say('worked the field; a good shift', 'I worked in the field. The shift went well.'),
|
|
314
331
|
});
|
|
315
332
|
}
|
|
316
333
|
state.food += 1;
|
|
@@ -318,7 +335,7 @@ export function createVillage(people, opts) {
|
|
|
318
335
|
...base,
|
|
319
336
|
status: 'failed',
|
|
320
337
|
needs: { competence: 0.02 },
|
|
321
|
-
summary: 'worked the field; a poor shift',
|
|
338
|
+
summary: say('worked the field; a poor shift', 'I worked in the field. The shift went poorly.'),
|
|
322
339
|
};
|
|
323
340
|
}
|
|
324
341
|
case 'forage': {
|
|
@@ -329,23 +346,35 @@ export function createVillage(people, opts) {
|
|
|
329
346
|
status: 'failed',
|
|
330
347
|
injury: { part: 'leg', severity: risk.severity, healRatePerDay: 0.1 },
|
|
331
348
|
summary: isNightAt(act.startedAt)
|
|
332
|
-
? 'went to the forest at night and got hurt by an animal'
|
|
333
|
-
: 'got hurt by an animal in the forest',
|
|
349
|
+
? say('went to the forest at night and got hurt by an animal', 'I went to the forest at night. An animal hurt me.')
|
|
350
|
+
: say('got hurt by an animal in the forest', 'An animal hurt me in the forest.'),
|
|
334
351
|
};
|
|
335
352
|
}
|
|
336
353
|
state.food += V.forageYield;
|
|
337
|
-
return done({
|
|
354
|
+
return done({
|
|
355
|
+
...base,
|
|
356
|
+
needs: { food: 0.3 },
|
|
357
|
+
summary: say('foraged in the forest', 'I gathered food in the forest.'),
|
|
358
|
+
});
|
|
338
359
|
}
|
|
339
360
|
case 'cook': {
|
|
340
361
|
const ok = chance(state.rng, successChance(p, 'cooking', 0.3, capacity));
|
|
341
362
|
if (ok) {
|
|
342
363
|
state.food += V.cookYield;
|
|
343
|
-
return done({
|
|
364
|
+
return done({
|
|
365
|
+
...base,
|
|
366
|
+
needs: { competence: 0.08 },
|
|
367
|
+
summary: say('cooked a good meal', 'I cooked a good meal.'),
|
|
368
|
+
});
|
|
344
369
|
}
|
|
345
|
-
return { ...base, status: 'failed', summary: 'burnt the meal' };
|
|
370
|
+
return { ...base, status: 'failed', summary: say('burnt the meal', 'I burnt the meal.') };
|
|
346
371
|
}
|
|
347
372
|
case 'pray':
|
|
348
|
-
return done({
|
|
373
|
+
return done({
|
|
374
|
+
...base,
|
|
375
|
+
needs: { meaning: 0.1 },
|
|
376
|
+
summary: say('prayed at the mosque', 'I prayed at the mosque.'),
|
|
377
|
+
});
|
|
349
378
|
case 'apologize': {
|
|
350
379
|
const other = act.targetId;
|
|
351
380
|
if (other !== undefined) {
|
|
@@ -357,10 +386,13 @@ export function createVillage(people, opts) {
|
|
|
357
386
|
targetId: other,
|
|
358
387
|
valence: 0.3,
|
|
359
388
|
salience: 0.6,
|
|
360
|
-
summary: `${p.name} apologised`,
|
|
389
|
+
summary: say(`${p.name} apologised`, `${p.name} apologised.`),
|
|
361
390
|
});
|
|
362
391
|
}
|
|
363
|
-
return done({
|
|
392
|
+
return done({
|
|
393
|
+
...base,
|
|
394
|
+
summary: say(`apologised to ${other ?? 'a neighbour'}`, `I apologised to ${other ?? 'a neighbour'}.`),
|
|
395
|
+
});
|
|
364
396
|
}
|
|
365
397
|
case 'chat': {
|
|
366
398
|
const other = aff.with?.[0];
|
|
@@ -373,13 +405,13 @@ export function createVillage(people, opts) {
|
|
|
373
405
|
targetId: other,
|
|
374
406
|
valence: 0.3,
|
|
375
407
|
salience: 0.4,
|
|
376
|
-
summary: `${p.name} stopped to chat`,
|
|
408
|
+
summary: say(`${p.name} stopped to chat`, `${p.name} stopped to chat.`),
|
|
377
409
|
});
|
|
378
410
|
}
|
|
379
411
|
return done({
|
|
380
412
|
...base,
|
|
381
413
|
needs: { belonging: 0.3, leisure: 0.1 },
|
|
382
|
-
summary: `chatted with ${other ?? 'a neighbour'}`,
|
|
414
|
+
summary: say(`chatted with ${other ?? 'a neighbour'}`, `I chatted with ${other ?? 'a neighbour'}.`),
|
|
383
415
|
});
|
|
384
416
|
}
|
|
385
417
|
case 'help': {
|
|
@@ -394,13 +426,13 @@ export function createVillage(people, opts) {
|
|
|
394
426
|
valence: 0.5,
|
|
395
427
|
salience: 0.6,
|
|
396
428
|
norms: [{ normId: 'help-neighbor', relation: 'fulfills' }],
|
|
397
|
-
summary: `${p.name} helped with the work`,
|
|
429
|
+
summary: say(`${p.name} helped with the work`, `${p.name} helped with the work.`),
|
|
398
430
|
});
|
|
399
431
|
}
|
|
400
432
|
return done({
|
|
401
433
|
...base,
|
|
402
434
|
needs: { meaning: 0.2, esteem: 0.05 },
|
|
403
|
-
summary: `helped ${other ?? 'a neighbour'}`,
|
|
435
|
+
summary: say(`helped ${other ?? 'a neighbour'}`, `I helped ${other ?? 'a neighbour'}.`),
|
|
404
436
|
percepts: other
|
|
405
437
|
? [
|
|
406
438
|
{
|
|
@@ -411,7 +443,7 @@ export function createVillage(people, opts) {
|
|
|
411
443
|
targetId: p.id,
|
|
412
444
|
valence: 0.3,
|
|
413
445
|
salience: 0.5,
|
|
414
|
-
summary: `${other} said thank you`,
|
|
446
|
+
summary: say(`${other} said thank you`, `${other} said thank you.`),
|
|
415
447
|
},
|
|
416
448
|
]
|
|
417
449
|
: [],
|
|
@@ -429,13 +461,18 @@ export function createVillage(people, opts) {
|
|
|
429
461
|
valence: -0.5,
|
|
430
462
|
salience: 0.8,
|
|
431
463
|
norms: [{ normId: 'theft', relation: 'violates' }],
|
|
432
|
-
summary: `${p.name} took bread from the house`,
|
|
464
|
+
summary: say(`${p.name} took bread from the house`, `${p.name} took bread from the house.`),
|
|
433
465
|
});
|
|
434
466
|
}
|
|
435
|
-
return done({
|
|
467
|
+
return done({
|
|
468
|
+
...base,
|
|
469
|
+
needs: { food: 0.5 },
|
|
470
|
+
material: 1,
|
|
471
|
+
summary: say('took bread that was not mine', 'I took bread that was not mine.'),
|
|
472
|
+
});
|
|
436
473
|
}
|
|
437
474
|
default:
|
|
438
|
-
return done({ ...base, summary: `${aff.label}: done` });
|
|
475
|
+
return done({ ...base, summary: say(`${aff.label}: done`, `I completed the task “${aff.label}”.`) });
|
|
439
476
|
}
|
|
440
477
|
};
|
|
441
478
|
const perceptsFor = (p, since, until) => {
|
package/dist/sim/longrun.js
CHANGED
|
@@ -77,11 +77,28 @@ export function liveCommunity(c, until, opts) {
|
|
|
77
77
|
days[p.id] = list;
|
|
78
78
|
}
|
|
79
79
|
for (const kind of r.onsets)
|
|
80
|
-
events.push({
|
|
80
|
+
events.push({
|
|
81
|
+
at: r.at,
|
|
82
|
+
personId: p.id,
|
|
83
|
+
kind: 'onset',
|
|
84
|
+
detail: p.lexicon?.plainEnglish ? `${p.name} developed ${kind}.` : `${p.name}: ${kind}`,
|
|
85
|
+
});
|
|
81
86
|
if (r.stage)
|
|
82
|
-
events.push({
|
|
87
|
+
events.push({
|
|
88
|
+
at: r.at,
|
|
89
|
+
personId: p.id,
|
|
90
|
+
kind: 'stage',
|
|
91
|
+
detail: p.lexicon?.plainEnglish
|
|
92
|
+
? `${p.name} entered the ${r.stage} stage of life.`
|
|
93
|
+
: `${p.name} is now ${r.stage}`,
|
|
94
|
+
});
|
|
83
95
|
if (r.died) {
|
|
84
|
-
events.push({
|
|
96
|
+
events.push({
|
|
97
|
+
at: r.at,
|
|
98
|
+
personId: p.id,
|
|
99
|
+
kind: 'died',
|
|
100
|
+
detail: p.lexicon?.plainEnglish ? `${p.name} died.` : `${p.name} died`,
|
|
101
|
+
});
|
|
85
102
|
died.push(p);
|
|
86
103
|
}
|
|
87
104
|
}
|
package/dist/sim/sim.d.ts
CHANGED
|
@@ -280,7 +280,7 @@ export declare function declineJoint(c: Community, proposal: JointProposal, prop
|
|
|
280
280
|
* activities are resolved as interrupted first, then the proposer begins the offer and each partner their
|
|
281
281
|
* mirror. Returns the activities begun (empty while other partners have yet to answer).
|
|
282
282
|
*/
|
|
283
|
-
export declare function acceptJoint(c: Community, world: World, proposal: JointProposal, partner: Person, partnerRecord: DecisionRecord,
|
|
283
|
+
export declare function acceptJoint(c: Community, world: World, proposal: JointProposal, partner: Person, partnerRecord: DecisionRecord, writeLog?: (e: SimEvent) => void): Activity[];
|
|
284
284
|
/**
|
|
285
285
|
* Chance that `lead` succeeds at a skilled task with `partners` helping: `successChance` with
|
|
286
286
|
* support = Σ partner skill × partner capacity / max(difficulty, 0.1), clamped to 0..1.
|
package/dist/sim/sim.js
CHANGED
|
@@ -79,6 +79,54 @@ function finishOpts(world) {
|
|
|
79
79
|
o.transfer = world.skillTransfer;
|
|
80
80
|
return o;
|
|
81
81
|
}
|
|
82
|
+
/** Complete sentences for generated event details. Host outcome summaries bypass this reader. */
|
|
83
|
+
function plainEventDetail(p, event) {
|
|
84
|
+
const name = p.name;
|
|
85
|
+
const detail = event.detail;
|
|
86
|
+
switch (event.kind) {
|
|
87
|
+
case 'decide':
|
|
88
|
+
return detail;
|
|
89
|
+
case 'idle':
|
|
90
|
+
return `${name} did not choose a task.`;
|
|
91
|
+
case 'died':
|
|
92
|
+
return `${name} died.`;
|
|
93
|
+
case 'begin':
|
|
94
|
+
case 'continue':
|
|
95
|
+
case 'finish':
|
|
96
|
+
return detail;
|
|
97
|
+
case 'perceive': {
|
|
98
|
+
const counts = /^(\d+)\/(\d+) percepts$/.exec(detail);
|
|
99
|
+
return counts ? `${name} noticed ${counts[1]} of ${counts[2]} events.` : `${name} noticed “${detail}”.`;
|
|
100
|
+
}
|
|
101
|
+
case 'interrupt':
|
|
102
|
+
return `${name} was interrupted by the event “${detail.replace(/^percept:/, '')}”.`;
|
|
103
|
+
case 'propose':
|
|
104
|
+
return `${name} proposed the shared task “${detail}”.`;
|
|
105
|
+
case 'accept':
|
|
106
|
+
return `${name} joined the shared task “${detail.replace(/^joins /, '')}”.`;
|
|
107
|
+
case 'decline':
|
|
108
|
+
return `${name} declined the shared task “${detail.replace(/^declined /, '')}”.`;
|
|
109
|
+
case 'converse':
|
|
110
|
+
return `${name} spoke. The exchange included: “${detail}”.`;
|
|
111
|
+
case 'contagion':
|
|
112
|
+
return `${name} ${detail}.`;
|
|
113
|
+
case 'command':
|
|
114
|
+
return `${name} received the order “${detail}”.`;
|
|
115
|
+
case 'release':
|
|
116
|
+
return `${name} was released from the order.`;
|
|
117
|
+
case 'onset':
|
|
118
|
+
return `${name} developed the condition “${detail}”.`;
|
|
119
|
+
case 'stage':
|
|
120
|
+
return `${name} entered the life stage “${detail}”.`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function taskEndLine(p, label, status) {
|
|
124
|
+
if (status === 'completed')
|
|
125
|
+
return `${p.name} completed the task “${label}”.`;
|
|
126
|
+
if (status === 'failed')
|
|
127
|
+
return `${p.name} failed to complete the task “${label}”.`;
|
|
128
|
+
return `${p.name} was interrupted during the task “${label}”.`;
|
|
129
|
+
}
|
|
82
130
|
/**
|
|
83
131
|
* Whether standing suggestion `s` is heard by `p` at a decision over `affordances` (standing-advice SCOPE): its target
|
|
84
132
|
* must be on offer or be the running activity, and if the action keeps commitments and was completed since the suggestion was given, doing it
|
|
@@ -208,7 +256,7 @@ export function tellDeath(c, dead, at, opts = {}) {
|
|
|
208
256
|
targetId: dead.id,
|
|
209
257
|
valence: -0.8,
|
|
210
258
|
salience: 0.9,
|
|
211
|
-
summary: `${dead.name} died`,
|
|
259
|
+
summary: q.lexicon?.plainEnglish ? `${dead.name} died.` : `${dead.name} died`,
|
|
212
260
|
},
|
|
213
261
|
]);
|
|
214
262
|
// A spouse is widowed even if the news went unattended.
|
|
@@ -355,7 +403,11 @@ export function declineJoint(c, proposal, proposer) {
|
|
|
355
403
|
* activities are resolved as interrupted first, then the proposer begins the offer and each partner their
|
|
356
404
|
* mirror. Returns the activities begun (empty while other partners have yet to answer).
|
|
357
405
|
*/
|
|
358
|
-
export function acceptJoint(c, world, proposal, partner, partnerRecord,
|
|
406
|
+
export function acceptJoint(c, world, proposal, partner, partnerRecord, writeLog = () => { }) {
|
|
407
|
+
const log = (e) => {
|
|
408
|
+
const person = personById(c, e.personId);
|
|
409
|
+
writeLog(person?.lexicon?.plainEnglish ? { ...e, detail: plainEventDetail(person, e) } : e);
|
|
410
|
+
};
|
|
359
411
|
if (proposal.status !== 'pending' || !proposal.partnerIds.includes(partner.id))
|
|
360
412
|
return [];
|
|
361
413
|
if (!proposal.accepted.includes(partner.id))
|
|
@@ -387,7 +439,9 @@ export function acceptJoint(c, world, proposal, partner, partnerRecord, log = ()
|
|
|
387
439
|
at,
|
|
388
440
|
personId: person.id,
|
|
389
441
|
kind: 'finish',
|
|
390
|
-
detail:
|
|
442
|
+
detail: person.lexicon?.plainEnglish
|
|
443
|
+
? taskEndLine(person, act.affordance.label, outcome.status)
|
|
444
|
+
: `${act.action} ${outcome.status}`,
|
|
391
445
|
decisionId: act.decisionId,
|
|
392
446
|
affordanceId: act.affordanceId,
|
|
393
447
|
action: act.action,
|
|
@@ -404,7 +458,9 @@ export function acceptJoint(c, world, proposal, partner, partnerRecord, log = ()
|
|
|
404
458
|
at,
|
|
405
459
|
personId: person.id,
|
|
406
460
|
kind: 'begin',
|
|
407
|
-
detail:
|
|
461
|
+
detail: person.lexicon?.plainEnglish
|
|
462
|
+
? `${person.name} began the shared task “${offer.label}”. ${record.intention}`
|
|
463
|
+
: `${offer.label} (joint ${proposal.id})`,
|
|
408
464
|
decisionId: record.id,
|
|
409
465
|
affordanceId: offer.id,
|
|
410
466
|
action: offer.action,
|
|
@@ -703,9 +759,12 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
703
759
|
// Communities created before joint support (plain JSON from an older host) get the new bookkeeping.
|
|
704
760
|
c.interrupts ??= {};
|
|
705
761
|
c.joint ??= { nextId: 0, proposals: [], excluded: {} };
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
762
|
+
// Presentation only: retain host-authored summaries and every event field other than generated detail.
|
|
763
|
+
const log = (e, authored = false) => {
|
|
764
|
+
const person = personById(c, e.personId);
|
|
765
|
+
if (events.length < maxEvents) {
|
|
766
|
+
events.push(person?.lexicon?.plainEnglish && !authored ? { ...e, detail: plainEventDetail(person, e) } : e);
|
|
767
|
+
}
|
|
709
768
|
};
|
|
710
769
|
const contagion = opts.contagion ?? true;
|
|
711
770
|
c.queued ??= {};
|
|
@@ -772,14 +831,17 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
772
831
|
at: t,
|
|
773
832
|
personId: p.id,
|
|
774
833
|
kind: 'finish',
|
|
775
|
-
detail: outcome.summary ??
|
|
834
|
+
detail: outcome.summary ??
|
|
835
|
+
(p.lexicon?.plainEnglish
|
|
836
|
+
? taskEndLine(p, act.affordance.label, outcome.status)
|
|
837
|
+
: `${act.action} ${outcome.status}`),
|
|
776
838
|
decisionId: act.decisionId,
|
|
777
839
|
affordanceId: act.affordanceId,
|
|
778
840
|
action: act.action,
|
|
779
841
|
};
|
|
780
842
|
if (report)
|
|
781
843
|
ev.status = report.status;
|
|
782
|
-
log(ev);
|
|
844
|
+
log(ev, outcome.summary !== undefined);
|
|
783
845
|
if (outcome.status === 'completed') {
|
|
784
846
|
noteStandingDone(c, p, act, t);
|
|
785
847
|
if (pairMinute !== t) {
|
|
@@ -837,14 +899,22 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
837
899
|
detail: `joins ${acceptedProposal.offer.label}`,
|
|
838
900
|
jointId: acceptedProposal.id,
|
|
839
901
|
});
|
|
840
|
-
acceptJoint(c, world, acceptedProposal, p, record, log);
|
|
902
|
+
acceptJoint(c, world, acceptedProposal, p, record, (event) => log(event, true));
|
|
841
903
|
continue;
|
|
842
904
|
}
|
|
843
905
|
const chosen = affordances.find((a) => a.id === record.chosenAffordanceId);
|
|
844
906
|
const continuing = act !== null && (record.chosenAffordanceId === null || record.chosenAffordanceId === act.affordanceId);
|
|
845
907
|
if (act && continuing) {
|
|
846
908
|
reviewed(p);
|
|
847
|
-
log({
|
|
909
|
+
log({
|
|
910
|
+
at: t,
|
|
911
|
+
personId: p.id,
|
|
912
|
+
kind: 'continue',
|
|
913
|
+
detail: p.lexicon?.plainEnglish
|
|
914
|
+
? `${p.name} continued the task “${act.affordance.label}”.`
|
|
915
|
+
: act.action,
|
|
916
|
+
affordanceId: act.affordanceId,
|
|
917
|
+
});
|
|
848
918
|
continue;
|
|
849
919
|
}
|
|
850
920
|
if (chosen && isJointOffer(c, p, chosen)) {
|
|
@@ -854,11 +924,15 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
854
924
|
at: t,
|
|
855
925
|
personId: p.id,
|
|
856
926
|
kind: proposal.status === 'declined' ? 'decline' : 'propose',
|
|
857
|
-
detail:
|
|
927
|
+
detail: p.lexicon?.plainEnglish
|
|
928
|
+
? proposal.status === 'declined'
|
|
929
|
+
? `The shared task “${chosen.label}” could not start because a partner was unavailable.`
|
|
930
|
+
: `${chosen.label} with ${proposal.partnerIds.map((id) => personById(c, id)?.name ?? id).join(', ')}`
|
|
931
|
+
: `${chosen.label} with ${proposal.partnerIds.join(', ')}`,
|
|
858
932
|
affordanceId: chosen.id,
|
|
859
933
|
action: chosen.action,
|
|
860
934
|
jointId: proposal.id,
|
|
861
|
-
});
|
|
935
|
+
}, Boolean(p.lexicon?.plainEnglish && proposal.status === 'declined'));
|
|
862
936
|
continue;
|
|
863
937
|
}
|
|
864
938
|
if (act) {
|
|
@@ -868,7 +942,9 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
868
942
|
at: t,
|
|
869
943
|
personId: p.id,
|
|
870
944
|
kind: 'finish',
|
|
871
|
-
detail:
|
|
945
|
+
detail: p.lexicon?.plainEnglish
|
|
946
|
+
? taskEndLine(p, act.affordance.label, 'interrupted')
|
|
947
|
+
: `${act.action} interrupted`,
|
|
872
948
|
decisionId: act.decisionId,
|
|
873
949
|
affordanceId: act.affordanceId,
|
|
874
950
|
action: act.action,
|
|
@@ -889,7 +965,9 @@ export function stepCommunity(c, world, until, opts = {}) {
|
|
|
889
965
|
at: t,
|
|
890
966
|
personId: p.id,
|
|
891
967
|
kind: 'begin',
|
|
892
|
-
detail:
|
|
968
|
+
detail: p.lexicon?.plainEnglish
|
|
969
|
+
? `${p.name} began the task “${chosen.label}”. ${record.intention}`
|
|
970
|
+
: `${chosen.label} (${record.intention})`,
|
|
893
971
|
decisionId: record.id,
|
|
894
972
|
affordanceId: chosen.id,
|
|
895
973
|
action: chosen.action,
|
package/dist/social/social.js
CHANGED
|
@@ -471,6 +471,16 @@ export const REPUTATION_DEFAULTS = {
|
|
|
471
471
|
/** First-hand familiarity damps hearsay: scale = 1 - familiarityDamping × familiarity. */
|
|
472
472
|
familiarityDamping: 0.5,
|
|
473
473
|
};
|
|
474
|
+
/** Only authored, finite effects count; prototype properties are not character traits. */
|
|
475
|
+
function reputationEffect(trait, table) {
|
|
476
|
+
const effect = Object.hasOwn(table, trait) ? table[trait] : undefined;
|
|
477
|
+
return effect &&
|
|
478
|
+
Number.isFinite(effect.affection) &&
|
|
479
|
+
Number.isFinite(effect.trust) &&
|
|
480
|
+
Number.isFinite(effect.respect)
|
|
481
|
+
? effect
|
|
482
|
+
: undefined;
|
|
483
|
+
}
|
|
474
484
|
/** Splits a character proposition '<personId>:<trait>[:...]' into its subject and trait, if it is one. */
|
|
475
485
|
export function parseTraitProp(prop, table = REPUTATION_TRAITS) {
|
|
476
486
|
const parts = prop.split(':');
|
|
@@ -478,7 +488,7 @@ export function parseTraitProp(prop, table = REPUTATION_TRAITS) {
|
|
|
478
488
|
return undefined;
|
|
479
489
|
const personId = parts[0];
|
|
480
490
|
const trait = parts[1];
|
|
481
|
-
if (personId === '' ||
|
|
491
|
+
if (personId === '' || reputationEffect(trait, table) === undefined)
|
|
482
492
|
return undefined;
|
|
483
493
|
return { personId, trait };
|
|
484
494
|
}
|
|
@@ -509,7 +519,7 @@ export function applyReputationBelief(p, prop, before, after, now, table = REPUT
|
|
|
509
519
|
}
|
|
510
520
|
/** Whether a character claim about a person is unfavourable to them (for backbiting tags). */
|
|
511
521
|
export function isUnfavourableTrait(trait, value, table = REPUTATION_TRAITS) {
|
|
512
|
-
const e = table
|
|
522
|
+
const e = reputationEffect(trait, table);
|
|
513
523
|
if (!e)
|
|
514
524
|
return false;
|
|
515
525
|
const sum = e.affection + e.trust + e.respect;
|
package/dist/types.d.ts
CHANGED
|
@@ -1509,6 +1509,10 @@ export interface PersonSpec {
|
|
|
1509
1509
|
values?: Partial<Values>;
|
|
1510
1510
|
norms?: HeldNorm[];
|
|
1511
1511
|
skills?: Record<string, Unit>;
|
|
1512
|
+
/** Authored habits, copied in order. Invalid or over-bound setup throws RangeError. */
|
|
1513
|
+
habits?: Habit[];
|
|
1514
|
+
/** Authored expectations, copied in order. Invalid or over-bound setup throws RangeError. */
|
|
1515
|
+
expectations?: ActionExpectation[];
|
|
1512
1516
|
body?: Partial<Omit<BodyState, 'injuries' | 'illnesses' | 'nextId'>>;
|
|
1513
1517
|
needs?: Partial<NeedReservoirs>;
|
|
1514
1518
|
relationships?: (Partial<Relationship> & {
|
|
@@ -1767,6 +1771,8 @@ export interface VoiceConflict {
|
|
|
1767
1771
|
* slots documented next to each key.
|
|
1768
1772
|
*/
|
|
1769
1773
|
export interface Lexicon {
|
|
1774
|
+
/** Use complete, plain English in built-in narration. Omitted keeps exact legacy wording. */
|
|
1775
|
+
plainEnglish?: boolean;
|
|
1770
1776
|
/** Include person identity and stable temperament in assent/deferral selection (no RNG draws). */
|
|
1771
1777
|
variety?: boolean;
|
|
1772
1778
|
/** What material gains mean here, e.g. 'food', 'household keep' or 'resources'; absent keeps legacy money lines. */
|
package/dist/will/will.d.ts
CHANGED
|
@@ -233,6 +233,18 @@ export declare function noteVoicePromise(p: Person, voiceId: EntityId, event: {
|
|
|
233
233
|
at?: Minute;
|
|
234
234
|
action?: string;
|
|
235
235
|
}): VoiceRelation;
|
|
236
|
+
/**
|
|
237
|
+
* SCOPE (host trust gesture): add a host-supplied signed amount to an existing advising voice's trust,
|
|
238
|
+
* clamped to 0..1, and record the realized change in the usual bounded history. Magnitudes and reason
|
|
239
|
+
* are host premises, not calibrated claims. Missing voices return undefined without creating a voice.
|
|
240
|
+
* Non-finite amounts are ignored. No pressure, credit, counters, memory, time or RNG changes occur.
|
|
241
|
+
* Empty/non-string reasons and non-string actions reject before mutation with RangeError.
|
|
242
|
+
*/
|
|
243
|
+
export declare function gestureVoiceTrust(p: Person, voiceId: EntityId, amount: number, event: {
|
|
244
|
+
reason: string;
|
|
245
|
+
at?: Minute;
|
|
246
|
+
action?: string;
|
|
247
|
+
}): VoiceRelation | undefined;
|
|
236
248
|
/**
|
|
237
249
|
* The next minute after `now` at which a pending commitment reaches `wakeCommitmentPressure`, the level at which a
|
|
238
250
|
* sleeper may wake to serve it (the asleep veto lifts). The composite caps a sleeper's review there so a duty whose
|
package/dist/will/will.js
CHANGED
|
@@ -37,6 +37,7 @@ import { commitmentPressure, isUnderWay, pressureReachedAt } from '../agenda/ind
|
|
|
37
37
|
import { downedAllows, readCapacities } from '../body/index.js';
|
|
38
38
|
import { CONSCIENCE_DEFAULTS, normVeto } from '../conscience/index.js';
|
|
39
39
|
import { clamp01, cmpStr, decay, dexp, dpow, hasNumbers, isObj, random, round } from '../core/index.js';
|
|
40
|
+
import { commitmentLabel } from '../presentation.js';
|
|
40
41
|
import { skillLevel } from '../skills/index.js';
|
|
41
42
|
import { MINUTES_PER_DAY, MINUTES_PER_HOUR } from '../types.js';
|
|
42
43
|
export const WILL_DEFAULTS = {
|
|
@@ -292,6 +293,27 @@ export function noteVoicePromise(p, voiceId, event) {
|
|
|
292
293
|
noteTrust(v, v.trust - before, event.kept ? 'promise-kept' : 'promise-broken', at, event.action);
|
|
293
294
|
return v;
|
|
294
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* SCOPE (host trust gesture): add a host-supplied signed amount to an existing advising voice's trust,
|
|
298
|
+
* clamped to 0..1, and record the realized change in the usual bounded history. Magnitudes and reason
|
|
299
|
+
* are host premises, not calibrated claims. Missing voices return undefined without creating a voice.
|
|
300
|
+
* Non-finite amounts are ignored. No pressure, credit, counters, memory, time or RNG changes occur.
|
|
301
|
+
* Empty/non-string reasons and non-string actions reject before mutation with RangeError.
|
|
302
|
+
*/
|
|
303
|
+
export function gestureVoiceTrust(p, voiceId, amount, event) {
|
|
304
|
+
if (!event || typeof event.reason !== 'string' || event.reason.length === 0)
|
|
305
|
+
throw new RangeError('A trust gesture needs a reason');
|
|
306
|
+
if (event.action !== undefined && typeof event.action !== 'string')
|
|
307
|
+
throw new RangeError('Invalid trust gesture action');
|
|
308
|
+
const v = voiceOf(p, voiceId);
|
|
309
|
+
if (!v || !Number.isFinite(amount))
|
|
310
|
+
return v;
|
|
311
|
+
const before = v.trust;
|
|
312
|
+
v.trust = clamp01(before + amount);
|
|
313
|
+
const at = event.at !== undefined && Number.isFinite(event.at) ? event.at : p.now;
|
|
314
|
+
noteTrust(v, v.trust - before, event.reason, at, event.action);
|
|
315
|
+
return v;
|
|
316
|
+
}
|
|
295
317
|
const finite = (x) => x === undefined || Number.isFinite(x);
|
|
296
318
|
/** Host numbers the framework computes with must be finite; a NaN offer is vetoed, never chosen. */
|
|
297
319
|
function wellFormed(aff) {
|
|
@@ -829,7 +851,10 @@ function judgeVoices(p, ctx, suggestions, prepared, selection) {
|
|
|
829
851
|
kind: 'notNow',
|
|
830
852
|
commitmentId: v.omission,
|
|
831
853
|
...(winnerAff ? { insteadAffordanceId: winnerAff.id } : {}),
|
|
832
|
-
counterOffer: {
|
|
854
|
+
counterOffer: {
|
|
855
|
+
affordanceId: suggested[0]?.affordanceId,
|
|
856
|
+
label: dutyLabel(duty, winnerAff, p),
|
|
857
|
+
},
|
|
833
858
|
}));
|
|
834
859
|
}
|
|
835
860
|
return out(resolution('refused', v.reason, { kind: 'willNot', commitmentId: v.omission }));
|
|
@@ -886,7 +911,12 @@ function judgeVoices(p, ctx, suggestions, prepared, selection) {
|
|
|
886
911
|
kind: 'notNow',
|
|
887
912
|
insteadAffordanceId: winnerAff?.id,
|
|
888
913
|
counterOffer: winnerAff
|
|
889
|
-
? {
|
|
914
|
+
? {
|
|
915
|
+
affordanceId: bestSuggested.affordanceId,
|
|
916
|
+
label: p.lexicon?.plainEnglish
|
|
917
|
+
? `after I finish this task (${winnerAff.label})`
|
|
918
|
+
: `after I ${winnerAff.label}`,
|
|
919
|
+
}
|
|
890
920
|
: { affordanceId: bestSuggested.affordanceId, label: 'later' },
|
|
891
921
|
}));
|
|
892
922
|
});
|
|
@@ -940,7 +970,11 @@ function arbitrateInsistence(p, judged, considered, affById, ev) {
|
|
|
940
970
|
insteadAffordanceId: forced,
|
|
941
971
|
counterOffer: {
|
|
942
972
|
...(firstTarget !== undefined ? { affordanceId: firstTarget } : {}),
|
|
943
|
-
label: forcedAff
|
|
973
|
+
label: forcedAff
|
|
974
|
+
? p.lexicon?.plainEnglish
|
|
975
|
+
? `after I finish this task (${forcedAff.label})`
|
|
976
|
+
: `after I ${forcedAff.label}`
|
|
977
|
+
: 'later',
|
|
944
978
|
},
|
|
945
979
|
};
|
|
946
980
|
if (j.res.likelihood !== undefined)
|
|
@@ -1134,7 +1168,12 @@ export function conflictBetweenVoices(considered, resolutions, chosenAffordanceI
|
|
|
1134
1168
|
};
|
|
1135
1169
|
}
|
|
1136
1170
|
/** Counter-offer for a request deferred behind a duty: "after I pray", "after I feed the children". */
|
|
1137
|
-
function dutyLabel(duty, winner) {
|
|
1171
|
+
function dutyLabel(duty, winner, p) {
|
|
1172
|
+
if (p.lexicon?.plainEnglish) {
|
|
1173
|
+
if (duty)
|
|
1174
|
+
return `after I finish this duty (${commitmentLabel(duty, p.lexicon)})`;
|
|
1175
|
+
return winner ? `after I finish this task (${winner.label})` : 'after my duty';
|
|
1176
|
+
}
|
|
1138
1177
|
if (duty?.kind === 'worship')
|
|
1139
1178
|
return duty.label ? `after I pray ${duty.label}` : 'after I pray';
|
|
1140
1179
|
if (duty?.label)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adam0white/human-framework",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.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",
|