@mindot/will 0.7.0 → 0.9.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 +87 -22
- package/dist/channels/discord.d.ts +67 -6
- package/dist/channels/discord.js +112 -6
- package/dist/channels/discord.js.map +1 -1
- package/dist/channels/whatsapp.d.ts +1 -1
- package/dist/channels/whatsapp.js +4 -1
- package/dist/channels/whatsapp.js.map +1 -1
- package/dist/cli.js +13733 -11057
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3729 -1199
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/{will-DAW0l-lY.d.ts → will-DbDj_TEH.d.ts} +1214 -93
- package/package.json +1 -1
- package/src/channels/discord.ts +189 -11
- package/src/channels/types.ts +90 -0
- package/src/channels/whatsapp.ts +13 -4
- package/src/cli.ts +9 -4
- package/src/cognition/agency/consequence.ts +122 -1
- package/src/cognition/agency/conversation.aim.ts +260 -0
- package/src/cognition/agency/engines/action.selector.ts +85 -3
- package/src/cognition/agency/engines/affordance.synthesizer.ts +90 -1
- package/src/cognition/agency/engines/motor.schema.executor.ts +152 -10
- package/src/cognition/agency/engines/reafference.engine.ts +129 -2
- package/src/cognition/agency/proactive.communicator.ts +19 -3
- package/src/cognition/agency/reconcile.learning.ts +16 -2
- package/src/cognition/agency/restart.ts +66 -0
- package/src/cognition/agency/schemas/repertoire.ts +12 -5
- package/src/cognition/agency/selection.scoring.ts +33 -0
- package/src/cognition/agency/types.ts +35 -0
- package/src/cognition/cache/composition.ts +232 -0
- package/src/cognition/cache/deliberation.cache.ts +219 -0
- package/src/cognition/cache/fingerprint.ts +120 -0
- package/src/cognition/cache/types.ts +105 -0
- package/src/cognition/config.mirror.entities.ts +109 -1
- package/src/cognition/event.schemas.ts +22 -0
- package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
- package/src/cognition/faculties/episodic.consolidator.ts +59 -3
- package/src/cognition/faculties/executive.engine/commands.ts +189 -14
- package/src/cognition/faculties/executive.engine/context.ts +67 -13
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
- package/src/cognition/faculties/executive.engine/engine.ts +686 -187
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +162 -44
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +310 -65
- package/src/cognition/faculties/executive.engine/facet.ts +89 -26
- package/src/cognition/faculties/executive.engine/gating.ts +14 -14
- package/src/cognition/faculties/executive.engine/parser.ts +21 -1
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +169 -20
- package/src/cognition/faculties/executive.engine/types.ts +69 -0
- package/src/cognition/faculties/goal.manager.ts +94 -14
- package/src/cognition/faculties/known.entity.tracker.ts +267 -28
- package/src/cognition/faculties/moral.evaluator.ts +8 -3
- package/src/cognition/faculties/persona.consolidator.ts +141 -0
- package/src/cognition/faculties/reputation.tracker.ts +66 -2
- package/src/cognition/faculties/self.model.updater.ts +19 -12
- package/src/cognition/faculties/social.perception.ts +47 -3
- package/src/cognition/faculties/threat.evaluator.ts +7 -0
- package/src/cognition/faculties/working.memory.ts +10 -20
- package/src/cognition/identity.entity.ts +205 -0
- package/src/cognition/index.ts +11 -0
- package/src/cognition/memory/vector.adapter.ts +12 -3
- package/src/cognition/memory/vector.embedder.ts +54 -7
- package/src/cognition/persona.prior.ts +6 -0
- package/src/cognition/senses/audition.engine/engine.ts +404 -46
- package/src/cognition/senses/base.sense.engine.ts +1 -1
- package/src/cognition/senses/index.ts +12 -0
- package/src/cognition/social.identity.ts +273 -0
- package/src/cognition/utilities/token.tracker.ts +246 -98
- package/src/core/orchestrator.ts +38 -0
- package/src/host/boot.ts +78 -22
- package/src/index.ts +35 -0
- package/src/llm/index.ts +415 -97
- package/src/llm/routing.ts +204 -0
- package/src/llm/summarizer.ts +5 -1
- package/src/llm/wire.contracts.ts +19 -0
- package/src/pma/index.ts +67 -53
- package/src/runners/thin-shim.runner.ts +18 -6
- package/src/sdk/will.ts +121 -22
- package/src/stem/assembly.audit.ts +1 -0
- package/src/stem/guards/identity.coherence.ts +17 -6
- package/src/stem/index.ts +82 -5
- package/src/stem/mind.ts +327 -79
- package/src/stem/policy/arbiter.ts +49 -14
- package/src/stem/policy/rule.table.ts +2 -2
- package/src/stem/tracts/effector.controller.ts +56 -9
- package/src/stem/tracts/outbox.writer.ts +40 -2
- package/src/cognition/faculties/executive.engine/messages.ts +0 -102
|
@@ -37,6 +37,9 @@ import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
|
37
37
|
import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
38
38
|
import type { Percept } from '#senses/index'
|
|
39
39
|
import { GenerativeModel } from '#cognition/generative.model'
|
|
40
|
+
import {
|
|
41
|
+
isReferentId, mintReferentId, canonicalOf, withHandle, type Handle,
|
|
42
|
+
} from '#cognition/social.identity'
|
|
40
43
|
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
41
44
|
|
|
42
45
|
export interface KnownEntityTrackerConfig {
|
|
@@ -71,6 +74,31 @@ export interface KnownEntity {
|
|
|
71
74
|
lastSeenTick: Tick
|
|
72
75
|
/** 0–1: how identified/coherent this referent is (a name + repeated encounters raise it). */
|
|
73
76
|
resolutionConfidence: number
|
|
77
|
+
/**
|
|
78
|
+
* The ways this referent has been reachable, with the circumstances.
|
|
79
|
+
*
|
|
80
|
+
* Distinct from an ALIAS, and the distinction is load-bearing: an alias is
|
|
81
|
+
* another NAME for the same referent (a transport user id), a handle is a
|
|
82
|
+
* PLACE it can be reached (a thread, a room). One person has one identity and
|
|
83
|
+
* several rooms, and conflating them is what made "which room should I say
|
|
84
|
+
* this in?" unaskable.
|
|
85
|
+
*/
|
|
86
|
+
handles: Handle[]
|
|
87
|
+
/**
|
|
88
|
+
* Referents this one MIGHT be the same someone as, unresolved.
|
|
89
|
+
*
|
|
90
|
+
* A blocked merge used to vanish. `_recognise` will only absorb a THIN handle
|
|
91
|
+
* into an established relationship — rightly, because fusing two real people
|
|
92
|
+
* who share a name is the dangerous direction — so once the same human was
|
|
93
|
+
* well-established on two channels they stayed two people permanently, and
|
|
94
|
+
* nothing anywhere recorded the near-miss.
|
|
95
|
+
*
|
|
96
|
+
* A person does not silently fail here. They NOTICE — "hang on, is this the
|
|
97
|
+
* same Mara?" — and then resolve it by asking. So the doubt is kept, shown, and
|
|
98
|
+
* left for the mind to settle. Deliberately not a merge: this is a question,
|
|
99
|
+
* and the answer is the mind's to give.
|
|
100
|
+
*/
|
|
101
|
+
suspectedSameAs?: string[]
|
|
74
102
|
}
|
|
75
103
|
|
|
76
104
|
/** Percept domains whose entities are minds. Everything else defaults to a thing. */
|
|
@@ -85,6 +113,19 @@ const CURIOUS_RESOLVED = 0.6
|
|
|
85
113
|
// Forgetting (Phase 4). (Reliability rate is now a Channel-A developable field — analytical.)
|
|
86
114
|
const FORGET_FLOOR = 0.02 // below this familiarity, an unidentified blip is forgotten
|
|
87
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Familiarity decay for a referent the mind has NOT resolved — no name, barely
|
|
118
|
+
* identified. Two orders of magnitude faster than for a known someone, because
|
|
119
|
+
* one rate cannot serve both: a face glimpsed once in a crowd should fade in
|
|
120
|
+
* minutes, and a colleague should not.
|
|
121
|
+
*
|
|
122
|
+
* The forgetting gate already drew exactly this line (`!d.name &&
|
|
123
|
+
* resolutionConfidence < CURIOUS_RESOLUTION`); only the rate did not, so slowing
|
|
124
|
+
* decay enough for relationships to exist would have left every blip resident
|
|
125
|
+
* forever. Same value the single shared rate used to have.
|
|
126
|
+
*/
|
|
127
|
+
const BLIP_DECAY_RATE = 0.005
|
|
128
|
+
|
|
88
129
|
// Recognition (Phase 5) — guards against conflating two *different* people who share a
|
|
89
130
|
// name. Only recognise a still-thin handle (≤) into a known person, and never when both
|
|
90
131
|
// were active at the same time (two people talking at once ⇒ distinct, not one on two
|
|
@@ -109,18 +150,50 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
109
150
|
private _restored = false
|
|
110
151
|
|
|
111
152
|
// Buffered from senses.*.percept events; drained each tick in react().
|
|
112
|
-
private _pendingEncounters: Array<{
|
|
153
|
+
private _pendingEncounters: Array<{
|
|
154
|
+
keid: string; domain: string; name?: string
|
|
155
|
+
/** The room this encounter happened in, and whether it was a private one. */
|
|
156
|
+
thread?: string; direct?: boolean
|
|
157
|
+
}> = []
|
|
113
158
|
// Buffered from known.entity.learned (the conscious / reasoning write-path, Phase 2.2).
|
|
114
|
-
private _pendingConscious: Array<{
|
|
159
|
+
private _pendingConscious: Array<{
|
|
160
|
+
keid: string; name?: string; feeling?: number
|
|
161
|
+
/** "This is the same someone as that." The mind settling an identity itself. */
|
|
162
|
+
sameAs?: string
|
|
163
|
+
}> = []
|
|
115
164
|
// Buffered from action.outcome — the reliability track-record signal (Phase 4).
|
|
116
165
|
private _pendingOutcomes: Array<{ keid: string; signal: number }> = []
|
|
117
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Aliases minted this tick, awaiting persistence. `_getOrCreate` runs deep inside
|
|
169
|
+
* the drain loops with no access to the command list, and an alias that lives
|
|
170
|
+
* only in memory is one the mind forgets on restart — every transport address
|
|
171
|
+
* would mint a SECOND anchor next boot and the person would fork in two.
|
|
172
|
+
*/
|
|
173
|
+
private _mintedAliases: Array<{ alias: string; canonical: string }> = []
|
|
174
|
+
|
|
118
175
|
private _bus: CognitiveBus | null = null
|
|
119
176
|
private readonly _model = new GenerativeModel()
|
|
120
177
|
|
|
121
178
|
constructor( config: KnownEntityTrackerConfig = {} ){
|
|
122
179
|
this._growthRate = config.familiarityGrowthRate ?? 0.15
|
|
123
|
-
|
|
180
|
+
// 0.005/tick spends the entire scale in 200 ticks — under four minutes at the
|
|
181
|
+
// 1s tick a hosted Will actually runs on. Familiarity could therefore never
|
|
182
|
+
// accumulate for a mind whose encounters are minutes apart, and it did not:
|
|
183
|
+
// measured 0.00 on a fresh mind after 5 encounters, and 0.00 on a mind that
|
|
184
|
+
// had banked 71. A permanently-zero signal is worse than a missing one — it
|
|
185
|
+
// feeds the curiosity-to-resolve drive (which therefore never fired) and the
|
|
186
|
+
// pruning order, both of which read it as "a total stranger".
|
|
187
|
+
//
|
|
188
|
+
// Mere exposure fades over days, not minutes. At 0.00002 a full scale takes
|
|
189
|
+
// ~50k ticks (~14h at 1s), so someone met this morning is still familiar
|
|
190
|
+
// tonight and a stranger from last month has genuinely faded.
|
|
191
|
+
//
|
|
192
|
+
// The deeper flaw stays: this is per-TICK, so a psychological timescale is
|
|
193
|
+
// pinned to a host's tick rate. `react()` already receives the real delta —
|
|
194
|
+
// expressing decay in experienced time is the actual fix, and it belongs with
|
|
195
|
+
// the other per-tick decays rather than in this one engine alone.
|
|
196
|
+
this._decayRate = config.familiarityDecayRate ?? 0.00002
|
|
124
197
|
this._curiosityGain = config.curiosityGain ?? 1.0
|
|
125
198
|
this._reliabilityRate = config.reliabilityRate ?? 0.2
|
|
126
199
|
this._maxTracked = config.maxTracked ?? 50
|
|
@@ -150,9 +223,9 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
150
223
|
|
|
151
224
|
// Conscious learning (the reasoning write-path): a learned name + felt valence.
|
|
152
225
|
if( e.type === 'known.entity.learned'){
|
|
153
|
-
const u = e.payload as { keid?: string; name?: string; feeling?: number }
|
|
226
|
+
const u = e.payload as { keid?: string; name?: string; feeling?: number; sameAs?: string }
|
|
154
227
|
if( u?.keid && u.keid !== 'agent-self')
|
|
155
|
-
this._pendingConscious.push({ keid: u.keid, name: u.name, feeling: u.feeling })
|
|
228
|
+
this._pendingConscious.push({ keid: u.keid, name: u.name, feeling: u.feeling, sameAs: u.sameAs })
|
|
156
229
|
return
|
|
157
230
|
}
|
|
158
231
|
|
|
@@ -170,10 +243,16 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
170
243
|
if( !keid || keid === 'agent-self') return
|
|
171
244
|
|
|
172
245
|
// A channel-supplied display name rides on the raw input (e.g. TextMessage.speakerName).
|
|
173
|
-
|
|
246
|
+
// A channel supplies the display name AND the room, both on the raw input.
|
|
247
|
+
// `direct` is the one bit the Discord edge computed (`isDM`) and threw away
|
|
248
|
+
// before the mind could see it — the single fact that decides whether a room
|
|
249
|
+
// is the right place for a given utterance.
|
|
250
|
+
const raw = p?.raw as { speakerName?: unknown; threadId?: unknown; direct?: unknown } | undefined
|
|
174
251
|
const name = typeof raw?.speakerName === 'string' ? raw.speakerName : undefined
|
|
252
|
+
const thread = typeof raw?.threadId === 'string' ? raw.threadId : undefined
|
|
253
|
+
const direct = typeof raw?.direct === 'boolean' ? raw.direct : undefined
|
|
175
254
|
|
|
176
|
-
this._pendingEncounters.push({ keid, domain: p!.domain, name })
|
|
255
|
+
this._pendingEncounters.push({ keid, domain: p!.domain, name, thread, direct })
|
|
177
256
|
}
|
|
178
257
|
|
|
179
258
|
snapshot(): Record<string, unknown> {
|
|
@@ -200,8 +279,10 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
200
279
|
|
|
201
280
|
// Familiarity fades a little each tick — absence dims the sense of an entity.
|
|
202
281
|
for( const d of this._dossiers.values() )
|
|
203
|
-
if( d.familiarity > 0 )
|
|
204
|
-
|
|
282
|
+
if( d.familiarity > 0 ){
|
|
283
|
+
const unresolved = !d.name && d.resolutionConfidence < CURIOUS_RESOLUTION
|
|
284
|
+
d.familiarity = Math.max( 0, d.familiarity - ( unresolved ? BLIP_DECAY_RATE : this._decayRate ) )
|
|
285
|
+
}
|
|
205
286
|
|
|
206
287
|
// Drain encounters — each percept is an exposure that re-warms familiarity.
|
|
207
288
|
let touched = false
|
|
@@ -210,6 +291,37 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
210
291
|
d.encounterCount += 1
|
|
211
292
|
d.familiarity = Math.min( 1, d.familiarity + this._growthRate * ( 1 - d.familiarity ) )
|
|
212
293
|
d.lastSeenTick = tick
|
|
294
|
+
// Where this happened is part of what happened. Merged, not appended:
|
|
295
|
+
// meeting someone again in a room already known is news about that room,
|
|
296
|
+
// not a new way to reach them.
|
|
297
|
+
if( enc.thread ){
|
|
298
|
+
d.handles = withHandle( d.handles, {
|
|
299
|
+
keid: enc.thread,
|
|
300
|
+
kind: enc.direct === true ? 'dm' : enc.direct === false ? 'room' : 'unknown',
|
|
301
|
+
lastSeenTick: tick,
|
|
302
|
+
} as Handle )
|
|
303
|
+
|
|
304
|
+
// The ROOM is a referent too, and gets a dossier of its own.
|
|
305
|
+
//
|
|
306
|
+
// `kind: 'thing'` has existed since this tracker shipped and nothing had
|
|
307
|
+
// ever created one — SENTIENT_DOMAINS admits only audition, so the seat
|
|
308
|
+
// for a non-person was built and left empty. A place earns exactly the
|
|
309
|
+
// same things a person does: familiarity with the room, a felt valence
|
|
310
|
+
// toward it, and a reliability that means something real — whether the
|
|
311
|
+
// mind gets answered there. A shared room nobody replies in is a fact
|
|
312
|
+
// about the ROOM as much as about the people in it, and until now there
|
|
313
|
+
// was nowhere to put it.
|
|
314
|
+
//
|
|
315
|
+
// Private threads are deliberately excluded: a DM is not a place, it is
|
|
316
|
+
// the person. Giving it a dossier would double every someone.
|
|
317
|
+
if( enc.direct === false ){
|
|
318
|
+
const place = this._getOrCreate( enc.thread, 'place', tick )
|
|
319
|
+
place.encounterCount += 1
|
|
320
|
+
place.familiarity = Math.min( 1, place.familiarity + this._growthRate * ( 1 - place.familiarity ) )
|
|
321
|
+
place.lastSeenTick = tick
|
|
322
|
+
place.resolutionConfidence = this._resolution( place )
|
|
323
|
+
}
|
|
324
|
+
}
|
|
213
325
|
if( enc.name && !d.name ) d.name = enc.name // learn a name the channel offers
|
|
214
326
|
d.resolutionConfidence = this._resolution( d )
|
|
215
327
|
touched = true
|
|
@@ -219,6 +331,16 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
219
331
|
// toward, not slammed). A known someone can exist here with no perceptual encounter.
|
|
220
332
|
for( const u of this._pendingConscious.splice( 0 ) ){
|
|
221
333
|
const d = this._getOrCreate( u.keid, 'audition', tick ) // a known *someone*
|
|
334
|
+
|
|
335
|
+
// "These two are the same person." The mind settling a doubt the heuristic
|
|
336
|
+
// was not entitled to settle — it has reasons a name-match does not, usually
|
|
337
|
+
// because somebody just told it. Allowed to fuse two ESTABLISHED referents,
|
|
338
|
+
// which is exactly what `_recognise` refuses to do on its own.
|
|
339
|
+
if( u.sameAs ){
|
|
340
|
+
const other = this._dossiers.get( canonicalOf( this._aliases, u.sameAs ) )
|
|
341
|
+
if( other && other.keid !== d.keid ) this._fuse( d, other, commands )
|
|
342
|
+
}
|
|
343
|
+
|
|
222
344
|
if( u.name ) d.name = u.name
|
|
223
345
|
if( u.feeling != null )
|
|
224
346
|
d.valence = Math.max( -1, Math.min( 1, d.valence + 0.5 * ( u.feeling - d.valence ) ) )
|
|
@@ -230,7 +352,12 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
230
352
|
// performs when acted on (general: a tool, a place, or a person). Only updates an
|
|
231
353
|
// already-known referent (acting on something you've never perceived doesn't conjure it).
|
|
232
354
|
for( const o of this._pendingOutcomes.splice( 0 ) ){
|
|
233
|
-
|
|
355
|
+
// Through the alias table: an `action.outcome` names its target the way the
|
|
356
|
+
// ACTOR did — a transport address, or whatever the host called it — and the
|
|
357
|
+
// dossier lives under the anchor. Reading `_dossiers` raw here silently
|
|
358
|
+
// dropped every outcome once addresses became aliases, so a referent could
|
|
359
|
+
// never earn or lose reliability again.
|
|
360
|
+
const d = this._dossiers.get( canonicalOf( this._aliases, o.keid ) )
|
|
234
361
|
if( !d ) continue
|
|
235
362
|
d.reliability = Math.max( 0, Math.min( 1, d.reliability + this._reliabilityRate * ( o.signal - d.reliability ) ) )
|
|
236
363
|
touched = true
|
|
@@ -255,6 +382,11 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
255
382
|
commands.delete!.push(`ke-${d.keid}`)
|
|
256
383
|
}
|
|
257
384
|
|
|
385
|
+
// Persist anchors minted this tick BEFORE the dossiers that depend on them.
|
|
386
|
+
for( const { alias, canonical } of this._mintedAliases.splice( 0 ) )
|
|
387
|
+
commands.set!.push({ id: `kea-${ alias }`, type: 'known-entity-alias',
|
|
388
|
+
metadata: { aliasKeid: alias, canonicalKeid: canonical } })
|
|
389
|
+
|
|
258
390
|
// Persist dossiers (the perceptual layer of the known-entity node).
|
|
259
391
|
for( const d of this._dossiers.values() ){
|
|
260
392
|
if( d.encounterCount === 0 && !d.name ) continue // keep a named-but-unseen someone
|
|
@@ -271,6 +403,8 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
271
403
|
encounterCount: d.encounterCount,
|
|
272
404
|
lastSeenTick: d.lastSeenTick,
|
|
273
405
|
resolutionConfidence: d.resolutionConfidence,
|
|
406
|
+
handles: d.handles,
|
|
407
|
+
...( d.suspectedSameAs?.length ? { suspectedSameAs: d.suspectedSameAs } : {} ),
|
|
274
408
|
},
|
|
275
409
|
})
|
|
276
410
|
}
|
|
@@ -329,10 +463,66 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
329
463
|
// ── Public API ───────────────────────────────────────────
|
|
330
464
|
|
|
331
465
|
/** The dossier for a referent, if the Will has one. */
|
|
332
|
-
|
|
466
|
+
/**
|
|
467
|
+
* The dossier for anything that names this referent — its anchor, or any address
|
|
468
|
+
* it has been met at.
|
|
469
|
+
*
|
|
470
|
+
* Alias-aware because a caller has no business knowing the anchor. This read
|
|
471
|
+
* `_dossiers.get(keid)` raw, so the moment addresses became aliases of a minted
|
|
472
|
+
* anchor, every caller holding a transport id got `undefined` — the mind would
|
|
473
|
+
* have looked up someone it knows perfectly well and found a stranger. The
|
|
474
|
+
* existing Phase-4 tests caught it, which is exactly what they are for.
|
|
475
|
+
*/
|
|
476
|
+
getDossier( keid: string ): KnownEntity | undefined {
|
|
477
|
+
return this._dossiers.get( canonicalOf( this._aliases, keid ) )
|
|
478
|
+
}
|
|
333
479
|
|
|
334
480
|
// ── Internal ─────────────────────────────────────────────
|
|
335
481
|
|
|
482
|
+
/**
|
|
483
|
+
* Absorb `alias` into `canon` — one someone where there were two.
|
|
484
|
+
*
|
|
485
|
+
* Shared by the recognition heuristic and by the mind's own `sameAs` verdict,
|
|
486
|
+
* because "these are the same person" must mean the same thing whichever
|
|
487
|
+
* concluded it. The only difference is who is ENTITLED to conclude it: the
|
|
488
|
+
* heuristic will not absorb an established relationship, the mind may, because
|
|
489
|
+
* it has reasons a name-match does not — usually that somebody just told it.
|
|
490
|
+
*
|
|
491
|
+
* Routes MOVE. This used to delete the absorbed dossier and keep only a
|
|
492
|
+
* redirect, so the mind concluded "same person" and in the same breath threw
|
|
493
|
+
* away the second way to reach them.
|
|
494
|
+
*/
|
|
495
|
+
private _fuse( canon: KnownEntity, alias: KnownEntity, commands: StateCommands ): void {
|
|
496
|
+
canon.encounterCount += alias.encounterCount
|
|
497
|
+
canon.familiarity = Math.max( canon.familiarity, alias.familiarity )
|
|
498
|
+
canon.resolutionConfidence = Math.max( canon.resolutionConfidence, alias.resolutionConfidence )
|
|
499
|
+
canon.lastSeenTick = Math.max( canon.lastSeenTick as unknown as number, alias.lastSeenTick as unknown as number ) as unknown as Tick
|
|
500
|
+
canon.valence = ( canon.valence + alias.valence ) / 2
|
|
501
|
+
canon.reliability = ( canon.reliability + alias.reliability ) / 2
|
|
502
|
+
canon.name ??= alias.name
|
|
503
|
+
for( const h of alias.handles ) canon.handles = withHandle( canon.handles, h )
|
|
504
|
+
|
|
505
|
+
// The doubt is answered — on both sides, and about each other only. A
|
|
506
|
+
// suspicion left standing after the fusion would have the mind keep wondering
|
|
507
|
+
// about a question it has already settled.
|
|
508
|
+
const settled = ( held: string[] | undefined ): string[] | undefined => {
|
|
509
|
+
const out = ( held ?? [] ).filter( k => k !== alias.keid && k !== canon.keid )
|
|
510
|
+
return out.length > 0 ? out : undefined
|
|
511
|
+
}
|
|
512
|
+
canon.suspectedSameAs = settled([ ...( canon.suspectedSameAs ?? [] ), ...( alias.suspectedSameAs ?? [] ) ])
|
|
513
|
+
|
|
514
|
+
// Every address that pointed at the absorbed referent must now point here, or
|
|
515
|
+
// the next message from one of them mints the person all over again.
|
|
516
|
+
for( const [ a, c ] of this._aliases )
|
|
517
|
+
if( c === alias.keid ) this._aliases.set( a, canon.keid )
|
|
518
|
+
|
|
519
|
+
this._dossiers.delete( alias.keid )
|
|
520
|
+
this._aliases.set( alias.keid, canon.keid )
|
|
521
|
+
commands.delete!.push(`ke-${ alias.keid }`)
|
|
522
|
+
commands.set!.push({ id: `kea-${ alias.keid }`, type: 'known-entity-alias',
|
|
523
|
+
metadata: { aliasKeid: alias.keid, canonicalKeid: canon.keid } })
|
|
524
|
+
}
|
|
525
|
+
|
|
336
526
|
/** Resolution confidence: a learned name plus repeated encounters identify a referent. */
|
|
337
527
|
private _resolution( d: KnownEntity ): number {
|
|
338
528
|
return Math.min( 1, ( d.name ? 0.4 : 0 ) + Math.min( 0.6, d.encounterCount * 0.05 ) )
|
|
@@ -361,34 +551,61 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
361
551
|
// handle is already an established relationship, or if both were active concurrently
|
|
362
552
|
// (two interlocutors at once ⇒ distinct). A same-name match alone is too weak.
|
|
363
553
|
const gap = Math.abs( ( canon.lastSeenTick as unknown as number ) - ( alias.lastSeenTick as unknown as number ) )
|
|
364
|
-
|
|
554
|
+
|
|
555
|
+
// Concurrency is evidence AGAINST: two people talking at once are two
|
|
556
|
+
// people, and there is nothing to wonder about. Say nothing.
|
|
557
|
+
if( gap < RECOGNITION_CONCURRENCY_WINDOW ) continue
|
|
558
|
+
|
|
559
|
+
// Establishment is merely insufficient evidence FOR. The two may well be
|
|
560
|
+
// one someone; the heuristic is just not entitled to decide it, because
|
|
561
|
+
// absorbing an established relationship would take a real person's
|
|
562
|
+
// history with them. So the mind is told, and gets to settle it itself —
|
|
563
|
+
// usually the way anyone would, by asking.
|
|
564
|
+
if( alias.encounterCount >= RECOGNITION_MERGE_MAX_ENCOUNTERS ){
|
|
565
|
+
for( const [ a, b ] of [ [ canon, alias ], [ alias, canon ] ] as const ){
|
|
566
|
+
const held = a.suspectedSameAs ?? []
|
|
567
|
+
if( !held.includes( b.keid ) ) a.suspectedSameAs = [ ...held, b.keid ].sort()
|
|
568
|
+
}
|
|
569
|
+
merged = true // the doubt is a change worth persisting
|
|
365
570
|
continue
|
|
571
|
+
}
|
|
366
572
|
|
|
367
|
-
|
|
368
|
-
canon.familiarity = Math.max( canon.familiarity, alias.familiarity )
|
|
369
|
-
canon.resolutionConfidence = Math.max( canon.resolutionConfidence, alias.resolutionConfidence )
|
|
370
|
-
canon.lastSeenTick = Math.max( canon.lastSeenTick as unknown as number, alias.lastSeenTick as unknown as number ) as unknown as Tick
|
|
371
|
-
canon.valence = ( canon.valence + alias.valence ) / 2
|
|
372
|
-
canon.reliability = ( canon.reliability + alias.reliability ) / 2
|
|
373
|
-
|
|
374
|
-
this._dossiers.delete( alias.keid )
|
|
375
|
-
this._aliases.set( alias.keid, canon.keid )
|
|
376
|
-
commands.delete!.push(`ke-${alias.keid}`)
|
|
377
|
-
commands.set!.push({ id: `kea-${alias.keid}`, type: 'known-entity-alias',
|
|
378
|
-
metadata: { aliasKeid: alias.keid, canonicalKeid: canon.keid } })
|
|
573
|
+
this._fuse( canon, alias, commands )
|
|
379
574
|
merged = true
|
|
380
575
|
}
|
|
381
576
|
}
|
|
382
577
|
return merged
|
|
383
578
|
}
|
|
384
579
|
|
|
580
|
+
/**
|
|
581
|
+
* The dossier for a referent, minting its anchor the first time it is met.
|
|
582
|
+
*
|
|
583
|
+
* A transport address arriving here (`discord:1019…`) is not an identity, it is
|
|
584
|
+
* a way the world happened to name someone. So it becomes an ALIAS of a fresh
|
|
585
|
+
* `ke:` anchor, and the dossier lives under the anchor. Everything downstream —
|
|
586
|
+
* reputation, theory-of-mind, attachment, goals, the PMA — keeps working
|
|
587
|
+
* untouched: it still sees one opaque string per referent, which is simply no
|
|
588
|
+
* longer a route. The same human met later on another channel resolves to this
|
|
589
|
+
* same anchor instead of becoming a second person nobody could connect.
|
|
590
|
+
*/
|
|
385
591
|
private _getOrCreate( keid: string, domain: string, tick: Tick ): KnownEntity {
|
|
386
|
-
keid = this._aliases
|
|
592
|
+
keid = canonicalOf( this._aliases, keid ) // an alias lands on the canonical dossier
|
|
387
593
|
const existing = this._dossiers.get( keid )
|
|
388
594
|
if( existing ) return existing
|
|
389
595
|
|
|
596
|
+
// Mint the anchor for a transport address seen for the first time. Deterministic
|
|
597
|
+
// from that address, so a replay of a recorded run mints the same id (R2).
|
|
598
|
+
let anchor = keid
|
|
599
|
+
if( !isReferentId( keid ) ){
|
|
600
|
+
anchor = mintReferentId( keid )
|
|
601
|
+
this._aliases.set( keid, anchor )
|
|
602
|
+
this._mintedAliases.push({ alias: keid, canonical: anchor })
|
|
603
|
+
const already = this._dossiers.get( anchor )
|
|
604
|
+
if( already ) return already
|
|
605
|
+
}
|
|
606
|
+
|
|
390
607
|
const d: KnownEntity = {
|
|
391
|
-
keid,
|
|
608
|
+
keid: anchor,
|
|
392
609
|
kind: SENTIENT_DOMAINS.has( domain ) ? 'sentient' : 'thing',
|
|
393
610
|
familiarity: 0,
|
|
394
611
|
valence: 0,
|
|
@@ -396,15 +613,35 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
396
613
|
encounterCount: 0,
|
|
397
614
|
lastSeenTick: tick,
|
|
398
615
|
resolutionConfidence: 0,
|
|
616
|
+
handles: [],
|
|
399
617
|
}
|
|
400
|
-
this._dossiers.set(
|
|
618
|
+
this._dossiers.set( anchor, d )
|
|
401
619
|
return d
|
|
402
620
|
}
|
|
403
621
|
|
|
404
622
|
/** Keep the most-familiar dossiers; absence-faded acquaintances fall away (forgetting). */
|
|
623
|
+
/**
|
|
624
|
+
* Forget the least-held referents when over capacity.
|
|
625
|
+
*
|
|
626
|
+
* Ranked by more than exposure, deliberately. This sorted on `familiarity`
|
|
627
|
+
* alone, which is MERE EXPOSURE — and now that a referent need not be a person
|
|
628
|
+
* (a document, a repo, a room), things get far more exposure than people do. A
|
|
629
|
+
* mind that touched sixty files would have evicted a colleague it speaks to
|
|
630
|
+
* weekly in favour of a config file it opened a lot, silently, taking that
|
|
631
|
+
* person's reputation, theory-of-mind model and attachment bond with it.
|
|
632
|
+
*
|
|
633
|
+
* So a referent the mind has actually got to know is stickier than one it has
|
|
634
|
+
* merely seen often: knowing their NAME is the single strongest signal (it is
|
|
635
|
+
* what distinguishes a someone from a blip), then how resolved the referent is,
|
|
636
|
+
* then exposure. Nothing here is about being a person — a named, well-resolved
|
|
637
|
+
* document outranks a glimpsed stranger, which is correct.
|
|
638
|
+
*/
|
|
405
639
|
private _prune(): void {
|
|
406
640
|
if( this._dossiers.size <= this._maxTracked ) return
|
|
407
|
-
const
|
|
641
|
+
const hold = ( d: KnownEntity ): number =>
|
|
642
|
+
( d.name ? 1 : 0 ) + d.resolutionConfidence + d.familiarity
|
|
643
|
+
const sorted = [ ...this._dossiers.values() ]
|
|
644
|
+
.sort( ( a, b ) => hold( b ) - hold( a ) || ( a.keid < b.keid ? -1 : 1 ) )
|
|
408
645
|
for( const d of sorted.slice( this._maxTracked ) ) this._dossiers.delete( d.keid )
|
|
409
646
|
}
|
|
410
647
|
|
|
@@ -430,6 +667,8 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
430
667
|
familiarity: ( m['familiarity'] as number ) ?? 0,
|
|
431
668
|
valence: ( m['valence'] as number ) ?? 0,
|
|
432
669
|
reliability: ( m['reliability'] as number ) ?? 0.5,
|
|
670
|
+
handles: Array.isArray( m['handles'] ) ? ( m['handles'] as Handle[] ) : [],
|
|
671
|
+
...( Array.isArray( m['suspectedSameAs'] ) ? { suspectedSameAs: m['suspectedSameAs'] as string[] } : {} ),
|
|
433
672
|
encounterCount: ( m['encounterCount'] as number ) ?? 0,
|
|
434
673
|
lastSeenTick: ( m['lastSeenTick'] as Tick ) ?? ( 0 as unknown as Tick ),
|
|
435
674
|
resolutionConfidence: ( m['resolutionConfidence'] as number ) ?? 0,
|
|
@@ -308,8 +308,13 @@ export class MoralEvaluator implements SimulationEngine, CognitiveEngine {
|
|
|
308
308
|
})
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
// Own actions that could be violations
|
|
312
|
-
|
|
311
|
+
// Own actions that could be violations. `decision.record` is what carries
|
|
312
|
+
// them (InhibitionController writes it); an `action.own` alternative was
|
|
313
|
+
// scanned alongside it and never written by anything (#114). Unlike the
|
|
314
|
+
// social/threat host seams, this is an INTERNAL record of the mind's own
|
|
315
|
+
// conduct — not something a host supplies — so the dead half is removed
|
|
316
|
+
// rather than documented.
|
|
317
|
+
if( entity.type === 'decision.record'){
|
|
313
318
|
const
|
|
314
319
|
outcome = entity.metadata?.outcome as string ?? '',
|
|
315
320
|
harmful = entity.metadata?.harmful === true,
|
|
@@ -386,7 +391,7 @@ export class MoralEvaluator implements SimulationEngine, CognitiveEngine {
|
|
|
386
391
|
let actionCount = 0
|
|
387
392
|
|
|
388
393
|
for( const entity of state.entities.values() ){
|
|
389
|
-
if( entity.type !== '
|
|
394
|
+
if( entity.type !== 'decision.record') continue
|
|
390
395
|
|
|
391
396
|
const
|
|
392
397
|
virtuous = entity.metadata?.virtuous === true,
|
|
@@ -135,6 +135,39 @@ const CONSCIENTIOUSNESS_MORAL_GAIN = -0.2 // −moral.eventThreshold per unit
|
|
|
135
135
|
// native scales — see the selector's effectiveSwitchCost. Lower gain to match the smaller base.
|
|
136
136
|
const CONSCIENTIOUSNESS_SELECTOR_FOCUS_GAIN = 0.1 // +action-selector.switchCost per unit conscientiousness (resists action-interruption)
|
|
137
137
|
|
|
138
|
+
// Attentional BREADTH — how many focused facets this mind holds at once
|
|
139
|
+
// (engine-config-executive.maxFacets, the FacetSupervisor's ceiling). Two opposing
|
|
140
|
+
// dispositional pulls on one param, the pattern rules 17/17b already use: an open
|
|
141
|
+
// mind spreads itself across more at once, a conscientious one narrows to fewer and
|
|
142
|
+
// sees them through. Scaled against a base of 10, so a strongly-expressed trait moves
|
|
143
|
+
// the ceiling by a few threads, not by an order of magnitude — and consolidatePrior's
|
|
144
|
+
// per-step and cumulative caps bound it regardless.
|
|
145
|
+
const OPENNESS_BREADTH_GAIN = 4 // +executive.maxFacets per unit openness (hold more at once)
|
|
146
|
+
const CONSCIENTIOUSNESS_BREADTH_GAIN = -3 // −executive.maxFacets per unit conscientiousness (fewer, seen through)
|
|
147
|
+
|
|
148
|
+
// The same breadth disposition at the ATTENDING level — how many things the mind
|
|
149
|
+
// holds in view at once (engine-config-attention.maxFoci, the allocator's slot cap),
|
|
150
|
+
// as opposed to how many threads it keeps open (maxFacets above). One disposition,
|
|
151
|
+
// two owners — rules 28/28b's pattern. Scaled against a base of 4, so the same trait
|
|
152
|
+
// moves both levels proportionally rather than pulling them apart.
|
|
153
|
+
const OPENNESS_FOCI_GAIN = 1.5 // +attention.maxFoci per unit openness
|
|
154
|
+
const CONSCIENTIOUSNESS_FOCI_GAIN = -1.2 // −attention.maxFoci per unit conscientiousness
|
|
155
|
+
|
|
156
|
+
// How long the Will sits with something it has already said before saying it again
|
|
157
|
+
// (action-selector.repeatDamping, EXAFFERENCE P5). Agreeableness gives people room;
|
|
158
|
+
// demonstrated persistence chases the answer. Scaled against a base of 0.30.
|
|
159
|
+
const AGREEABLENESS_PATIENCE_GAIN = 0.12 // +repeatDamping per unit agreeableness
|
|
160
|
+
const PERSISTENCE_FOLLOWUP_GAIN = -0.10 // −repeatDamping per unit persistence
|
|
161
|
+
// The same disposition on the WINDOW rather than the strength: how long what it
|
|
162
|
+
// said stands before coming back to it. Scaled against a base of 60 ticks.
|
|
163
|
+
const AGREEABLENESS_WINDOW_GAIN = 24 // +repeatWindowTicks per unit agreeableness
|
|
164
|
+
const PERSISTENCE_WINDOW_GAIN = -20 // −repeatWindowTicks per unit persistence
|
|
165
|
+
// Who the mind is drawn toward. Agreeableness leans into reciprocity (toward the
|
|
166
|
+
// people who answer); demonstrated persistence leans the other way, and can carry
|
|
167
|
+
// this SIGNED weight negative — a mind that reaches hardest for the silence.
|
|
168
|
+
const AGREEABLENESS_RECIPROCITY_GAIN = 0.12 // +action-selector.socialWeight per unit agreeableness
|
|
169
|
+
const PERSISTENCE_RECIPROCITY_GAIN = -0.14 // −action-selector.socialWeight per unit persistence
|
|
170
|
+
|
|
138
171
|
// Emotional stability (formed from observed affect dynamics, not task success) develops
|
|
139
172
|
// the affect *reactivity gain* down: a steadier Will lets frustration snowball into
|
|
140
173
|
// chronic irritability more slowly. Distinct axis from resilience — resilience tunes how
|
|
@@ -346,6 +379,11 @@ export class PersonaConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
346
379
|
[ 'persona.executive.impulsivity_delta', next.priors[ 'engine-config-executive' ]?.impulsivity ?? 0 ],
|
|
347
380
|
[ 'persona.frustration.irritability_rate_delta', next.priors[ 'engine-config-frustration' ]?.irritabilityRate ?? 0 ],
|
|
348
381
|
[ 'persona.executive.deliberate_threshold_delta', next.priors[ 'engine-config-executive' ]?.deliberateThreshold ?? 0 ],
|
|
382
|
+
[ 'persona.executive.max_facets_delta', next.priors[ 'engine-config-executive' ]?.maxFacets ?? 0 ],
|
|
383
|
+
[ 'persona.attention.max_foci_delta', next.priors[ 'engine-config-attention' ]?.maxFoci ?? 0 ],
|
|
384
|
+
[ 'persona.selector.repeat_damping_delta', next.priors[ 'engine-config-action-selector' ]?.repeatDamping ?? 0 ],
|
|
385
|
+
[ 'persona.selector.repeat_window_delta', next.priors[ 'engine-config-action-selector' ]?.repeatWindowTicks ?? 0 ],
|
|
386
|
+
[ 'persona.selector.social_weight_delta', next.priors[ 'engine-config-action-selector' ]?.socialWeight ?? 0 ],
|
|
349
387
|
[ 'persona.reward.social_weight_delta', next.priors[ 'engine-config-reward' ]?.socialWeight ?? 0 ],
|
|
350
388
|
[ 'persona.frustration.anger_reactivity_delta', next.priors[ 'engine-config-frustration' ]?.angerReactivity ?? 0 ],
|
|
351
389
|
[ 'persona.novelty.significance_threshold_delta', next.priors[ 'engine-config-novelty' ]?.significanceThreshold ?? 0 ],
|
|
@@ -730,6 +768,109 @@ export class PersonaConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
730
768
|
engineConfigId: 'engine-config-task-switcher',
|
|
731
769
|
param: 'baseSwitchCost'
|
|
732
770
|
},
|
|
771
|
+
// 27c. Attentional BREADTH — how many things this mind holds at once (the
|
|
772
|
+
// FacetSupervisor's facet ceiling). Openness widens it; conscientiousness
|
|
773
|
+
// narrows it. Two opposing pulls on one param (rules 17/17b's pattern), so a
|
|
774
|
+
// Will that is both lands somewhere of its own rather than at either extreme.
|
|
775
|
+
// This is what makes "I can hold five conversations" or "I do one thing at a
|
|
776
|
+
// time" a fact about the person instead of a constant — the ceiling is the
|
|
777
|
+
// persona's; spare attention only scales the live allowance within it.
|
|
778
|
+
{
|
|
779
|
+
magnitude: openDev,
|
|
780
|
+
threshold: GRIT_THRESHOLD,
|
|
781
|
+
gain: OPENNESS_BREADTH_GAIN,
|
|
782
|
+
engineConfigId: 'engine-config-executive',
|
|
783
|
+
param: 'maxFacets'
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
magnitude: conscDev,
|
|
787
|
+
threshold: GRIT_THRESHOLD,
|
|
788
|
+
gain: CONSCIENTIOUSNESS_BREADTH_GAIN,
|
|
789
|
+
engineConfigId: 'engine-config-executive',
|
|
790
|
+
param: 'maxFacets'
|
|
791
|
+
},
|
|
792
|
+
// 27d. The same breadth disposition one level down: how many things the mind
|
|
793
|
+
// holds IN VIEW at once (the allocator's `maxFoci` slots), as against how
|
|
794
|
+
// many threads it keeps OPEN (27c). A reasoning facet now competes for
|
|
795
|
+
// these slots like any percept, so the two levels are one economy — a Will
|
|
796
|
+
// can be in ten conversations while attending to two, and which two is
|
|
797
|
+
// settled by salience against everything else it could be noticing.
|
|
798
|
+
// `maxFoci` was already config-mirrored and read through the persona-prior;
|
|
799
|
+
// it just had no description and no edge, so it could never actually move.
|
|
800
|
+
{
|
|
801
|
+
magnitude: openDev,
|
|
802
|
+
threshold: GRIT_THRESHOLD,
|
|
803
|
+
gain: OPENNESS_FOCI_GAIN,
|
|
804
|
+
engineConfigId: 'engine-config-attention',
|
|
805
|
+
param: 'maxFoci'
|
|
806
|
+
},
|
|
807
|
+
{
|
|
808
|
+
magnitude: conscDev,
|
|
809
|
+
threshold: GRIT_THRESHOLD,
|
|
810
|
+
gain: CONSCIENTIOUSNESS_FOCI_GAIN,
|
|
811
|
+
engineConfigId: 'engine-config-attention',
|
|
812
|
+
param: 'maxFoci'
|
|
813
|
+
},
|
|
814
|
+
// 27e. How long the Will lets its own words stand before repeating them. A
|
|
815
|
+
// delivered act leaves a live footprint (EXAFFERENCE P5) that damps redoing
|
|
816
|
+
// it; how HARD it damps is a disposition. An agreeable Will gives people
|
|
817
|
+
// room after saying its piece; a persistent one chases the answer sooner.
|
|
818
|
+
// Opposing pulls on one param, same as 17/17b. This is the trait behind
|
|
819
|
+
// "she messaged me the same thing three times" — the mechanism stops the
|
|
820
|
+
// loop, this decides where in the range between patient and dogged she sits.
|
|
821
|
+
{
|
|
822
|
+
magnitude: agreeableDev,
|
|
823
|
+
threshold: GRIT_THRESHOLD,
|
|
824
|
+
gain: AGREEABLENESS_PATIENCE_GAIN,
|
|
825
|
+
engineConfigId: 'engine-config-action-selector',
|
|
826
|
+
param: 'repeatDamping'
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
magnitude: persistDev,
|
|
830
|
+
threshold: GRIT_THRESHOLD,
|
|
831
|
+
gain: PERSISTENCE_FOLLOWUP_GAIN,
|
|
832
|
+
engineConfigId: 'engine-config-action-selector',
|
|
833
|
+
param: 'repeatDamping'
|
|
834
|
+
},
|
|
835
|
+
// 27f. The same patience on the satiation WINDOW rather than its strength —
|
|
836
|
+
// 27e decides how hard having spoken damps speaking again, this decides
|
|
837
|
+
// for how long. Kept separate because a mind can be quick to repeat but
|
|
838
|
+
// only briefly, or slow to repeat but for a long time.
|
|
839
|
+
{
|
|
840
|
+
magnitude: agreeableDev,
|
|
841
|
+
threshold: GRIT_THRESHOLD,
|
|
842
|
+
gain: AGREEABLENESS_WINDOW_GAIN,
|
|
843
|
+
engineConfigId: 'engine-config-action-selector',
|
|
844
|
+
param: 'repeatWindowTicks'
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
magnitude: persistDev,
|
|
848
|
+
threshold: GRIT_THRESHOLD,
|
|
849
|
+
gain: PERSISTENCE_WINDOW_GAIN,
|
|
850
|
+
engineConfigId: 'engine-config-action-selector',
|
|
851
|
+
param: 'repeatWindowTicks'
|
|
852
|
+
},
|
|
853
|
+
// 27g. Who the Will is drawn toward. `socialWeight` scales its learned read on
|
|
854
|
+
// a person in the action competition, and it is the one weight left
|
|
855
|
+
// deliberately SIGNED: agreeableness leans into reciprocity (toward the
|
|
856
|
+
// people who answer), demonstrated persistence leans against it and can
|
|
857
|
+
// carry the weight negative — a mind that reaches hardest for the silence
|
|
858
|
+
// precisely because it is silent. Both are coherent colleagues, so the
|
|
859
|
+
// container declines to pick and lets the persona land where it lands.
|
|
860
|
+
{
|
|
861
|
+
magnitude: agreeableDev,
|
|
862
|
+
threshold: GRIT_THRESHOLD,
|
|
863
|
+
gain: AGREEABLENESS_RECIPROCITY_GAIN,
|
|
864
|
+
engineConfigId: 'engine-config-action-selector',
|
|
865
|
+
param: 'socialWeight'
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
magnitude: persistDev,
|
|
869
|
+
threshold: GRIT_THRESHOLD,
|
|
870
|
+
gain: PERSISTENCE_RECIPROCITY_GAIN,
|
|
871
|
+
engineConfigId: 'engine-config-action-selector',
|
|
872
|
+
param: 'socialWeight'
|
|
873
|
+
},
|
|
733
874
|
// 28b. Same disposition, the AGENCY selector's owner (R2): a conscientious Will also
|
|
734
875
|
// resists having an in-flight action preempted — raise the selector's switch-cost
|
|
735
876
|
// hysteresis. Shares the conscientiousness driver with rule 28 (one switch-resistance
|