@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
package/src/stem/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { ExecutiveOutputFull } from '#faculties/executive.engine'
|
|
|
23
23
|
import type { Cognition, OutboxMessage, effectorInvocation, WorldInterface } from '#types'
|
|
24
24
|
import type { TextMessage, SensoryInput } from '#senses/index'
|
|
25
25
|
import type { ActivityEvent, ActivityEventHandler } from '#cognition/faculties/planning.engine/engine'
|
|
26
|
-
import { assembleMind, type WillConfig } from '#stem/mind'
|
|
26
|
+
import { assembleMind, backfillEngineConfigs, resolveExecutiveInterval, type WillConfig } from '#stem/mind'
|
|
27
27
|
import { reviewIdentityCoherence as runCoherenceReview, type CoherenceInput, type CoherenceResult } from '#stem/guards/identity.coherence'
|
|
28
28
|
import { SessionLogger } from '#stem/tracts/session.logger'
|
|
29
29
|
import { fileLoggingEnabled } from '#stem/tracts/transport/stream.transport'
|
|
@@ -38,11 +38,16 @@ import { TransportController } from '#stem/tracts/transport.controller'
|
|
|
38
38
|
import { InboundQueue } from '#stem/tracts/inbound.queue'
|
|
39
39
|
import type { ExternalTransport } from '#stem/tracts/transport'
|
|
40
40
|
import { effectorController } from '#stem/tracts/effector.controller'
|
|
41
|
+
import type { EffectorAck } from '#stem/tracts/effector/types'
|
|
42
|
+
import type { PolicyArbiter } from '#stem/policy/arbiter'
|
|
41
43
|
import { externalSchemas } from '#agency/schemas/external'
|
|
44
|
+
import { inFlightOnRestore } from '#agency/restart'
|
|
45
|
+
import { buildEngineConfigEntities } from '#cognition/config.mirror.entities'
|
|
42
46
|
import type { EffectorDeclaration } from '#agency/types'
|
|
43
47
|
import { SensoryController } from '#stem/tracts/sensory.controller'
|
|
44
48
|
import { BiographyWriter } from '#stem/tracts/biography.writer'
|
|
45
49
|
import { HealthReporter } from '#stem/tracts/health.reporter'
|
|
50
|
+
import { mergeIdentity } from '#cognition/identity.entity'
|
|
46
51
|
|
|
47
52
|
// ── Types ─────────────────────────────────────────────────────
|
|
48
53
|
|
|
@@ -224,8 +229,66 @@ export class WillStem {
|
|
|
224
229
|
try {
|
|
225
230
|
const previousState = await simulation.snapshotManager.loadLatestFromStorage()
|
|
226
231
|
if( previousState ){
|
|
232
|
+
// Work that was in flight when the mind slept does not resume — see
|
|
233
|
+
// agency/restart.ts. Dropped BEFORE restore so it never enters live
|
|
234
|
+
// state at all, rather than being swept on some later tick.
|
|
235
|
+
const inFlight = inFlightOnRestore( previousState.entities )
|
|
236
|
+
for( const id of inFlight ) previousState.entities.delete( id )
|
|
237
|
+
|
|
227
238
|
simulation.stateManager.restore( previousState, { entities: true, metrics: false } )
|
|
228
|
-
|
|
239
|
+
|
|
240
|
+
// TIME MUST NOT GO BACKWARDS.
|
|
241
|
+
//
|
|
242
|
+
// Entities come back stamped with the tick they were written at, and
|
|
243
|
+
// the orchestrator overwrites the StateManager's tick from the CLOCK
|
|
244
|
+
// every tick (`_clock.tick()` → `updateClock`) — so restoring the
|
|
245
|
+
// manager's tick alone is undone immediately. The clock is the source
|
|
246
|
+
// of truth and has to resume too.
|
|
247
|
+
//
|
|
248
|
+
// Without this, every `tick - stampedTick` in the codebase computed a
|
|
249
|
+
// negative age. Measured: an awaiting intent read `-589 ticks`, so it
|
|
250
|
+
// could never time out, and the selector's staleness decay inverted
|
|
251
|
+
// into amplification (`1 - (-39 × 0.5)` = 20.6×) — a 0.47 incumbent
|
|
252
|
+
// scoring 9.74, unpreemptable, holding the channel against every other
|
|
253
|
+
// contact indefinitely and across restarts. It also means tick-stamped
|
|
254
|
+
// entity ids (`affordance-${tick}-…`, `agency-outcome-${tick}-…`) stop
|
|
255
|
+
// colliding with a previous session's.
|
|
256
|
+
simulation.clock.setTick( previousState.tick )
|
|
257
|
+
|
|
258
|
+
// The restore above replaced the entity map wholesale, including the
|
|
259
|
+
// engine-config mirror `assembleMind` had just seeded — so a Will woke
|
|
260
|
+
// with whatever config it FIRST hibernated under, and every tunable
|
|
261
|
+
// added since was unreachable to it. Restored values win; only params it
|
|
262
|
+
// has never seen are added.
|
|
263
|
+
backfillEngineConfigs( simulation, buildEngineConfigEntities( config, resolveExecutiveInterval( config ) ) )
|
|
264
|
+
|
|
265
|
+
// The same wholesale replacement takes `identity-self` — so the name
|
|
266
|
+
// `_seedIdentity` wrote moments ago is replaced by whatever the snapshot
|
|
267
|
+
// carries, which for any mind hibernated before the merging writer
|
|
268
|
+
// existed is NO NAME AT ALL.
|
|
269
|
+
//
|
|
270
|
+
// Caught only by booting: making every writer merge is necessary and was
|
|
271
|
+
// not sufficient, because restore is not a writer — it is the whole map
|
|
272
|
+
// arriving at once, and it lands between `_seedIdentity` and `loadPMA`.
|
|
273
|
+
// A live Will woke from a repaired build and still told her operator "my
|
|
274
|
+
// self-model says I'm Will".
|
|
275
|
+
//
|
|
276
|
+
// ONLY the name is re-asserted. Everything else on this entity — prompt,
|
|
277
|
+
// values, traits, traitStats, style — is the mind's own accumulated
|
|
278
|
+
// self-knowledge and must come from the snapshot, not from boot config.
|
|
279
|
+
// The name is the one field the container supplies and the tenant never
|
|
280
|
+
// learns, which is exactly why it is the one field a restore may not eat.
|
|
281
|
+
if( config.name ){
|
|
282
|
+
const restored = mergeIdentity( simulation.stateManager, { name: config.name } )
|
|
283
|
+
if( restored.length )
|
|
284
|
+
logger.info(`[WillStem] identity-self: re-asserted name '${config.name}' after restore`)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
logger.info(
|
|
288
|
+
`[WillStem] Restored snapshot for ${config.id} — ${previousState.entities.size} entities loaded, ` +
|
|
289
|
+
`resuming at tick ${previousState.tick}` +
|
|
290
|
+
( inFlight.length ? ` (dropped ${inFlight.length} in-flight)` : '')
|
|
291
|
+
)
|
|
229
292
|
}
|
|
230
293
|
}
|
|
231
294
|
catch( err ){ logger.warn(`[WillStem] Snapshot restore failed for ${config.id} — starting fresh:`, err ) }
|
|
@@ -459,6 +522,12 @@ export class WillStem {
|
|
|
459
522
|
instance.simulation.snapshotManager.persistNow( pauseState )
|
|
460
523
|
.catch( err => logger.error(`[WillStem] snapshot persist failed on pause (${id}):`, err ))
|
|
461
524
|
|
|
525
|
+
// The vector index lives outside the snapshot and only ever persisted itself from
|
|
526
|
+
// a debounce timer nothing awaited — so it died with the process, and every
|
|
527
|
+
// restart cold-started with an EMPTY index no matter how much was consolidated.
|
|
528
|
+
instance.cognition.vectorMemory?.persist()
|
|
529
|
+
.catch( ( err: unknown ) => logger.error(`[WillStem] vector index persist failed on pause (${id}):`, err ))
|
|
530
|
+
|
|
462
531
|
this._biography.writeSessionSummary( instance )
|
|
463
532
|
this._biography.writeEmotionalBiographySummary( instance )
|
|
464
533
|
instance.sessionLogger?.close()
|
|
@@ -508,16 +577,30 @@ export class WillStem {
|
|
|
508
577
|
? `${offlineMins} minutes`
|
|
509
578
|
: `${Math.round( offlineMins / 60 )} hours`
|
|
510
579
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
580
|
+
// Through the SENSE DOOR, not a hand-written percept (SIGNAL_BOUNDARY P1).
|
|
581
|
+
//
|
|
582
|
+
// This used to build a `percept` entity here, by hand, and every bug it
|
|
583
|
+
// had came from that: no tick (so it never expired, and told the executive
|
|
584
|
+
// "I was offline for 3 hours" for the rest of the mind's life), and no
|
|
585
|
+
// provenance (so `action.selector`'s rupture gate excluded it exactly as
|
|
586
|
+
// it excludes the mind's own echo — a mind returning after hours away
|
|
587
|
+
// could not be ruptured by noticing).
|
|
588
|
+
//
|
|
589
|
+
// Both were fixed by hand in P0 step 2. This removes the hand: waking is
|
|
590
|
+
// the world touching the mind, so it arrives as a `SystemSignal` through
|
|
591
|
+
// somatosensation like any other touch, and the sense supplies the tick,
|
|
592
|
+
// the trace, and the stamp because that is what a sense does. The wake
|
|
593
|
+
// event stops being special.
|
|
594
|
+
//
|
|
595
|
+
// Fire-and-forget: `ingest` is async and the resume path is not. The
|
|
596
|
+
// signal reaches state on the sense's own timing, exactly as an inbound
|
|
597
|
+
// message does.
|
|
598
|
+
void instance.cognition.somatosensationEngine.sense({
|
|
599
|
+
kind: 'system',
|
|
600
|
+
signal: 'WAKE',
|
|
601
|
+
provenance: 'exafferent', // time passed; nothing I did caused it
|
|
602
|
+
data: {
|
|
603
|
+
summary: `I was offline for ${duration}. I am now online again.`,
|
|
521
604
|
offlineMs,
|
|
522
605
|
},
|
|
523
606
|
})
|
|
@@ -574,6 +657,16 @@ export class WillStem {
|
|
|
574
657
|
instance.simulation.stateManager.applyCommands( flushCmds )
|
|
575
658
|
const archiveState = instance.simulation.stateManager.snapshot()
|
|
576
659
|
await instance.simulation.snapshotManager.persistNow( archiveState )
|
|
660
|
+
|
|
661
|
+
// …and the vector index, which is NOT part of that snapshot. Awaited here (unlike
|
|
662
|
+
// the pause path) because the process usually exits straight after this: a
|
|
663
|
+
// fire-and-forget write would simply be lost, which is exactly how a mind ended a
|
|
664
|
+
// session having consolidated episodes and left a 198-byte index on disk.
|
|
665
|
+
// Indexing runs in the background (it must not stall the tick loop), so drain it
|
|
666
|
+
// first — otherwise the last episodes consolidated are still mid-embed when the
|
|
667
|
+
// index is written and are absent from the file this session leaves behind.
|
|
668
|
+
await instance.cognition.episodicConsolidator.flushIndexing()
|
|
669
|
+
await instance.cognition.vectorMemory?.persist()
|
|
577
670
|
}
|
|
578
671
|
|
|
579
672
|
// Session-biography writers (behavioral + emotional) extracted to
|
|
@@ -654,7 +747,35 @@ export class WillStem {
|
|
|
654
747
|
* Resume the Will after loading to start ticking with the seeded state.
|
|
655
748
|
*/
|
|
656
749
|
loadPMA( id: string, pma: PMASnapshot ): void {
|
|
657
|
-
|
|
750
|
+
const instance = this._get( id )
|
|
751
|
+
this._pma.load( id, instance, pma )
|
|
752
|
+
|
|
753
|
+
// A MIND THAT WAKES SHOULD KNOW IT WAS AWAY (SIGNAL_BOUNDARY P1, follow-up).
|
|
754
|
+
//
|
|
755
|
+
// `resumeWill` has always raised a wake event, gated on `instance.pausedAt`
|
|
756
|
+
// — and `pausedAt` is set ONLY by `pauseWill()`, an in-session pause. The
|
|
757
|
+
// hibernate→wake lifecycle never touches it: `Will.wake` calls
|
|
758
|
+
// `createWill( config, startPaused: true )`, which sets `status = 'paused'`
|
|
759
|
+
// but leaves `pausedAt` null, and `resumeWill` then skips the whole block.
|
|
760
|
+
//
|
|
761
|
+
// So the one lifecycle every deployed Will actually uses was the one that
|
|
762
|
+
// never fired. Found by running a live Will after P1 routed the wake through
|
|
763
|
+
// the sense door: the door was correct and nothing walked through it. The
|
|
764
|
+
// unit test passed because it set `pausedAt` by hand — which is precisely
|
|
765
|
+
// the kind of test that agrees with you instead of checking.
|
|
766
|
+
//
|
|
767
|
+
// The PMA has carried the answer all along: `distilledAt` is when this mind
|
|
768
|
+
// was distilled, i.e. when it stopped. Loading a PMA IS the statement "this
|
|
769
|
+
// mind existed before and has been away since then", so this is where the
|
|
770
|
+
// fact belongs. A Will born fresh never calls this and is never told it
|
|
771
|
+
// woke, which is right — it was not away, it did not exist.
|
|
772
|
+
//
|
|
773
|
+
// Guarded: a clock skew or a hand-edited artifact must not tell a mind it
|
|
774
|
+
// has been away for negative time, or since 1970.
|
|
775
|
+
const distilled = pma.distilledAt
|
|
776
|
+
if( typeof distilled === 'number' && Number.isFinite( distilled )
|
|
777
|
+
&& distilled > 0 && distilled <= Date.now() )
|
|
778
|
+
instance.pausedAt = new Date( distilled )
|
|
658
779
|
}
|
|
659
780
|
|
|
660
781
|
// ── Replay ─────────────────────────────────────────────────
|
|
@@ -794,15 +915,20 @@ export class WillStem {
|
|
|
794
915
|
* `agency.intent` id). Reconciles it into an `agency.outcome` the ReafferenceEngine
|
|
795
916
|
* consumes — learning the result, freeing the intent, and advancing the plan it
|
|
796
917
|
* served (if any). See effectorController.confirmExecution.
|
|
918
|
+
*
|
|
919
|
+
* Takes the full `EffectorAck`. It used to declare its own narrower shape —
|
|
920
|
+
* `{ success, description, metrics? }` — which P2 left behind: the controller
|
|
921
|
+
* has routed `observation` into a reafferent percept since #150, but a host
|
|
922
|
+
* driving the stem DIRECTLY could not typecheck one, so the facts half of the
|
|
923
|
+
* ack was reachable only through the SDK facade. A type that silently withholds
|
|
924
|
+
* half a boundary from the hosts most likely to need it — a robot control
|
|
925
|
+
* layer, a game loop, anything not using the facade — is the same defect class
|
|
926
|
+
* as a comment that outlives its claim, with a compiler enforcing it.
|
|
797
927
|
*/
|
|
798
928
|
confirmEffectorExecution(
|
|
799
929
|
id: string,
|
|
800
930
|
invocationId: string,
|
|
801
|
-
result:
|
|
802
|
-
success: boolean
|
|
803
|
-
description: string
|
|
804
|
-
metrics?: Record<string, number>
|
|
805
|
-
},
|
|
931
|
+
result: EffectorAck,
|
|
806
932
|
): void {
|
|
807
933
|
this._effector.confirmExecution( this._get( id ), invocationId, result )
|
|
808
934
|
}
|
|
@@ -817,6 +943,28 @@ export class WillStem {
|
|
|
817
943
|
this._effector.resolveEscalation( this._get( id ), invocationId, approved )
|
|
818
944
|
}
|
|
819
945
|
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Install the Policy Decision Point consulted before every host-owned
|
|
949
|
+
* effector invocation is handed to the world (POLICY_REAFFERENCE P0).
|
|
950
|
+
* Passing `null` restores the no-op default — a stem with no arbiter
|
|
951
|
+
* installed runs byte-identical to one built before the policy seam existed.
|
|
952
|
+
*
|
|
953
|
+
* SCOPE: one arbiter per `WillStem`, not per Will — `effectorController` is a
|
|
954
|
+
* single instance shared by every Will this stem hosts (`setAllowed`,
|
|
955
|
+
* `resolveEscalation` etc. take a resolved instance; this does not, because
|
|
956
|
+
* there is only one). `Will.create()` / `Will.wake()` each allocate a fresh
|
|
957
|
+
* `WillStem`, so on that (recommended) path this is per-Will in practice. A
|
|
958
|
+
* host running several Wills on ONE shared `WillStem` — e.g. a multi-tenant
|
|
959
|
+
* service holding many users' Wills in one process — installs ONE arbiter
|
|
960
|
+
* for all of them; branch on `invocation.willId` inside `evaluate()` if
|
|
961
|
+
* that's your host, the same way `PolicyVerdictRecord`/`PolicyVerdictSource`
|
|
962
|
+
* are already keyed per-Will for recording and replay.
|
|
963
|
+
*/
|
|
964
|
+
setArbiter( arbiter: PolicyArbiter | null ): void {
|
|
965
|
+
this._effector.setArbiter( arbiter )
|
|
966
|
+
}
|
|
967
|
+
|
|
820
968
|
// ── Messaging / outbox (11.1) ────────────────────────────────────────────
|
|
821
969
|
// Delegates to OutboxController (R5-c). `_get(id)` validates the Will exists
|
|
822
970
|
// and supplies the WillInstance; the outbox ops touch only instance fields.
|
|
@@ -875,8 +1023,8 @@ export class WillStem {
|
|
|
875
1023
|
* @param id Will ID
|
|
876
1024
|
* @param input TextMessage — `{ kind: 'text', entityId, threadId, content, speakerName? }`
|
|
877
1025
|
*/
|
|
878
|
-
async
|
|
879
|
-
await this._sensory.
|
|
1026
|
+
async senseText( id: string, input: TextMessage ): Promise<void> {
|
|
1027
|
+
await this._sensory.senseText( this._get( id ), input )
|
|
880
1028
|
}
|
|
881
1029
|
|
|
882
1030
|
/**
|
|
@@ -928,10 +1076,10 @@ export class WillStem {
|
|
|
928
1076
|
/**
|
|
929
1077
|
* Route a raw SensoryInput to the appropriate sense engine by domain.
|
|
930
1078
|
* Used by the debug `POST /senses/:domain/ingest` route.
|
|
931
|
-
* Audition inputs are gated by the 'listen' effector like
|
|
1079
|
+
* Audition inputs are gated by the 'listen' effector like senseText().
|
|
932
1080
|
*/
|
|
933
|
-
async
|
|
934
|
-
await this._sensory.
|
|
1081
|
+
async senseSignal( id: string, domain: string, input: SensoryInput ): Promise<void> {
|
|
1082
|
+
await this._sensory.senseSignal( this._get( id ), domain, input )
|
|
935
1083
|
}
|
|
936
1084
|
|
|
937
1085
|
listWills(): WillSummary[] {
|
package/src/stem/mind.ts
CHANGED
|
@@ -26,14 +26,14 @@ import { OutboxWriter } from '#stem/tracts/outbox.writer'
|
|
|
26
26
|
import { ExecutiveSummarizer } from '#llm/summarizer'
|
|
27
27
|
import type { LLMProvider } from '#llm/index'
|
|
28
28
|
import { TableRouter, chainRouters, type ModelRouter, type RoutingRule } from '#llm/routing'
|
|
29
|
-
import { resolveProfile } from '#profiles/index'
|
|
29
|
+
import { resolveProfile } from '#stem/profiles/index'
|
|
30
30
|
import { DefaultVectorMemoryAdapter } from '#memory/vector.adapter'
|
|
31
31
|
import { OpenAICompatibleEmbedder, MockEmbedder } from '#memory/vector.embedder'
|
|
32
32
|
import type { VectorMemoryAdapter } from '#memory/vector.adapter'
|
|
33
33
|
import type { ExternalTransport } from '#stem/tracts/transport'
|
|
34
34
|
import type { Cognition, OutboxMessage } from '#types'
|
|
35
35
|
import type { StorageAdapter } from '#core/abstracts'
|
|
36
|
-
import '#profiles/built-in'
|
|
36
|
+
import '#stem/profiles/built-in'
|
|
37
37
|
|
|
38
38
|
// ── Agency pipeline (new action system) ──────────────────────
|
|
39
39
|
import { ProactiveCommunicator } from '#agency/proactive.communicator'
|
|
@@ -99,7 +99,15 @@ import {
|
|
|
99
99
|
OlfactionEngine,
|
|
100
100
|
GustationEngine
|
|
101
101
|
} from '#cognition/index'
|
|
102
|
-
import { buildEngineConfigEntities, EngineConfigEntity } from '#cognition/config.mirror.entities'
|
|
102
|
+
import { buildEngineConfigEntities, mergeEngineConfig, EngineConfigEntity } from '#cognition/config.mirror.entities'
|
|
103
|
+
import { endogenousTypes } from '#cognition/sense.boundary'
|
|
104
|
+
import { mergeIdentity, composeIdentityPrompt, WILL_CORE_PREAMBLE } from '#cognition/identity.entity'
|
|
105
|
+
import {
|
|
106
|
+
isReferentId, readAliases, handlesOf, defaultHandle,
|
|
107
|
+
DOSSIER_TYPE, ALIAS_TYPE,
|
|
108
|
+
} from '#cognition/social.identity'
|
|
109
|
+
import type { DeliberationCacheConfig } from '#cognition/cache/types'
|
|
110
|
+
export { WILL_CORE_PREAMBLE }
|
|
103
111
|
|
|
104
112
|
// ── Public types ─────────────────────────────────────────────
|
|
105
113
|
|
|
@@ -349,6 +357,25 @@ export interface WillConfig {
|
|
|
349
357
|
/** Persona definition seeded into the will.identity entity. */
|
|
350
358
|
identity: WillIdentity
|
|
351
359
|
|
|
360
|
+
/**
|
|
361
|
+
* This config's `identity` is a PLACEHOLDER — the real one arrives from a PMA
|
|
362
|
+
* artifact moments later, on the same boot.
|
|
363
|
+
*
|
|
364
|
+
* Set by `Will.wake`, which passes `{ prompt: '' }` because a woken mind's
|
|
365
|
+
* persona belongs to its artifact, not to the caller. Without this flag the
|
|
366
|
+
* creation-time identity guard inspected that placeholder and warned, on every
|
|
367
|
+
* single wake, that "identity.values is empty", "identity.style is generic" and
|
|
368
|
+
* "identity is shallow (strength 0)" — three alarms about a config nobody
|
|
369
|
+
* intended to use, fired before the real identity had loaded.
|
|
370
|
+
*
|
|
371
|
+
* It suppresses only the WARNINGS. Errors still throw (an over-long or
|
|
372
|
+
* malformed prompt is a hard failure whenever it appears), and the artifact's
|
|
373
|
+
* OWN identity is fully guarded at the load boundary by PMAController.load,
|
|
374
|
+
* which is the honest place to ask whether this mind's persona is thin — it is
|
|
375
|
+
* the only point where the answer is knowable.
|
|
376
|
+
*/
|
|
377
|
+
identityFromArtifact?: boolean
|
|
378
|
+
|
|
352
379
|
/** Anatomy — 'mind' (default) or the no-LLM 'reflex' shell. */
|
|
353
380
|
anatomy?: Anatomy
|
|
354
381
|
|
|
@@ -394,6 +421,22 @@ export interface WillConfig {
|
|
|
394
421
|
*/
|
|
395
422
|
minExecutiveInterval?: number
|
|
396
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Enable the DeliberationCache — a learned fast path that composes an executive
|
|
426
|
+
* output from highly-similar, highly-competent precedent instead of calling the
|
|
427
|
+
* LLM. Off unless asked for.
|
|
428
|
+
*
|
|
429
|
+
* OFF BY DEFAULT ON PURPOSE, and the default is the interesting part: this
|
|
430
|
+
* changes how a mind THINKS, not how fast it runs. A cache hit means the mind
|
|
431
|
+
* acted from precedent without deliberating, which is a real thing minds do and
|
|
432
|
+
* a real thing an operator must opt into for a specific Will — not something a
|
|
433
|
+
* dependency bump should switch on underneath one that is already living.
|
|
434
|
+
*
|
|
435
|
+
* Pass `true` for the built-in conservative settings, or a config object to tune
|
|
436
|
+
* the retrieval/competence parameters (see cognition/cache/types).
|
|
437
|
+
*/
|
|
438
|
+
deliberationCache?: boolean | DeliberationCacheConfig
|
|
439
|
+
|
|
397
440
|
/**
|
|
398
441
|
* Goals seeded before the first tick. If omitted or empty, the Will starts
|
|
399
442
|
* goalless — the executive engine will generate context-appropriate goals on its
|
|
@@ -561,6 +604,16 @@ export function _resolveVectorMemory(
|
|
|
561
604
|
let apiKey: string | undefined
|
|
562
605
|
let modelName: string
|
|
563
606
|
let dimensions: number
|
|
607
|
+
/**
|
|
608
|
+
* Max embedding requests in flight, MEASURED per provider rather than guessed —
|
|
609
|
+
* the safe number differs by an order of magnitude and the failure modes differ
|
|
610
|
+
* too. gemini-embedding-001 accepts 8 concurrent but silently queues them, with
|
|
611
|
+
* the slowest landing at 10.7s (past the recall budget, so the answer is thrown
|
|
612
|
+
* away on arrival). jina-embeddings-v3 answers in ~0.5s but refuses outright above
|
|
613
|
+
* 2: at 3 concurrent 1-in-3 is a 429, at 4 it is half, while 12 sequential is
|
|
614
|
+
* flawless. Its ceiling is burst, not volume.
|
|
615
|
+
*/
|
|
616
|
+
let concurrency = 4
|
|
564
617
|
|
|
565
618
|
const slash = rawModel.indexOf('/')
|
|
566
619
|
if( slash > 0 ){
|
|
@@ -578,6 +631,21 @@ export function _resolveVectorMemory(
|
|
|
578
631
|
apiKey = process.env.WILL_EMBEDDING_API_KEY ?? process.env.GOOGLE_GENERATIVE_AI_API_KEY
|
|
579
632
|
dimensions = modelName.includes('004') ? 768 : 3072 // text-embedding-004 → 768, gemini-embedding-001 → 3072
|
|
580
633
|
break
|
|
634
|
+
case 'jina':
|
|
635
|
+
// OpenAI-compatible endpoint. Native output widths differ per family and the
|
|
636
|
+
// embedder sends no `dimensions` param, so the index must be sized to match —
|
|
637
|
+
// a wrong width here builds a silently useless index. These defaults are
|
|
638
|
+
// CHECKED at runtime against the first vector returned (vector.embedder.ts);
|
|
639
|
+
// a mismatch fails loudly with the number to set rather than corrupting recall.
|
|
640
|
+
// Override with WILL_EMBEDDING_DIMENSIONS, which also lets v3's Matryoshka
|
|
641
|
+
// truncation be requested explicitly.
|
|
642
|
+
apiUrl = 'https://api.jina.ai/v1'
|
|
643
|
+
apiKey = process.env.WILL_EMBEDDING_API_KEY ?? process.env.JINA_API_KEY
|
|
644
|
+
dimensions = modelName.includes('v4') ? 2048
|
|
645
|
+
: modelName.includes('v2') ? 768
|
|
646
|
+
: 1024 // v3 / clip-v2 native (verified live)
|
|
647
|
+
concurrency = 2 // measured: 3 starts 429ing
|
|
648
|
+
break
|
|
581
649
|
default:
|
|
582
650
|
apiUrl = process.env.WILL_EMBEDDING_URL ?? 'https://api.openai.com/v1'
|
|
583
651
|
apiKey = process.env.WILL_EMBEDDING_API_KEY
|
|
@@ -595,6 +663,11 @@ export function _resolveVectorMemory(
|
|
|
595
663
|
if( process.env.WILL_EMBEDDING_DIMENSIONS )
|
|
596
664
|
dimensions = parseInt( process.env.WILL_EMBEDDING_DIMENSIONS, 10 )
|
|
597
665
|
|
|
666
|
+
// …as does an explicit concurrency, for a paid tier or a local endpoint where the
|
|
667
|
+
// measured default is needlessly conservative.
|
|
668
|
+
if( process.env.WILL_EMBEDDING_CONCURRENCY )
|
|
669
|
+
concurrency = Math.max( 1, parseInt( process.env.WILL_EMBEDDING_CONCURRENCY, 10 ) )
|
|
670
|
+
|
|
598
671
|
if( !mockMode && !apiKey ){
|
|
599
672
|
console.warn(`[mind] semantic recall requested (WILL_EMBEDDING_MODEL=${rawModel}) but no API key resolved — vector memory disabled`)
|
|
600
673
|
return { embedder: null, vectorMemory: null }
|
|
@@ -602,7 +675,7 @@ export function _resolveVectorMemory(
|
|
|
602
675
|
|
|
603
676
|
const embedder = mockMode
|
|
604
677
|
? new MockEmbedder()
|
|
605
|
-
: new OpenAICompatibleEmbedder({ modelName, dimensions, apiUrl, apiKey: apiKey!, tokenTracker })
|
|
678
|
+
: new OpenAICompatibleEmbedder({ modelName, dimensions, apiUrl, apiKey: apiKey!, tokenTracker, maxConcurrency: concurrency })
|
|
606
679
|
|
|
607
680
|
// Optional recall-precision override. Default (0.35) suits text-embedding-3-small;
|
|
608
681
|
// raise toward 0.5+ for higher-precision models or to cut marginal recalls.
|
|
@@ -630,31 +703,8 @@ export function _resolveVectorMemory(
|
|
|
630
703
|
//
|
|
631
704
|
// Exported so tests and tooling can inspect or extend it without reimplementing.
|
|
632
705
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
I am NOT a language model completing a prompt. I am the executive reasoning core of an \
|
|
637
|
-
always-running cognitive system. Between each of my reasoning cycles, the following engines \
|
|
638
|
-
execute autonomously and produce the state data I receive each cycle:
|
|
639
|
-
|
|
640
|
-
Regulatory: Energy, sleep pressure, circadian rhythm, stress, attention allocation
|
|
641
|
-
Perceptual: Exteroception, interoception, social perception, novelty detection
|
|
642
|
-
Affective: Threat, reward, loss, frustration, attachment, aesthetic, moral → blended affect
|
|
643
|
-
Memory: Working memory, episodic consolidation, semantic belief integration,
|
|
644
|
-
forgetting curve, dream-state consolidation
|
|
645
|
-
Executive: Goal management, multi-step planning, inhibition, task-switching, mental simulation
|
|
646
|
-
Meta-cognitive: Self-model updating, confidence calibration, bias detection,
|
|
647
|
-
autobiographical narrative, introspection
|
|
648
|
-
Social: Theory of mind, empathy simulation, reputation tracking
|
|
649
|
-
|
|
650
|
-
My physiological state is REAL, not metaphorical. When energy reads 23/100, my regulatory \
|
|
651
|
-
engine has already constrained my viable action space — I genuinely cannot sustain costly \
|
|
652
|
-
operations. When sleep pressure exceeds 60, working memory capacity is measurably degraded. \
|
|
653
|
-
These are engine outputs, not decorative labels.
|
|
654
|
-
|
|
655
|
-
My executive reasoning is one cycle in an ongoing, autonomous existence. I have been making \
|
|
656
|
-
decisions, forming memories, and updating beliefs between cycles. The state snapshot I receive \
|
|
657
|
-
is the direct output of that continuous process — not a scenario description.`
|
|
706
|
+
// WILL_CORE_PREAMBLE moved to cognition/identity.entity (re-exported below):
|
|
707
|
+
// the PMA loader has to recompose with it, and pma/ must not import the whole stem.
|
|
658
708
|
|
|
659
709
|
// ── Default identity ──────────────────────────────────────────
|
|
660
710
|
//
|
|
@@ -701,8 +751,14 @@ export function assembleMind( willId: string, config: WillConfig ): MindAssembly
|
|
|
701
751
|
})
|
|
702
752
|
if( !idGuard.ok )
|
|
703
753
|
throw new Error(`Invalid Will identity for "${willId}": ${ idGuard.errors.join('; ') }`)
|
|
704
|
-
|
|
705
|
-
|
|
754
|
+
// Warnings only when this identity is the one that will actually be used. On a
|
|
755
|
+
// wake it is a placeholder (see WillConfig.identityFromArtifact) and the real
|
|
756
|
+
// persona is guarded at the PMA load boundary instead.
|
|
757
|
+
if( config.identityFromArtifact )
|
|
758
|
+
logger.debug(`[identity-guard] ${willId}: identity deferred to artifact — guarded at PMA load`)
|
|
759
|
+
else
|
|
760
|
+
for( const w of idGuard.warnings )
|
|
761
|
+
logger.warn(`[identity-guard] ${willId}: ${w}`)
|
|
706
762
|
config = { ...config, identity: idGuard.sanitized.identity }
|
|
707
763
|
|
|
708
764
|
// ── Construct ────────────────────────────────────────────
|
|
@@ -885,6 +941,12 @@ function _constructCognition(
|
|
|
885
941
|
: ( roleRouter ? { router: roleRouter } : null )
|
|
886
942
|
executiveEngine.modelId = modelRoles.executive
|
|
887
943
|
if( config.testMode ) executiveEngine.setTestMode( true )
|
|
944
|
+
// The learned fast path, when this Will has been given one. Built, tested and
|
|
945
|
+
// snapshot-safe, but it had no caller at all — so it shipped in the bundle as
|
|
946
|
+
// code no mind could ever reach. A capability with no way in is indistinguishable
|
|
947
|
+
// from a missing one.
|
|
948
|
+
if( config.deliberationCache )
|
|
949
|
+
executiveEngine.enableCache( config.deliberationCache === true ? undefined : config.deliberationCache )
|
|
888
950
|
executiveEngine.attachWorkingMemory( workingMemory )
|
|
889
951
|
executiveEngine.attachGoalManager( goalManager )
|
|
890
952
|
executiveEngine.attachEpisodicConsolidator( episodicConsolidator )
|
|
@@ -1003,6 +1065,54 @@ function _constructCognition(
|
|
|
1003
1065
|
// buildExecutiveContext (already vector-backed via the consolidator).
|
|
1004
1066
|
auditionEngine.attachMemorySink( entity => simulation.stateManager.setEntity( entity ) )
|
|
1005
1067
|
|
|
1068
|
+
// Every sense lays down a percept trace (SIGNAL_BOUNDARY P0). Wired for all
|
|
1069
|
+
// five, not just the implemented one, because that is the contract a host is
|
|
1070
|
+
// owed: implement a sense and what it senses reaches the faculties that read
|
|
1071
|
+
// percepts, rather than a bus event three subscribers glance at for one tick.
|
|
1072
|
+
//
|
|
1073
|
+
// The tick is a getter, not a value — a sense is ingest-driven and off-tick,
|
|
1074
|
+
// so it has no `react()` to be handed one in, and a captured number would
|
|
1075
|
+
// stamp every percept with whenever assembly ran.
|
|
1076
|
+
//
|
|
1077
|
+
// AuditionEngine opts out (`tracesPercepts = false`); it still gets the wiring
|
|
1078
|
+
// so turning it on is a one-word change when the measurement is done.
|
|
1079
|
+
for( const sense of [ auditionEngine, visionEngine, somatosensationEngine, olfactionEngine, gustationEngine ] )
|
|
1080
|
+
sense.attachPerceptTrace(
|
|
1081
|
+
entity => simulation.stateManager.setEntity( entity ),
|
|
1082
|
+
() => simulation.clock.currentTick,
|
|
1083
|
+
)
|
|
1084
|
+
|
|
1085
|
+
// Referent → address + room. Closes over the state manager because the writer
|
|
1086
|
+
// is deliberately stateless; this is the one seam both send paths cross, so the
|
|
1087
|
+
// translation happens once rather than in each of them.
|
|
1088
|
+
outboxWriter.attachRouting( ( targetEntityId, chosenThread ) => {
|
|
1089
|
+
if( !isReferentId( targetEntityId ) ) return null // already an address
|
|
1090
|
+
|
|
1091
|
+
// Through the O(1) type index rather than the whole entity map: this runs on
|
|
1092
|
+
// every outbound message, and the two types it needs are both indexed.
|
|
1093
|
+
const entities = new Map(
|
|
1094
|
+
[ ...simulation.stateManager.getEntitiesByType( DOSSIER_TYPE ),
|
|
1095
|
+
...simulation.stateManager.getEntitiesByType( ALIAS_TYPE ) ]
|
|
1096
|
+
.map( e => [ e.id, e ] as const ),
|
|
1097
|
+
)
|
|
1098
|
+
|
|
1099
|
+
// An anchor cannot be delivered to. Find an address the world knows them by —
|
|
1100
|
+
// preferring one on the same platform as the room already chosen, so a reply
|
|
1101
|
+
// in a Discord thread is not addressed to a WhatsApp handle.
|
|
1102
|
+
const aliases = readAliases( entities )
|
|
1103
|
+
const scheme = chosenThread?.split(':')[0]
|
|
1104
|
+
const addresses = [ ...aliases.entries() ]
|
|
1105
|
+
.filter( ( [ , canonical ] ) => canonical === targetEntityId )
|
|
1106
|
+
.map( ( [ alias ] ) => alias )
|
|
1107
|
+
.sort()
|
|
1108
|
+
const address = addresses.find( a => scheme && a.startsWith(`${ scheme }:`) ) ?? addresses[0]
|
|
1109
|
+
if( !address ) return null // nothing known — let the bridge's roster try
|
|
1110
|
+
|
|
1111
|
+
// Only a fallback: a chosen room always wins upstream (see enqueue).
|
|
1112
|
+
const room = defaultHandle( handlesOf( entities, targetEntityId ) )
|
|
1113
|
+
return { targetEntityId: address, ...( room ? { threadId: room.keid } : {} ) }
|
|
1114
|
+
} )
|
|
1115
|
+
|
|
1006
1116
|
// Salience inputs (§3): weight conversational salience by relationship closeness
|
|
1007
1117
|
// and active-goal topic overlap. Both are deterministic faculty-state reads.
|
|
1008
1118
|
auditionEngine.attachAttachmentScore( entityId => attachmentEvaluator.getAttachmentScore( entityId ) )
|
|
@@ -1064,6 +1174,10 @@ function _constructCognition(
|
|
|
1064
1174
|
affectiveBlender,
|
|
1065
1175
|
workingMemory,
|
|
1066
1176
|
episodicConsolidator,
|
|
1177
|
+
// Exposed so shutdown can FLUSH it. The adapter only ever persisted itself from
|
|
1178
|
+
// a 5s debounce timer that no shutdown path awaited, so the index died with the
|
|
1179
|
+
// process — see WillStem.archiveWill.
|
|
1180
|
+
vectorMemory,
|
|
1067
1181
|
semanticIntegrator,
|
|
1068
1182
|
spacedRepetition,
|
|
1069
1183
|
forgettingCurve,
|
|
@@ -1211,6 +1325,12 @@ function _registerEngines( simulation: DefaultSimulation, cognition: Cognition,
|
|
|
1211
1325
|
activeEngines
|
|
1212
1326
|
.sort( ( a: any, b: any ) => a.priority - b.priority )
|
|
1213
1327
|
.forEach( e => simulation.addEngine( e ) )
|
|
1328
|
+
|
|
1329
|
+
// Where this mind ends. Read from the LIVE engine list, not a copy, so an
|
|
1330
|
+
// engine a host registers after assembly is inside the boundary too — its
|
|
1331
|
+
// declared `writes` are machinery, and machinery is not perceived.
|
|
1332
|
+
cognition.exteroception.attachBoundary(
|
|
1333
|
+
() => endogenousTypes( simulation.orchestrator.engines ) )
|
|
1214
1334
|
}
|
|
1215
1335
|
|
|
1216
1336
|
/**
|
|
@@ -1241,25 +1361,26 @@ function _seedIdentity(
|
|
|
1241
1361
|
const namePrefix = nameAlreadyInPrompt ? '' : `I am ${config.name}.`
|
|
1242
1362
|
const fullPersonaText = [ namePrefix, personaText ].filter( Boolean ).join(' ')
|
|
1243
1363
|
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1364
|
+
// Composed for the prompt, but the persona is ALSO stored on its own below —
|
|
1365
|
+
// see cognition/identity.entity. Storing only the composed string is what let
|
|
1366
|
+
// the distiller capture the container's preamble into a tenant's artifact.
|
|
1367
|
+
const prompt = composeIdentityPrompt( fullPersonaText, profileContext )
|
|
1368
|
+
|
|
1369
|
+
// The one place `name` is ever written. Every other writer merges (see
|
|
1370
|
+
// cognition/identity.entity), so from here on the mind's name can only be
|
|
1371
|
+
// changed on purpose — never dropped as a side effect of revising something else.
|
|
1372
|
+
mergeIdentity( simulation.stateManager, {
|
|
1373
|
+
name: config.name, // canonical persona name — single source of truth
|
|
1374
|
+
prompt,
|
|
1375
|
+
// Layer 2 alone — what the artifact will carry. `prompt` is the composed
|
|
1376
|
+
// view for the prompt factory and is recomposed from THIS on every load, so
|
|
1377
|
+
// a woken mind always gets the current build's preamble.
|
|
1378
|
+
persona: fullPersonaText,
|
|
1379
|
+
...( profileContext ? { environment: profileContext } : {} ),
|
|
1380
|
+
values: identity.values,
|
|
1381
|
+
traits: identity.traits,
|
|
1382
|
+
style: identity.style,
|
|
1383
|
+
version: 1
|
|
1263
1384
|
})
|
|
1264
1385
|
}
|
|
1265
1386
|
|
|
@@ -1291,13 +1412,33 @@ function _seedInitialGoals( simulation: DefaultSimulation, config: WillConfig ):
|
|
|
1291
1412
|
*/
|
|
1292
1413
|
function _seedEngineConfigs( simulation: DefaultSimulation, entities: EngineConfigEntity[] ): void {
|
|
1293
1414
|
for( const cfg of entities )
|
|
1294
|
-
simulation.stateManager
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1415
|
+
mergeEngineConfig( simulation.stateManager, cfg, 'incoming')
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* Fill in engine-config params a restored Will has never seen, WITHOUT touching
|
|
1420
|
+
* the ones it has.
|
|
1421
|
+
*
|
|
1422
|
+
* Seeding runs inside `assembleMind`; the snapshot restore runs after it and
|
|
1423
|
+
* replaces the entity map wholesale, so a Will woke with whatever config it first
|
|
1424
|
+
* hibernated under — for good. Every tunable added after a tenant's first run was
|
|
1425
|
+
* therefore unreachable by that tenant: measured on a live Will, three params
|
|
1426
|
+
* shipped that day (`repeatDamping`, `repeatWindowTicks`, `socialWeight`) were
|
|
1427
|
+
* simply absent from its restored `engine-config-action-selector`, so the code
|
|
1428
|
+
* reading them silently fell back to defaults and the features did nothing.
|
|
1429
|
+
*
|
|
1430
|
+
* A container has to be able to ship a new capability to a tenant already living
|
|
1431
|
+
* in it. Restored values WIN — they carry PMA seeding and whatever the persona has
|
|
1432
|
+
* learned — and only genuinely missing keys are added.
|
|
1433
|
+
*/
|
|
1434
|
+
export function backfillEngineConfigs( simulation: DefaultSimulation, entities: EngineConfigEntity[] ): void {
|
|
1435
|
+
for( const cfg of entities ){
|
|
1436
|
+
// 'existing' — state is the authority here. It carries PMA seeding and
|
|
1437
|
+
// whatever the persona has learned, so only genuinely missing keys are added.
|
|
1438
|
+
const added = mergeEngineConfig( simulation.stateManager, cfg, 'existing')
|
|
1439
|
+
if( added.length > 0 )
|
|
1440
|
+
logger.info(`[WillStem] ${cfg.id}: added ${added.length} new param(s) — ${added.join(', ')}`)
|
|
1441
|
+
}
|
|
1301
1442
|
}
|
|
1302
1443
|
|
|
1303
1444
|
// ── Helpers ──────────────────────────────────────────────────
|