@mindot/will 0.8.0 → 0.10.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 +15 -11
- package/dist/index.d.ts +188 -141
- package/dist/index.js +6985 -3337
- package/dist/index.js.map +1 -1
- package/dist/surface/channels/discord.d.ts +177 -0
- package/dist/surface/channels/discord.js +394 -0
- package/dist/surface/channels/discord.js.map +1 -0
- package/dist/{channels → surface/channels}/whatsapp.d.ts +2 -2
- package/dist/{channels → surface/channels}/whatsapp.js +11 -5
- package/dist/surface/channels/whatsapp.js.map +1 -0
- package/dist/{cli.js → surface/cli.js} +5382 -1573
- package/dist/surface/cli.js.map +1 -0
- package/dist/{mcp → surface/mcp}/effectors.d.ts +1 -1
- package/dist/{mcp → surface/mcp}/effectors.js +10 -4
- package/dist/surface/mcp/effectors.js.map +1 -0
- package/dist/{will-cS6k4uiJ.d.ts → will-evj9_vrd.d.ts} +6658 -5078
- package/package.json +11 -12
- package/src/cognition/agency/consequence.ts +356 -1
- package/src/cognition/agency/conversation.aim.ts +292 -0
- package/src/cognition/agency/engines/action.selector.ts +142 -5
- package/src/cognition/agency/engines/affordance.synthesizer.ts +200 -7
- package/src/cognition/agency/engines/deliberation.engine.ts +76 -3
- package/src/cognition/agency/engines/motor.schema.executor.ts +509 -32
- package/src/cognition/agency/engines/reafference.engine.ts +160 -4
- package/src/cognition/agency/execution.primitives.ts +17 -4
- package/src/cognition/agency/proactive.communicator.ts +19 -3
- package/src/cognition/agency/reconcile.learning.ts +5 -4
- package/src/cognition/agency/restart.ts +66 -0
- package/src/cognition/agency/schemas/innate.ts +96 -1
- package/src/cognition/agency/schemas/repertoire.ts +19 -1
- package/src/cognition/agency/selection.scoring.ts +49 -0
- package/src/cognition/agency/settlement.ts +203 -0
- package/src/cognition/agency/types.ts +73 -1
- 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 +108 -1
- package/src/cognition/event.schemas.ts +22 -8
- package/src/cognition/faculties/affective.blender.ts +21 -4
- package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
- package/src/cognition/faculties/circadian.oscillator.ts +36 -7
- package/src/cognition/faculties/episodic.consolidator.ts +59 -3
- package/src/cognition/faculties/executive.engine/action.record.ts +129 -0
- package/src/cognition/faculties/executive.engine/commands.ts +189 -14
- package/src/cognition/faculties/executive.engine/context.ts +106 -46
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
- package/src/cognition/faculties/executive.engine/engine.ts +714 -136
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +230 -45
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +320 -65
- package/src/cognition/faculties/executive.engine/facet.ts +81 -26
- package/src/cognition/faculties/executive.engine/gating.ts +14 -14
- package/src/cognition/faculties/executive.engine/parser.ts +120 -38
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +331 -38
- package/src/cognition/faculties/executive.engine/types.ts +84 -12
- package/src/cognition/faculties/exteroception.ts +99 -98
- package/src/cognition/faculties/goal.manager.ts +144 -16
- package/src/cognition/faculties/known.entity.tracker.ts +287 -29
- package/src/cognition/faculties/moral.evaluator.ts +8 -3
- package/src/cognition/faculties/persona.consolidator.ts +141 -0
- package/src/cognition/faculties/planning.engine/engine.ts +4 -4
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +1 -1
- package/src/cognition/faculties/reputation.tracker.ts +66 -2
- package/src/cognition/faculties/self.model.updater.ts +19 -12
- package/src/cognition/faculties/semantic.engine/integrator.ts +1 -1
- 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 +16 -21
- package/src/cognition/identity.entity.ts +205 -0
- package/src/cognition/index.ts +7 -0
- package/src/cognition/instruction.handler.ts +1 -1
- package/src/cognition/memory/vector.adapter.ts +12 -3
- package/src/cognition/memory/vector.embedder.ts +45 -4
- package/src/cognition/percept.entity.ts +122 -0
- package/src/cognition/persona.prior.ts +6 -0
- package/src/cognition/sense.boundary.ts +176 -0
- package/src/cognition/senses/audition.engine/engine.ts +492 -53
- package/src/cognition/senses/base.sense.engine.ts +105 -7
- package/src/cognition/senses/index.ts +83 -13
- package/src/cognition/senses/provenance.ts +128 -0
- package/src/cognition/senses/somatosensation.engine.ts +117 -9
- package/src/cognition/social.identity.ts +296 -0
- package/src/cognition/utilities/token.tracker.ts +94 -10
- package/src/core/orchestrator.ts +51 -0
- package/src/index.ts +39 -5
- package/src/llm/gate.ts +48 -0
- package/src/llm/index.ts +127 -33
- package/src/llm/routing.ts +6 -0
- package/src/llm/summarizer.ts +1 -1
- package/src/llm/wire.contracts.ts +57 -0
- package/src/pma/index.ts +68 -54
- package/src/stem/assembly.audit.ts +1 -0
- package/src/stem/guards/identity.coherence.ts +1 -1
- package/src/stem/index.ts +171 -23
- package/src/stem/mind.ts +198 -57
- package/src/stem/policy/arbiter.ts +10 -0
- package/src/stem/profiles/built-in.ts +7 -0
- package/src/{profiles → stem/profiles}/companion.ts +1 -1
- package/src/{profiles → stem/profiles}/company-brain.ts +1 -1
- package/src/{profiles → stem/profiles}/customer-service.ts +1 -1
- package/src/{profiles → stem/profiles}/game-npc.ts +1 -1
- package/src/{profiles → stem/profiles}/index.ts +1 -1
- package/src/{profiles → stem/profiles}/smart-home.ts +1 -1
- package/src/stem/tracts/effector/escalation.lifecycle.ts +239 -0
- package/src/stem/tracts/effector/policy.enforcement.ts +261 -0
- package/src/stem/tracts/effector/types.ts +110 -0
- package/src/stem/tracts/effector.controller.ts +112 -401
- package/src/stem/tracts/outbox.controller.ts +30 -17
- package/src/stem/tracts/outbox.writer.ts +40 -2
- package/src/stem/tracts/sensory.controller.ts +6 -6
- package/src/stem/tracts/session.logger.ts +6 -1
- package/src/stem/tracts/transport/types.ts +20 -1
- package/src/stem/tracts/transport.controller.ts +33 -9
- package/src/surface/channels/discord.ts +609 -0
- package/src/{channels → surface/channels}/roster.ts +1 -1
- package/src/surface/channels/types.ts +136 -0
- package/src/{channels → surface/channels}/whatsapp.ts +22 -10
- package/src/{cli.ts → surface/cli.ts} +14 -9
- package/src/{host → surface/host}/boot.ts +3 -3
- package/src/{host → surface/host}/utterances.ts +2 -2
- package/src/{mcp → surface/mcp}/effectors.ts +19 -6
- package/src/{mcp → surface/mcp}/server.ts +19 -5
- package/src/{sdk → surface/sdk}/will.ts +194 -33
- package/src/{serve → surface/serve}/server.ts +11 -4
- package/src/types.ts +24 -3
- package/dist/channels/discord.d.ts +0 -69
- package/dist/channels/discord.js +0 -193
- package/dist/channels/discord.js.map +0 -1
- package/dist/channels/whatsapp.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/mcp/effectors.js.map +0 -1
- package/src/channels/discord.ts +0 -214
- package/src/channels/types.ts +0 -46
- package/src/cognition/faculties/executive.engine/messages.ts +0 -102
- package/src/extensions/livestream.ext.ts +0 -570
- package/src/extensions/time.ext.ts +0 -339
- package/src/profiles/built-in.ts +0 -7
- package/src/runners/coherence.runner.ts +0 -49
- package/src/runners/outreach.runner.ts +0 -155
- package/src/runners/social.runner.ts +0 -179
- package/src/runners/thin-shim.runner.ts +0 -205
- /package/dist/{cli.d.ts → surface/cli.d.ts} +0 -0
|
@@ -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. */
|
|
@@ -80,11 +108,33 @@ const SENTIENT_DOMAINS = new Set<string>([ 'audition' ])
|
|
|
80
108
|
// specific "get to know them" pull; it subsides once resolution reaches RESOLVED.
|
|
81
109
|
const CURIOUS_FAMILIARITY = 0.5
|
|
82
110
|
const CURIOUS_RESOLUTION = 0.4
|
|
83
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Resolved enough to stop wondering — the point the curiosity goal completes at.
|
|
113
|
+
*
|
|
114
|
+
* Exported because the AffordanceSynthesizer offers `inspect` against referents
|
|
115
|
+
* BELOW it. Two readers, one constant, deliberately: if the act that satisfies
|
|
116
|
+
* curiosity and the goal that measures it disagreed about when something is
|
|
117
|
+
* known, a mind could keep being offered a look at what it had already placed —
|
|
118
|
+
* or stop being offered one while the goal it holds is still open.
|
|
119
|
+
*/
|
|
120
|
+
export const CURIOUS_RESOLVED = 0.6
|
|
84
121
|
|
|
85
122
|
// Forgetting (Phase 4). (Reliability rate is now a Channel-A developable field — analytical.)
|
|
86
123
|
const FORGET_FLOOR = 0.02 // below this familiarity, an unidentified blip is forgotten
|
|
87
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Familiarity decay for a referent the mind has NOT resolved — no name, barely
|
|
127
|
+
* identified. Two orders of magnitude faster than for a known someone, because
|
|
128
|
+
* one rate cannot serve both: a face glimpsed once in a crowd should fade in
|
|
129
|
+
* minutes, and a colleague should not.
|
|
130
|
+
*
|
|
131
|
+
* The forgetting gate already drew exactly this line (`!d.name &&
|
|
132
|
+
* resolutionConfidence < CURIOUS_RESOLUTION`); only the rate did not, so slowing
|
|
133
|
+
* decay enough for relationships to exist would have left every blip resident
|
|
134
|
+
* forever. Same value the single shared rate used to have.
|
|
135
|
+
*/
|
|
136
|
+
const BLIP_DECAY_RATE = 0.005
|
|
137
|
+
|
|
88
138
|
// Recognition (Phase 5) — guards against conflating two *different* people who share a
|
|
89
139
|
// name. Only recognise a still-thin handle (≤) into a known person, and never when both
|
|
90
140
|
// were active at the same time (two people talking at once ⇒ distinct, not one on two
|
|
@@ -109,18 +159,52 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
109
159
|
private _restored = false
|
|
110
160
|
|
|
111
161
|
// Buffered from senses.*.percept events; drained each tick in react().
|
|
112
|
-
private _pendingEncounters: Array<{
|
|
162
|
+
private _pendingEncounters: Array<{
|
|
163
|
+
keid: string; domain: string; name?: string
|
|
164
|
+
/** The room this encounter happened in, and whether it was a private one. */
|
|
165
|
+
thread?: string; direct?: boolean
|
|
166
|
+
/** What that room is called, where the channel offered a label. */
|
|
167
|
+
threadName?: string
|
|
168
|
+
}> = []
|
|
113
169
|
// Buffered from known.entity.learned (the conscious / reasoning write-path, Phase 2.2).
|
|
114
|
-
private _pendingConscious: Array<{
|
|
170
|
+
private _pendingConscious: Array<{
|
|
171
|
+
keid: string; name?: string; feeling?: number
|
|
172
|
+
/** "This is the same someone as that." The mind settling an identity itself. */
|
|
173
|
+
sameAs?: string
|
|
174
|
+
}> = []
|
|
115
175
|
// Buffered from action.outcome — the reliability track-record signal (Phase 4).
|
|
116
176
|
private _pendingOutcomes: Array<{ keid: string; signal: number }> = []
|
|
117
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Aliases minted this tick, awaiting persistence. `_getOrCreate` runs deep inside
|
|
180
|
+
* the drain loops with no access to the command list, and an alias that lives
|
|
181
|
+
* only in memory is one the mind forgets on restart — every transport address
|
|
182
|
+
* would mint a SECOND anchor next boot and the person would fork in two.
|
|
183
|
+
*/
|
|
184
|
+
private _mintedAliases: Array<{ alias: string; canonical: string }> = []
|
|
185
|
+
|
|
118
186
|
private _bus: CognitiveBus | null = null
|
|
119
187
|
private readonly _model = new GenerativeModel()
|
|
120
188
|
|
|
121
189
|
constructor( config: KnownEntityTrackerConfig = {} ){
|
|
122
190
|
this._growthRate = config.familiarityGrowthRate ?? 0.15
|
|
123
|
-
|
|
191
|
+
// 0.005/tick spends the entire scale in 200 ticks — under four minutes at the
|
|
192
|
+
// 1s tick a hosted Will actually runs on. Familiarity could therefore never
|
|
193
|
+
// accumulate for a mind whose encounters are minutes apart, and it did not:
|
|
194
|
+
// measured 0.00 on a fresh mind after 5 encounters, and 0.00 on a mind that
|
|
195
|
+
// had banked 71. A permanently-zero signal is worse than a missing one — it
|
|
196
|
+
// feeds the curiosity-to-resolve drive (which therefore never fired) and the
|
|
197
|
+
// pruning order, both of which read it as "a total stranger".
|
|
198
|
+
//
|
|
199
|
+
// Mere exposure fades over days, not minutes. At 0.00002 a full scale takes
|
|
200
|
+
// ~50k ticks (~14h at 1s), so someone met this morning is still familiar
|
|
201
|
+
// tonight and a stranger from last month has genuinely faded.
|
|
202
|
+
//
|
|
203
|
+
// The deeper flaw stays: this is per-TICK, so a psychological timescale is
|
|
204
|
+
// pinned to a host's tick rate. `react()` already receives the real delta —
|
|
205
|
+
// expressing decay in experienced time is the actual fix, and it belongs with
|
|
206
|
+
// the other per-tick decays rather than in this one engine alone.
|
|
207
|
+
this._decayRate = config.familiarityDecayRate ?? 0.00002
|
|
124
208
|
this._curiosityGain = config.curiosityGain ?? 1.0
|
|
125
209
|
this._reliabilityRate = config.reliabilityRate ?? 0.2
|
|
126
210
|
this._maxTracked = config.maxTracked ?? 50
|
|
@@ -150,9 +234,9 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
150
234
|
|
|
151
235
|
// Conscious learning (the reasoning write-path): a learned name + felt valence.
|
|
152
236
|
if( e.type === 'known.entity.learned'){
|
|
153
|
-
const u = e.payload as { keid?: string; name?: string; feeling?: number }
|
|
237
|
+
const u = e.payload as { keid?: string; name?: string; feeling?: number; sameAs?: string }
|
|
154
238
|
if( u?.keid && u.keid !== 'agent-self')
|
|
155
|
-
this._pendingConscious.push({ keid: u.keid, name: u.name, feeling: u.feeling })
|
|
239
|
+
this._pendingConscious.push({ keid: u.keid, name: u.name, feeling: u.feeling, sameAs: u.sameAs })
|
|
156
240
|
return
|
|
157
241
|
}
|
|
158
242
|
|
|
@@ -170,10 +254,19 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
170
254
|
if( !keid || keid === 'agent-self') return
|
|
171
255
|
|
|
172
256
|
// A channel-supplied display name rides on the raw input (e.g. TextMessage.speakerName).
|
|
173
|
-
|
|
257
|
+
// A channel supplies the display name AND the room, both on the raw input.
|
|
258
|
+
// `direct` is the one bit the Discord edge computed (`isDM`) and threw away
|
|
259
|
+
// before the mind could see it — the single fact that decides whether a room
|
|
260
|
+
// is the right place for a given utterance.
|
|
261
|
+
const raw = p?.raw as { speakerName?: unknown; threadId?: unknown; direct?: unknown; threadName?: unknown } | undefined
|
|
174
262
|
const name = typeof raw?.speakerName === 'string' ? raw.speakerName : undefined
|
|
263
|
+
const thread = typeof raw?.threadId === 'string' ? raw.threadId : undefined
|
|
264
|
+
const direct = typeof raw?.direct === 'boolean' ? raw.direct : undefined
|
|
265
|
+
// What the room is CALLED. A place has had a dossier since 0.9.0 and no way
|
|
266
|
+
// to be named, so every room the mind knew rendered as "something".
|
|
267
|
+
const threadName = typeof raw?.threadName === 'string' ? raw.threadName : undefined
|
|
175
268
|
|
|
176
|
-
this._pendingEncounters.push({ keid, domain: p!.domain, name })
|
|
269
|
+
this._pendingEncounters.push({ keid, domain: p!.domain, name, thread, direct, threadName })
|
|
177
270
|
}
|
|
178
271
|
|
|
179
272
|
snapshot(): Record<string, unknown> {
|
|
@@ -200,8 +293,10 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
200
293
|
|
|
201
294
|
// Familiarity fades a little each tick — absence dims the sense of an entity.
|
|
202
295
|
for( const d of this._dossiers.values() )
|
|
203
|
-
if( d.familiarity > 0 )
|
|
204
|
-
|
|
296
|
+
if( d.familiarity > 0 ){
|
|
297
|
+
const unresolved = !d.name && d.resolutionConfidence < CURIOUS_RESOLUTION
|
|
298
|
+
d.familiarity = Math.max( 0, d.familiarity - ( unresolved ? BLIP_DECAY_RATE : this._decayRate ) )
|
|
299
|
+
}
|
|
205
300
|
|
|
206
301
|
// Drain encounters — each percept is an exposure that re-warms familiarity.
|
|
207
302
|
let touched = false
|
|
@@ -210,6 +305,42 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
210
305
|
d.encounterCount += 1
|
|
211
306
|
d.familiarity = Math.min( 1, d.familiarity + this._growthRate * ( 1 - d.familiarity ) )
|
|
212
307
|
d.lastSeenTick = tick
|
|
308
|
+
// Where this happened is part of what happened. Merged, not appended:
|
|
309
|
+
// meeting someone again in a room already known is news about that room,
|
|
310
|
+
// not a new way to reach them.
|
|
311
|
+
if( enc.thread ){
|
|
312
|
+
d.handles = withHandle( d.handles, {
|
|
313
|
+
keid: enc.thread,
|
|
314
|
+
kind: enc.direct === true ? 'dm' : enc.direct === false ? 'room' : 'unknown',
|
|
315
|
+
lastSeenTick: tick,
|
|
316
|
+
} as Handle )
|
|
317
|
+
|
|
318
|
+
// The ROOM is a referent too, and gets a dossier of its own.
|
|
319
|
+
//
|
|
320
|
+
// `kind: 'thing'` has existed since this tracker shipped and nothing had
|
|
321
|
+
// ever created one — SENTIENT_DOMAINS admits only audition, so the seat
|
|
322
|
+
// for a non-person was built and left empty. A place earns exactly the
|
|
323
|
+
// same things a person does: familiarity with the room, a felt valence
|
|
324
|
+
// toward it, and a reliability that means something real — whether the
|
|
325
|
+
// mind gets answered there. A shared room nobody replies in is a fact
|
|
326
|
+
// about the ROOM as much as about the people in it, and until now there
|
|
327
|
+
// was nowhere to put it.
|
|
328
|
+
//
|
|
329
|
+
// Private threads are deliberately excluded: a DM is not a place, it is
|
|
330
|
+
// the person. Giving it a dossier would double every someone.
|
|
331
|
+
if( enc.direct === false ){
|
|
332
|
+
const place = this._getOrCreate( enc.thread, 'place', tick )
|
|
333
|
+
place.encounterCount += 1
|
|
334
|
+
place.familiarity = Math.min( 1, place.familiarity + this._growthRate * ( 1 - place.familiarity ) )
|
|
335
|
+
place.lastSeenTick = tick
|
|
336
|
+
// A room learns its name the same way a person does — from what the
|
|
337
|
+
// channel offers, and only while it has none. Re-taken every encounter
|
|
338
|
+
// it would churn the cached prompt on a rename; more to the point, a
|
|
339
|
+
// name the mind already holds is the mind's, not the platform's.
|
|
340
|
+
if( enc.threadName && !place.name ) place.name = enc.threadName
|
|
341
|
+
place.resolutionConfidence = this._resolution( place )
|
|
342
|
+
}
|
|
343
|
+
}
|
|
213
344
|
if( enc.name && !d.name ) d.name = enc.name // learn a name the channel offers
|
|
214
345
|
d.resolutionConfidence = this._resolution( d )
|
|
215
346
|
touched = true
|
|
@@ -219,6 +350,16 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
219
350
|
// toward, not slammed). A known someone can exist here with no perceptual encounter.
|
|
220
351
|
for( const u of this._pendingConscious.splice( 0 ) ){
|
|
221
352
|
const d = this._getOrCreate( u.keid, 'audition', tick ) // a known *someone*
|
|
353
|
+
|
|
354
|
+
// "These two are the same person." The mind settling a doubt the heuristic
|
|
355
|
+
// was not entitled to settle — it has reasons a name-match does not, usually
|
|
356
|
+
// because somebody just told it. Allowed to fuse two ESTABLISHED referents,
|
|
357
|
+
// which is exactly what `_recognise` refuses to do on its own.
|
|
358
|
+
if( u.sameAs ){
|
|
359
|
+
const other = this._dossiers.get( canonicalOf( this._aliases, u.sameAs ) )
|
|
360
|
+
if( other && other.keid !== d.keid ) this._fuse( d, other, commands )
|
|
361
|
+
}
|
|
362
|
+
|
|
222
363
|
if( u.name ) d.name = u.name
|
|
223
364
|
if( u.feeling != null )
|
|
224
365
|
d.valence = Math.max( -1, Math.min( 1, d.valence + 0.5 * ( u.feeling - d.valence ) ) )
|
|
@@ -230,7 +371,12 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
230
371
|
// performs when acted on (general: a tool, a place, or a person). Only updates an
|
|
231
372
|
// already-known referent (acting on something you've never perceived doesn't conjure it).
|
|
232
373
|
for( const o of this._pendingOutcomes.splice( 0 ) ){
|
|
233
|
-
|
|
374
|
+
// Through the alias table: an `action.outcome` names its target the way the
|
|
375
|
+
// ACTOR did — a transport address, or whatever the host called it — and the
|
|
376
|
+
// dossier lives under the anchor. Reading `_dossiers` raw here silently
|
|
377
|
+
// dropped every outcome once addresses became aliases, so a referent could
|
|
378
|
+
// never earn or lose reliability again.
|
|
379
|
+
const d = this._dossiers.get( canonicalOf( this._aliases, o.keid ) )
|
|
234
380
|
if( !d ) continue
|
|
235
381
|
d.reliability = Math.max( 0, Math.min( 1, d.reliability + this._reliabilityRate * ( o.signal - d.reliability ) ) )
|
|
236
382
|
touched = true
|
|
@@ -255,6 +401,11 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
255
401
|
commands.delete!.push(`ke-${d.keid}`)
|
|
256
402
|
}
|
|
257
403
|
|
|
404
|
+
// Persist anchors minted this tick BEFORE the dossiers that depend on them.
|
|
405
|
+
for( const { alias, canonical } of this._mintedAliases.splice( 0 ) )
|
|
406
|
+
commands.set!.push({ id: `kea-${ alias }`, type: 'known-entity-alias',
|
|
407
|
+
metadata: { aliasKeid: alias, canonicalKeid: canonical } })
|
|
408
|
+
|
|
258
409
|
// Persist dossiers (the perceptual layer of the known-entity node).
|
|
259
410
|
for( const d of this._dossiers.values() ){
|
|
260
411
|
if( d.encounterCount === 0 && !d.name ) continue // keep a named-but-unseen someone
|
|
@@ -271,6 +422,8 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
271
422
|
encounterCount: d.encounterCount,
|
|
272
423
|
lastSeenTick: d.lastSeenTick,
|
|
273
424
|
resolutionConfidence: d.resolutionConfidence,
|
|
425
|
+
handles: d.handles,
|
|
426
|
+
...( d.suspectedSameAs?.length ? { suspectedSameAs: d.suspectedSameAs } : {} ),
|
|
274
427
|
},
|
|
275
428
|
})
|
|
276
429
|
}
|
|
@@ -329,10 +482,66 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
329
482
|
// ── Public API ───────────────────────────────────────────
|
|
330
483
|
|
|
331
484
|
/** The dossier for a referent, if the Will has one. */
|
|
332
|
-
|
|
485
|
+
/**
|
|
486
|
+
* The dossier for anything that names this referent — its anchor, or any address
|
|
487
|
+
* it has been met at.
|
|
488
|
+
*
|
|
489
|
+
* Alias-aware because a caller has no business knowing the anchor. This read
|
|
490
|
+
* `_dossiers.get(keid)` raw, so the moment addresses became aliases of a minted
|
|
491
|
+
* anchor, every caller holding a transport id got `undefined` — the mind would
|
|
492
|
+
* have looked up someone it knows perfectly well and found a stranger. The
|
|
493
|
+
* existing Phase-4 tests caught it, which is exactly what they are for.
|
|
494
|
+
*/
|
|
495
|
+
getDossier( keid: string ): KnownEntity | undefined {
|
|
496
|
+
return this._dossiers.get( canonicalOf( this._aliases, keid ) )
|
|
497
|
+
}
|
|
333
498
|
|
|
334
499
|
// ── Internal ─────────────────────────────────────────────
|
|
335
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Absorb `alias` into `canon` — one someone where there were two.
|
|
503
|
+
*
|
|
504
|
+
* Shared by the recognition heuristic and by the mind's own `sameAs` verdict,
|
|
505
|
+
* because "these are the same person" must mean the same thing whichever
|
|
506
|
+
* concluded it. The only difference is who is ENTITLED to conclude it: the
|
|
507
|
+
* heuristic will not absorb an established relationship, the mind may, because
|
|
508
|
+
* it has reasons a name-match does not — usually that somebody just told it.
|
|
509
|
+
*
|
|
510
|
+
* Routes MOVE. This used to delete the absorbed dossier and keep only a
|
|
511
|
+
* redirect, so the mind concluded "same person" and in the same breath threw
|
|
512
|
+
* away the second way to reach them.
|
|
513
|
+
*/
|
|
514
|
+
private _fuse( canon: KnownEntity, alias: KnownEntity, commands: StateCommands ): void {
|
|
515
|
+
canon.encounterCount += alias.encounterCount
|
|
516
|
+
canon.familiarity = Math.max( canon.familiarity, alias.familiarity )
|
|
517
|
+
canon.resolutionConfidence = Math.max( canon.resolutionConfidence, alias.resolutionConfidence )
|
|
518
|
+
canon.lastSeenTick = Math.max( canon.lastSeenTick as unknown as number, alias.lastSeenTick as unknown as number ) as unknown as Tick
|
|
519
|
+
canon.valence = ( canon.valence + alias.valence ) / 2
|
|
520
|
+
canon.reliability = ( canon.reliability + alias.reliability ) / 2
|
|
521
|
+
canon.name ??= alias.name
|
|
522
|
+
for( const h of alias.handles ) canon.handles = withHandle( canon.handles, h )
|
|
523
|
+
|
|
524
|
+
// The doubt is answered — on both sides, and about each other only. A
|
|
525
|
+
// suspicion left standing after the fusion would have the mind keep wondering
|
|
526
|
+
// about a question it has already settled.
|
|
527
|
+
const settled = ( held: string[] | undefined ): string[] | undefined => {
|
|
528
|
+
const out = ( held ?? [] ).filter( k => k !== alias.keid && k !== canon.keid )
|
|
529
|
+
return out.length > 0 ? out : undefined
|
|
530
|
+
}
|
|
531
|
+
canon.suspectedSameAs = settled([ ...( canon.suspectedSameAs ?? [] ), ...( alias.suspectedSameAs ?? [] ) ])
|
|
532
|
+
|
|
533
|
+
// Every address that pointed at the absorbed referent must now point here, or
|
|
534
|
+
// the next message from one of them mints the person all over again.
|
|
535
|
+
for( const [ a, c ] of this._aliases )
|
|
536
|
+
if( c === alias.keid ) this._aliases.set( a, canon.keid )
|
|
537
|
+
|
|
538
|
+
this._dossiers.delete( alias.keid )
|
|
539
|
+
this._aliases.set( alias.keid, canon.keid )
|
|
540
|
+
commands.delete!.push(`ke-${ alias.keid }`)
|
|
541
|
+
commands.set!.push({ id: `kea-${ alias.keid }`, type: 'known-entity-alias',
|
|
542
|
+
metadata: { aliasKeid: alias.keid, canonicalKeid: canon.keid } })
|
|
543
|
+
}
|
|
544
|
+
|
|
336
545
|
/** Resolution confidence: a learned name plus repeated encounters identify a referent. */
|
|
337
546
|
private _resolution( d: KnownEntity ): number {
|
|
338
547
|
return Math.min( 1, ( d.name ? 0.4 : 0 ) + Math.min( 0.6, d.encounterCount * 0.05 ) )
|
|
@@ -361,34 +570,61 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
361
570
|
// handle is already an established relationship, or if both were active concurrently
|
|
362
571
|
// (two interlocutors at once ⇒ distinct). A same-name match alone is too weak.
|
|
363
572
|
const gap = Math.abs( ( canon.lastSeenTick as unknown as number ) - ( alias.lastSeenTick as unknown as number ) )
|
|
364
|
-
|
|
573
|
+
|
|
574
|
+
// Concurrency is evidence AGAINST: two people talking at once are two
|
|
575
|
+
// people, and there is nothing to wonder about. Say nothing.
|
|
576
|
+
if( gap < RECOGNITION_CONCURRENCY_WINDOW ) continue
|
|
577
|
+
|
|
578
|
+
// Establishment is merely insufficient evidence FOR. The two may well be
|
|
579
|
+
// one someone; the heuristic is just not entitled to decide it, because
|
|
580
|
+
// absorbing an established relationship would take a real person's
|
|
581
|
+
// history with them. So the mind is told, and gets to settle it itself —
|
|
582
|
+
// usually the way anyone would, by asking.
|
|
583
|
+
if( alias.encounterCount >= RECOGNITION_MERGE_MAX_ENCOUNTERS ){
|
|
584
|
+
for( const [ a, b ] of [ [ canon, alias ], [ alias, canon ] ] as const ){
|
|
585
|
+
const held = a.suspectedSameAs ?? []
|
|
586
|
+
if( !held.includes( b.keid ) ) a.suspectedSameAs = [ ...held, b.keid ].sort()
|
|
587
|
+
}
|
|
588
|
+
merged = true // the doubt is a change worth persisting
|
|
365
589
|
continue
|
|
590
|
+
}
|
|
366
591
|
|
|
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 } })
|
|
592
|
+
this._fuse( canon, alias, commands )
|
|
379
593
|
merged = true
|
|
380
594
|
}
|
|
381
595
|
}
|
|
382
596
|
return merged
|
|
383
597
|
}
|
|
384
598
|
|
|
599
|
+
/**
|
|
600
|
+
* The dossier for a referent, minting its anchor the first time it is met.
|
|
601
|
+
*
|
|
602
|
+
* A transport address arriving here (`discord:1019…`) is not an identity, it is
|
|
603
|
+
* a way the world happened to name someone. So it becomes an ALIAS of a fresh
|
|
604
|
+
* `ke:` anchor, and the dossier lives under the anchor. Everything downstream —
|
|
605
|
+
* reputation, theory-of-mind, attachment, goals, the PMA — keeps working
|
|
606
|
+
* untouched: it still sees one opaque string per referent, which is simply no
|
|
607
|
+
* longer a route. The same human met later on another channel resolves to this
|
|
608
|
+
* same anchor instead of becoming a second person nobody could connect.
|
|
609
|
+
*/
|
|
385
610
|
private _getOrCreate( keid: string, domain: string, tick: Tick ): KnownEntity {
|
|
386
|
-
keid = this._aliases
|
|
611
|
+
keid = canonicalOf( this._aliases, keid ) // an alias lands on the canonical dossier
|
|
387
612
|
const existing = this._dossiers.get( keid )
|
|
388
613
|
if( existing ) return existing
|
|
389
614
|
|
|
615
|
+
// Mint the anchor for a transport address seen for the first time. Deterministic
|
|
616
|
+
// from that address, so a replay of a recorded run mints the same id (R2).
|
|
617
|
+
let anchor = keid
|
|
618
|
+
if( !isReferentId( keid ) ){
|
|
619
|
+
anchor = mintReferentId( keid )
|
|
620
|
+
this._aliases.set( keid, anchor )
|
|
621
|
+
this._mintedAliases.push({ alias: keid, canonical: anchor })
|
|
622
|
+
const already = this._dossiers.get( anchor )
|
|
623
|
+
if( already ) return already
|
|
624
|
+
}
|
|
625
|
+
|
|
390
626
|
const d: KnownEntity = {
|
|
391
|
-
keid,
|
|
627
|
+
keid: anchor,
|
|
392
628
|
kind: SENTIENT_DOMAINS.has( domain ) ? 'sentient' : 'thing',
|
|
393
629
|
familiarity: 0,
|
|
394
630
|
valence: 0,
|
|
@@ -396,15 +632,35 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
396
632
|
encounterCount: 0,
|
|
397
633
|
lastSeenTick: tick,
|
|
398
634
|
resolutionConfidence: 0,
|
|
635
|
+
handles: [],
|
|
399
636
|
}
|
|
400
|
-
this._dossiers.set(
|
|
637
|
+
this._dossiers.set( anchor, d )
|
|
401
638
|
return d
|
|
402
639
|
}
|
|
403
640
|
|
|
404
641
|
/** Keep the most-familiar dossiers; absence-faded acquaintances fall away (forgetting). */
|
|
642
|
+
/**
|
|
643
|
+
* Forget the least-held referents when over capacity.
|
|
644
|
+
*
|
|
645
|
+
* Ranked by more than exposure, deliberately. This sorted on `familiarity`
|
|
646
|
+
* alone, which is MERE EXPOSURE — and now that a referent need not be a person
|
|
647
|
+
* (a document, a repo, a room), things get far more exposure than people do. A
|
|
648
|
+
* mind that touched sixty files would have evicted a colleague it speaks to
|
|
649
|
+
* weekly in favour of a config file it opened a lot, silently, taking that
|
|
650
|
+
* person's reputation, theory-of-mind model and attachment bond with it.
|
|
651
|
+
*
|
|
652
|
+
* So a referent the mind has actually got to know is stickier than one it has
|
|
653
|
+
* merely seen often: knowing their NAME is the single strongest signal (it is
|
|
654
|
+
* what distinguishes a someone from a blip), then how resolved the referent is,
|
|
655
|
+
* then exposure. Nothing here is about being a person — a named, well-resolved
|
|
656
|
+
* document outranks a glimpsed stranger, which is correct.
|
|
657
|
+
*/
|
|
405
658
|
private _prune(): void {
|
|
406
659
|
if( this._dossiers.size <= this._maxTracked ) return
|
|
407
|
-
const
|
|
660
|
+
const hold = ( d: KnownEntity ): number =>
|
|
661
|
+
( d.name ? 1 : 0 ) + d.resolutionConfidence + d.familiarity
|
|
662
|
+
const sorted = [ ...this._dossiers.values() ]
|
|
663
|
+
.sort( ( a, b ) => hold( b ) - hold( a ) || ( a.keid < b.keid ? -1 : 1 ) )
|
|
408
664
|
for( const d of sorted.slice( this._maxTracked ) ) this._dossiers.delete( d.keid )
|
|
409
665
|
}
|
|
410
666
|
|
|
@@ -430,6 +686,8 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
430
686
|
familiarity: ( m['familiarity'] as number ) ?? 0,
|
|
431
687
|
valence: ( m['valence'] as number ) ?? 0,
|
|
432
688
|
reliability: ( m['reliability'] as number ) ?? 0.5,
|
|
689
|
+
handles: Array.isArray( m['handles'] ) ? ( m['handles'] as Handle[] ) : [],
|
|
690
|
+
...( Array.isArray( m['suspectedSameAs'] ) ? { suspectedSameAs: m['suspectedSameAs'] as string[] } : {} ),
|
|
433
691
|
encounterCount: ( m['encounterCount'] as number ) ?? 0,
|
|
434
692
|
lastSeenTick: ( m['lastSeenTick'] as Tick ) ?? ( 0 as unknown as Tick ),
|
|
435
693
|
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
|