@mindot/will 0.9.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 +16660 -15206
- package/dist/index.js.map +1 -1
- package/dist/{channels → surface/channels}/discord.d.ts +50 -3
- package/dist/{channels → surface/channels}/discord.js +102 -7
- 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 +7 -4
- package/dist/surface/channels/whatsapp.js.map +1 -0
- package/dist/{cli.js → surface/cli.js} +4406 -2904
- 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-DbDj_TEH.d.ts → will-evj9_vrd.d.ts} +5699 -4854
- package/package.json +11 -12
- package/src/cognition/agency/consequence.ts +237 -3
- package/src/cognition/agency/conversation.aim.ts +32 -0
- package/src/cognition/agency/engines/action.selector.ts +59 -3
- package/src/cognition/agency/engines/affordance.synthesizer.ts +114 -10
- package/src/cognition/agency/engines/deliberation.engine.ts +76 -3
- package/src/cognition/agency/engines/motor.schema.executor.ts +362 -27
- package/src/cognition/agency/engines/reafference.engine.ts +43 -4
- package/src/cognition/agency/execution.primitives.ts +17 -4
- package/src/cognition/agency/reconcile.learning.ts +5 -4
- 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 +16 -0
- package/src/cognition/agency/settlement.ts +203 -0
- package/src/cognition/agency/types.ts +38 -1
- package/src/cognition/config.mirror.entities.ts +0 -1
- package/src/cognition/event.schemas.ts +0 -8
- package/src/cognition/faculties/affective.blender.ts +21 -4
- package/src/cognition/faculties/circadian.oscillator.ts +36 -7
- package/src/cognition/faculties/executive.engine/action.record.ts +129 -0
- package/src/cognition/faculties/executive.engine/context.ts +41 -35
- package/src/cognition/faculties/executive.engine/engine.ts +270 -113
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +126 -59
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +10 -0
- package/src/cognition/faculties/executive.engine/parser.ts +100 -38
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +168 -23
- package/src/cognition/faculties/executive.engine/types.ts +18 -15
- package/src/cognition/faculties/exteroception.ts +99 -98
- package/src/cognition/faculties/goal.manager.ts +50 -2
- package/src/cognition/faculties/known.entity.tracker.ts +22 -3
- 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/semantic.engine/integrator.ts +1 -1
- package/src/cognition/faculties/working.memory.ts +6 -1
- package/src/cognition/instruction.handler.ts +1 -1
- package/src/cognition/memory/vector.embedder.ts +1 -3
- package/src/cognition/percept.entity.ts +122 -0
- package/src/cognition/sense.boundary.ts +176 -0
- package/src/cognition/senses/audition.engine/engine.ts +97 -16
- package/src/cognition/senses/base.sense.engine.ts +104 -6
- package/src/cognition/senses/index.ts +71 -13
- package/src/cognition/senses/provenance.ts +128 -0
- package/src/cognition/senses/somatosensation.engine.ts +117 -9
- package/src/cognition/social.identity.ts +23 -0
- package/src/cognition/utilities/token.tracker.ts +36 -5
- package/src/core/orchestrator.ts +13 -0
- package/src/index.ts +39 -5
- package/src/llm/gate.ts +48 -0
- package/src/llm/index.ts +102 -25
- package/src/llm/wire.contracts.ts +38 -0
- package/src/pma/index.ts +1 -1
- package/src/stem/index.ts +92 -21
- package/src/stem/mind.ts +26 -2
- 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/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/{channels → surface/channels}/discord.ts +224 -7
- package/src/{channels → surface/channels}/roster.ts +1 -1
- package/src/{channels → surface/channels}/types.ts +1 -1
- package/src/{channels → surface/channels}/whatsapp.ts +9 -6
- package/src/{cli.ts → surface/cli.ts} +6 -6
- 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 +156 -28
- package/src/{serve → surface/serve}/server.ts +11 -4
- package/src/types.ts +24 -3
- 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/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
|
@@ -116,6 +116,100 @@ export function traitEmphasis( value: number ): TraitEmphasis | null {
|
|
|
116
116
|
// low" yet "above my norm" (low overall, but high for me lately).
|
|
117
117
|
const TRAIT_NORM_BAND = 0.12 // deviation from personal baseline to read as above/below my norm
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* One percept, as the mind reads it: the engine's LABEL, and beneath it the
|
|
121
|
+
* EVIDENCE the host actually sent.
|
|
122
|
+
*
|
|
123
|
+
* The whole line is one unit on purpose. Rendering the label and the data
|
|
124
|
+
* separately let a mutation that dropped the data from the call site pass every
|
|
125
|
+
* test — the data renderer stayed correct while the prompt stopped showing it,
|
|
126
|
+
* which is exactly the state this fix exists to leave behind. Same lesson as
|
|
127
|
+
* `temporalLine`.
|
|
128
|
+
*/
|
|
129
|
+
export function perceptLine(
|
|
130
|
+
p: { category: string; summary: string; salience: number; data?: unknown },
|
|
131
|
+
): string {
|
|
132
|
+
return `- [${ p.category }] ${ p.summary } (salience: ${ p.salience.toFixed( 2 ) })${ perceptData( p.data ) }`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* One working-memory item, as the mind reads it — label, then evidence.
|
|
137
|
+
*
|
|
138
|
+
* A sibling of `perceptLine` and a unit for the same reason: rendering the data
|
|
139
|
+
* inline let a mutation that dropped it pass every test. A percept entity is
|
|
140
|
+
* swept after 2 ticks and the executive fires on its own schedule, so MEMORY is
|
|
141
|
+
* often where a mind actually meets an observation — dropping the evidence here
|
|
142
|
+
* loses it just as completely as never storing it, one step later.
|
|
143
|
+
*/
|
|
144
|
+
export function ruminationLine(
|
|
145
|
+
w: { type: string; summary: string; activation: number; data?: unknown },
|
|
146
|
+
): string {
|
|
147
|
+
return `- [${ w.type }] ${ w.summary } (activation: ${ w.activation.toFixed( 2 ) })${ perceptData( w.data ) }`
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A percept's own data, rendered under its label — what the host actually sent.
|
|
152
|
+
*
|
|
153
|
+
* The label is the engine's words about the signal; this is the evidence. A mind
|
|
154
|
+
* that only ever sees labels is being handed conclusions, and the whole job of a
|
|
155
|
+
* mind is to make meaning by connecting pieces of information it can see.
|
|
156
|
+
*
|
|
157
|
+
* Indented on its own line rather than inlined: it can be long, and a host is
|
|
158
|
+
* explicitly not asked to keep it short — what it sent is what it sent.
|
|
159
|
+
*/
|
|
160
|
+
function perceptData( data: unknown ): string {
|
|
161
|
+
if( data === undefined || data === null ) return ''
|
|
162
|
+
if( typeof data === 'string') return data.length > 0 ? `\n ${ data }` : ''
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// A host's own `summary`, when it offered one, is already the label on the
|
|
166
|
+
// line above. Repeating it underneath is noise, and noise in a percept is
|
|
167
|
+
// not free — it is read every tick the percept is alive. NOT reshaping: the
|
|
168
|
+
// stored data keeps every field, this only declines to print one twice.
|
|
169
|
+
const shown = Array.isArray( data )
|
|
170
|
+
? data
|
|
171
|
+
: Object.fromEntries( Object.entries( data as Record<string, unknown> ).filter( ( [ k ] ) => k !== 'summary') )
|
|
172
|
+
const json = JSON.stringify( shown )
|
|
173
|
+
return json === '{}' || json === '[]' ? '' : `\n ${ json }`
|
|
174
|
+
}
|
|
175
|
+
catch { return '' }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* What a phase of the cycle is CALLED, in the words a mind would use.
|
|
180
|
+
*
|
|
181
|
+
* NOT the clock. The hour this is computed from is the oscillator's own — free-
|
|
182
|
+
* running from the tick unless a host entrains it — so it is what the BODY
|
|
183
|
+
* reads, which is a different claim from what time it is. A jet-lagged body
|
|
184
|
+
* says night at noon and is not lying; it is reporting itself.
|
|
185
|
+
*
|
|
186
|
+
* The prompt used to render the raw hour beside this, and the two disagreed on
|
|
187
|
+
* every tick of every Will ever run (`Time: 12.0h (night)`). The hour is gone
|
|
188
|
+
* from the prompt entirely now: it is a fact about the world, and a fact about
|
|
189
|
+
* the world is something a mind goes and gets — see the `check-time` schema.
|
|
190
|
+
*/
|
|
191
|
+
export function temporalLine( timeOfDay: number, circadian: number ): string {
|
|
192
|
+
return `Body rhythm: it feels like ${ labelForHour( timeOfDay ) } to me`
|
|
193
|
+
+ ` (my own cycle, not a clock — I use \`check-time\` to find out the actual hour).`
|
|
194
|
+
+ ` Circadian phase: ${ circadian.toFixed( 2 ) }.`
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The label, from the hour. Kept separate from `temporalLine` only so the bands
|
|
199
|
+
* are readable; nothing outside this file should need it.
|
|
200
|
+
*/
|
|
201
|
+
export function labelForHour( h: number ): string {
|
|
202
|
+
const hour = ( ( h % 24 ) + 24 ) % 24
|
|
203
|
+
return hour < 2 ? 'deep night'
|
|
204
|
+
: hour < 5 ? 'late night'
|
|
205
|
+
: hour < 8 ? 'early morning'
|
|
206
|
+
: hour < 11 ? 'morning'
|
|
207
|
+
: hour < 14 ? 'midday'
|
|
208
|
+
: hour < 17 ? 'afternoon'
|
|
209
|
+
: hour < 21 ? 'evening'
|
|
210
|
+
: 'night'
|
|
211
|
+
}
|
|
212
|
+
|
|
119
213
|
export function normEmphasis( value: number, mean: number ): 'above' | 'below' | null {
|
|
120
214
|
const d = value - mean
|
|
121
215
|
if( d >= TRAIT_NORM_BAND ) return 'above'
|
|
@@ -270,7 +364,15 @@ export interface PromptBuildOptions {
|
|
|
270
364
|
* how one mind ends up opening a second conversation with someone it is already
|
|
271
365
|
* talking to — or telling one person it has contacted another when it has not.
|
|
272
366
|
*/
|
|
273
|
-
activeConversations?: {
|
|
367
|
+
activeConversations?: {
|
|
368
|
+
entityId: string
|
|
369
|
+
name?: string
|
|
370
|
+
sinceTick: number
|
|
371
|
+
/** What the mind worked out in that thread — its own reasoning, come back. */
|
|
372
|
+
concluded?: string
|
|
373
|
+
/** Commitments it made there toward someone NOT in the thread. */
|
|
374
|
+
promised?: Array<{ what: string; target?: string; gist?: string; tick: number }>
|
|
375
|
+
}[]
|
|
274
376
|
}
|
|
275
377
|
|
|
276
378
|
// ── PromptFactory ────────────────────────────────────────────
|
|
@@ -616,16 +718,6 @@ completionType guide:
|
|
|
616
718
|
const timeOfDay = context.worldState.timeOfDay
|
|
617
719
|
const threatLevel = context.worldState.threatLevel
|
|
618
720
|
|
|
619
|
-
// Map raw circadian phase (0–1) to a human-readable label for full temporal awareness.
|
|
620
|
-
// 0 = midnight, 0.5 = noon, 1 = midnight.
|
|
621
|
-
const phaseLabel = circadian < 0.083 ? 'deep night'
|
|
622
|
-
: circadian < 0.208 ? 'late night'
|
|
623
|
-
: circadian < 0.333 ? 'early morning'
|
|
624
|
-
: circadian < 0.458 ? 'morning'
|
|
625
|
-
: circadian < 0.583 ? 'midday'
|
|
626
|
-
: circadian < 0.708 ? 'afternoon'
|
|
627
|
-
: circadian < 0.833 ? 'evening'
|
|
628
|
-
: 'night'
|
|
629
721
|
|
|
630
722
|
const energyGuidance = this._buildEnergyGuidance( energy )
|
|
631
723
|
const stressGuidance = this._buildStressGuidance( stress )
|
|
@@ -676,7 +768,7 @@ completionType guide:
|
|
|
676
768
|
Energy: ${energy.toFixed( 1 )}/100
|
|
677
769
|
Sleep Pressure: ${sleepPressure.toFixed( 1 )}/100
|
|
678
770
|
Stress: ${stress.toFixed( 1 )}/100${threatLine}
|
|
679
|
-
|
|
771
|
+
${temporalLine( timeOfDay, circadian )}
|
|
680
772
|
Cognitive capacity:${capacityNote}
|
|
681
773
|
Epistemic uncertainty: ${( epistemicUncertainty * 100 ).toFixed( 0 )}%${uncertaintyLabel}
|
|
682
774
|
Tick: ${state.tick}
|
|
@@ -721,7 +813,7 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
721
813
|
: ''
|
|
722
814
|
|
|
723
815
|
const perceptsBlock = has('percepts')
|
|
724
|
-
? `## Percepts (What I Notice)\n${context.percepts.slice( 0, 10 ).map(
|
|
816
|
+
? `## Percepts (What I Notice)\n${context.percepts.slice( 0, 10 ).map( perceptLine ).join('\n') || 'Nothing notable'}`
|
|
725
817
|
: ''
|
|
726
818
|
|
|
727
819
|
// Host abilities afforded right now + what each is for. Framed as
|
|
@@ -734,7 +826,7 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
734
826
|
: ''
|
|
735
827
|
|
|
736
828
|
const ruminationsBlock = has('ruminations')
|
|
737
|
-
? `## Active Ruminations (retrieved memories & thoughts)\n${context.workingMemory.map(
|
|
829
|
+
? `## Active Ruminations (retrieved memories & thoughts)\n${context.workingMemory.map( ruminationLine ).join('\n') || 'Nothing actively held in mind'}`
|
|
738
830
|
: ''
|
|
739
831
|
|
|
740
832
|
const memoriesBlock = has('memories')
|
|
@@ -787,10 +879,33 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
787
879
|
// can hold several conversations as one situation rather than as N strangers.
|
|
788
880
|
// Names come from what the mind has actually learned; the id is shown because
|
|
789
881
|
// that is what a reach-out must be addressed to.
|
|
882
|
+
// WHAT I worked out there, not only WHO I am with.
|
|
883
|
+
//
|
|
884
|
+
// `executive.facet.sync` has always carried the facet's full reasoning and the
|
|
885
|
+
// master discarded it, so the return leg of the loop was empty: my thinking
|
|
886
|
+
// went down to a facet as "What I've Been Turning Over" and came back as a
|
|
887
|
+
// name. A facet is this same mind with a focus — reading back what it worked
|
|
888
|
+
// out is not a report from a subordinate, it is remembering where my attention
|
|
889
|
+
// has been. First person throughout, for that reason.
|
|
890
|
+
//
|
|
891
|
+
// A promise made in one thread to someone NOT in it is stated as the plain
|
|
892
|
+
// fact it is. There is deliberately no instruction about it: whether to keep
|
|
893
|
+
// it, drop it, or make a goal of it is mine to decide, and I have a whole
|
|
894
|
+
// faculty for that.
|
|
790
895
|
const conversationsBlock = ( options.mode !== 'facet' && options.activeConversations?.length )
|
|
791
|
-
? `## In Conversation Now\n${options.activeConversations.map( c =>
|
|
792
|
-
`- ${c.name ?? 'someone'} (id: ${c.entityId})`
|
|
793
|
-
|
|
896
|
+
? `## In Conversation Now\n${options.activeConversations.map( c => {
|
|
897
|
+
const who = `- ${c.name ?? 'someone'} (id: ${c.entityId})`
|
|
898
|
+
const concluded = c.concluded ? `\n What I worked out there: ${c.concluded.trim()}` : ''
|
|
899
|
+
const promised = ( c.promised ?? [] ).map( p =>
|
|
900
|
+
`\n I said there that I would ${p.what}${ p.gist ? ` — about: "${p.gist}"` : '' }`
|
|
901
|
+
+ `, at tick ${p.tick}.`
|
|
902
|
+
// Only a CONTACT can be mistaken for already done by having been
|
|
903
|
+
// said, which is the confusion this clause exists to break. A
|
|
904
|
+
// promise about work carries no such ambiguity and gets no lecture.
|
|
905
|
+
+ ( p.target ? ' Saying it in that thread did not send it.' : '')
|
|
906
|
+
).join('')
|
|
907
|
+
return `${who}${concluded}${promised}`
|
|
908
|
+
} ).join('\n')}\nThese threads are already open — I am in them. Reaching out to one of these people again starts a second, parallel thread with them.`
|
|
794
909
|
: ''
|
|
795
910
|
|
|
796
911
|
// Task focus — what the Will is committed to and the felt cost of switching away.
|
|
@@ -997,6 +1112,13 @@ Rest and sleep RESTORE energy. All other actions CONSUME energy. Do not let ener
|
|
|
997
1112
|
*/
|
|
998
1113
|
|
|
999
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* What the mind has been CHOOSING lately — action types, for the variety
|
|
1117
|
+
* check. Not a history of what happened: nothing here says an act landed.
|
|
1118
|
+
* Retitled from `## Recent Actions` at P3 for exactly that reason (see
|
|
1119
|
+
* `_buildRecentOutcomesSection`) — it sat beside a real history under a name
|
|
1120
|
+
* that claimed to be one.
|
|
1121
|
+
*/
|
|
1000
1122
|
private static _buildActionDiversitySection( recentActionTypes: string[] ): string {
|
|
1001
1123
|
if( recentActionTypes.length === 0 ) return ''
|
|
1002
1124
|
|
|
@@ -1006,14 +1128,27 @@ Rest and sleep RESTORE energy. All other actions CONSUME energy. Do not let ener
|
|
|
1006
1128
|
? `\n⚠️ **Action variety alert**: "${recent.filter( t => t === 'reflect' || t === 'observe').join('", "')}" dominated my last ${recent.length} cycles. Choose something DIFFERENT this cycle — e.g. learn, express_emotion, explore, communicate, set_goal, or rest.`
|
|
1007
1129
|
: ''
|
|
1008
1130
|
|
|
1009
|
-
return `##
|
|
1131
|
+
return `## What I Have Been Choosing (last ${recent.length})
|
|
1010
1132
|
${recent.map( ( t, i ) => `${i + 1}. ${t}`).join(' → ')}${warning}
|
|
1011
1133
|
|
|
1012
1134
|
`
|
|
1013
1135
|
}
|
|
1014
1136
|
|
|
1015
1137
|
/**
|
|
1016
|
-
* Render
|
|
1138
|
+
* Render what became of what the mind did — closes the Act→Confirm→Sense loop.
|
|
1139
|
+
*
|
|
1140
|
+
* NAMED FOR WHAT IT RENDERS (SIGNAL_BOUNDARY P3). It was `## Recent Action
|
|
1141
|
+
* Outcomes`, which named the `action.outcome` BUS EVENT — a different shape
|
|
1142
|
+
* this section never touches. It renders `action.record` entities: what became
|
|
1143
|
+
* of acts the mind actually took. Meanwhile the section two above it,
|
|
1144
|
+
* `## Recent Actions`, listed action TYPES THE MIND CHOSE — a list of
|
|
1145
|
+
* intentions wearing the name of a history, directly beside a history.
|
|
1146
|
+
*
|
|
1147
|
+
* A mind that cannot tell those two apart cannot tell an intention from an
|
|
1148
|
+
* act, and one live COO could not: asked "have you completed that?", she said
|
|
1149
|
+
* "Yes — it's done", having posted nothing and having no effectors at all. The
|
|
1150
|
+
* section titles are the mind's own labels for its own memory; they are not
|
|
1151
|
+
* decoration.
|
|
1017
1152
|
* Shows the executive what it tried, whether it landed, and if anything timed out.
|
|
1018
1153
|
* Only rendered when there are status-bearing action records in state.
|
|
1019
1154
|
*/
|
|
@@ -1114,6 +1249,9 @@ ${recent.map( ( t, i ) => `${i + 1}. ${t}`).join(' → ')}${warning}
|
|
|
1114
1249
|
const STATUS_BADGE: Record<string, string> = {
|
|
1115
1250
|
completed: '✓',
|
|
1116
1251
|
failed: '✗',
|
|
1252
|
+
// Not a failure. I formed it and chose not to complete it — reading that
|
|
1253
|
+
// back as ✗ is how a mind learns it is bad at something it decided against.
|
|
1254
|
+
withheld: '⊘ chose not to',
|
|
1117
1255
|
awaiting_host: '⏳',
|
|
1118
1256
|
timed_out: '⏱ TIMED OUT',
|
|
1119
1257
|
}
|
|
@@ -1126,12 +1264,19 @@ ${recent.map( ( t, i ) => `${i + 1}. ${t}`).join(' → ')}${warning}
|
|
|
1126
1264
|
return `- ${badge} **${a.type}** (tick ${a.tick}, ${age} ticks ago${planCtx})${outcome}`
|
|
1127
1265
|
} )
|
|
1128
1266
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1267
|
+
// A note that can actually fire. The one here before keyed on a `timed_out`
|
|
1268
|
+
// status nothing produces — and it sat inside a section that had never
|
|
1269
|
+
// rendered at all, so neither could ever have been seen.
|
|
1270
|
+
const failed = recentActions.filter( a => a.status === 'failed').length
|
|
1271
|
+
const didNotLand = failed > 0
|
|
1272
|
+
? `\n⚠️ **${ failed } of these did not land** — my body attempted them and they did not complete.`
|
|
1132
1273
|
: ''
|
|
1274
|
+
// Invariant, and identical on every branch: this is the sentence that makes
|
|
1275
|
+
// the section load-bearing rather than decorative, and a mind must not get a
|
|
1276
|
+
// differently-worded version of it depending on how its week went.
|
|
1277
|
+
const note = `${ didNotLand }\nThis is what I HAVE done, not what I meant to do. If something I intended is not on this list, it did not happen.`
|
|
1133
1278
|
|
|
1134
|
-
return `##
|
|
1279
|
+
return `## What Became Of What I Did\n${lines.join('\n')}${note}\n\n`
|
|
1135
1280
|
}
|
|
1136
1281
|
|
|
1137
1282
|
/**
|
|
@@ -96,15 +96,6 @@ export interface ExecutiveOutputFull {
|
|
|
96
96
|
* Present ⇒ nothing is sent, whatever else the response contains.
|
|
97
97
|
*/
|
|
98
98
|
noMessage?: string
|
|
99
|
-
/**
|
|
100
|
-
* @deprecated Legacy JSON reply format — no longer emitted by conversation facets.
|
|
101
|
-
* Kept for backward compatibility with any tests/tooling that inspect parsed output.
|
|
102
|
-
*/
|
|
103
|
-
conversationReplies?: Array<{
|
|
104
|
-
targetEntityId: string
|
|
105
|
-
targetEntityName: string
|
|
106
|
-
messages: string[]
|
|
107
|
-
}>
|
|
108
99
|
/**
|
|
109
100
|
* System 2 only — the distinct approaches the master generated and weighed before
|
|
110
101
|
* committing, retained for explainability/auditability (and a future regret /
|
|
@@ -254,8 +245,11 @@ export interface ExecutiveContext {
|
|
|
254
245
|
relevantPlanIds: string[]
|
|
255
246
|
percepts: Array<{
|
|
256
247
|
category: string
|
|
248
|
+
/** The engine's label for the signal. */
|
|
257
249
|
summary: string
|
|
258
250
|
salience: number
|
|
251
|
+
/** What the host actually sent, whole — the evidence under the label. */
|
|
252
|
+
data?: unknown
|
|
259
253
|
}>
|
|
260
254
|
/**
|
|
261
255
|
* Host-declared abilities afforded to the Will *right now* — what it can do in
|
|
@@ -272,8 +266,11 @@ export interface ExecutiveContext {
|
|
|
272
266
|
}>
|
|
273
267
|
workingMemory: Array<{
|
|
274
268
|
type: string
|
|
269
|
+
/** The engine's label for the item. */
|
|
275
270
|
summary: string
|
|
276
271
|
activation: number
|
|
272
|
+
/** What a host sent, whole — carried through memory, not only into it. */
|
|
273
|
+
data?: unknown
|
|
277
274
|
}>
|
|
278
275
|
memories: Array<{
|
|
279
276
|
content: string
|
|
@@ -290,15 +287,21 @@ export interface ExecutiveContext {
|
|
|
290
287
|
/** How many beliefs exist but were not included due to the relevance cap. */
|
|
291
288
|
beliefsOmitted: number
|
|
292
289
|
/**
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
290
|
+
* What became of what it did — the Act→Confirm→Perceive loop, surfaced.
|
|
291
|
+
*
|
|
292
|
+
* Built from `action.record` entities the executive writes from the
|
|
293
|
+
* `action.outcome` / `action.withheld` events it already receives. It used to
|
|
294
|
+
* be built from `decision.record` entities carrying an `actionStatus`, a field
|
|
295
|
+
* read in one place and written in none — so this was empty in every prompt a
|
|
296
|
+
* live mind ever received, and it could see what it had SAID but never what it
|
|
297
|
+
* had DONE. See `action.record.ts`.
|
|
296
298
|
*/
|
|
297
299
|
recentActions: Array<{
|
|
298
300
|
/** Effector name that was invoked */
|
|
299
301
|
type: string
|
|
300
|
-
/**
|
|
301
|
-
|
|
302
|
+
/** How it resolved. `withheld` is distinct from `failed` on purpose: the
|
|
303
|
+
* mind formed the act and chose not to complete it. */
|
|
304
|
+
status: 'completed' | 'failed' | 'withheld'
|
|
302
305
|
/** Tick the action was executed or dispatched */
|
|
303
306
|
tick: number
|
|
304
307
|
/** Short outcome description — truncated to 120 chars */
|
|
@@ -312,7 +315,7 @@ export interface ExecutiveContext {
|
|
|
312
315
|
* The one thing it could never see about itself. `conversation.sent` has been in
|
|
313
316
|
* state since the beginning — 57 records on the Will this was found on — and
|
|
314
317
|
* reached no prompt at all, so the sole evidence of having spoken was a `✓
|
|
315
|
-
* reach-out` line under
|
|
318
|
+
* reach-out` line under What Became Of What I Did: no words, no person, and a tick
|
|
316
319
|
* mark asserting it had worked. That is why the same question went out eleven
|
|
317
320
|
* times in two and a half minutes; from the inside each one was the first.
|
|
318
321
|
*
|
|
@@ -24,18 +24,18 @@ import type {
|
|
|
24
24
|
SimulationContext,
|
|
25
25
|
ReadonlySimulationState,
|
|
26
26
|
StateCommands,
|
|
27
|
-
SimulationEvent,
|
|
28
27
|
SimulationEntity,
|
|
29
28
|
} from '#core/types'
|
|
30
29
|
import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
|
|
31
30
|
import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
32
31
|
import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
32
|
+
import { perceptEntity, PERCEPT_SUMMARY_CAP } from '#cognition/percept.entity'
|
|
33
33
|
import { GenerativeModel } from '#cognition/generative.model'
|
|
34
34
|
import {
|
|
35
|
-
|
|
35
|
+
ATTENUATION, CORRESPONDENCE_ATTENUATION,
|
|
36
36
|
liveConsequences, matchConsequenceText, matchConsequenceEntity,
|
|
37
37
|
} from '#agency/consequence'
|
|
38
|
-
import {
|
|
38
|
+
import { MIND_OWN_ENTITY_TYPES } from '#cognition/sense.boundary'
|
|
39
39
|
|
|
40
40
|
export interface ExteroceptionConfig {
|
|
41
41
|
/** Maximum percepts to produce per tick */
|
|
@@ -43,9 +43,18 @@ export interface ExteroceptionConfig {
|
|
|
43
43
|
/** Default salience for unmarked percepts */
|
|
44
44
|
defaultSalience?: number
|
|
45
45
|
/** Whether to emit percept events */
|
|
46
|
-
emitPerceptEvents?: boolean
|
|
47
46
|
/** Entity types to always treat as high-salience */
|
|
48
47
|
highPriorityTypes?: string[]
|
|
48
|
+
/**
|
|
49
|
+
* Where this mind ends — the entity types its own engines write about its own
|
|
50
|
+
* operation, which it therefore cannot encounter as world events. Live, so a
|
|
51
|
+
* host adding a cognitive engine after assembly is accounted for.
|
|
52
|
+
*
|
|
53
|
+
* Unwired (a bare harness, a unit test) it falls back to the shipped anatomy,
|
|
54
|
+
* which is right for every assembly that adds no cognitive engine of its own.
|
|
55
|
+
* See `#cognition/sense.boundary`.
|
|
56
|
+
*/
|
|
57
|
+
endogenous?: () => ReadonlySet<string>
|
|
49
58
|
bus?: CognitiveBus
|
|
50
59
|
}
|
|
51
60
|
|
|
@@ -63,51 +72,60 @@ interface RawPercept {
|
|
|
63
72
|
valenceSource?: 'entity' | 'ambient'
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
// Skip internal entities — they're not external percepts.
|
|
67
|
-
// Percepts about percepts create a feedback loop that floods
|
|
68
|
-
// working memory and episodic storage with noise.
|
|
69
|
-
const internalTypes = new Set([
|
|
70
|
-
'percept', 'percept.social', 'working_memory.item',
|
|
71
|
-
'interoception', 'attention.focus', 'decision.record',
|
|
72
|
-
'task.focus', 'self_observation',
|
|
73
|
-
'goal', 'belief', 'plan', 'narrative_chapter',
|
|
74
|
-
'introspection', 'self_narrative', 'cognitive_bias',
|
|
75
|
-
'effector.created', 'empathic_state', 'attachment.bond',
|
|
76
|
-
'theory_of_mind', 'reputation', 'episodic_memory',
|
|
77
|
-
// Internal state entities written by our own engines — not external events
|
|
78
|
-
'affect.blends', 'executive.summary',
|
|
79
|
-
// The agency's own forward-model records (EXAFFERENCE P1/P2): perceiving our
|
|
80
|
-
// expected-consequence descriptors would be a self-noise loop.
|
|
81
|
-
CONSEQUENCE_TYPE,
|
|
82
|
-
// The agency's own revocation tombstones (EXAFFERENCE P4) — internal bookkeeping.
|
|
83
|
-
REVOCATION_TYPE,
|
|
84
|
-
])
|
|
85
|
-
|
|
86
75
|
export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
87
76
|
readonly name = 'exteroception'
|
|
88
77
|
|
|
89
78
|
private _maxPerceptsPerTick: number
|
|
90
79
|
private _defaultSalience: number
|
|
91
|
-
private _emitPerceptEvents: boolean
|
|
92
80
|
private _highPriorityTypes: Set<string>
|
|
93
|
-
|
|
81
|
+
/**
|
|
82
|
+
* entityId → what was last seen of it. The TYPE is remembered alongside the
|
|
83
|
+
* version because a removal has to answer the same question an appearance
|
|
84
|
+
* does — "was this mine?" — and by then the entity is gone. It used to be
|
|
85
|
+
* answered by a second, separately-drifting list of id prefixes.
|
|
86
|
+
*/
|
|
87
|
+
private _previousEntityVersions = new Map<string, { at: Timestamp; type: string }>()
|
|
94
88
|
|
|
95
89
|
private _bus: CognitiveBus | null = null
|
|
96
90
|
|
|
97
91
|
private readonly _model = new GenerativeModel()
|
|
98
92
|
|
|
93
|
+
/** Live sense boundary + a memo, so a 50-engine union isn't rebuilt per tick. */
|
|
94
|
+
private _boundary: ( () => ReadonlySet<string> ) | null
|
|
95
|
+
private _endogenousMemo: ReadonlySet<string> = MIND_OWN_ENTITY_TYPES
|
|
96
|
+
|
|
99
97
|
|
|
100
98
|
constructor( config: ExteroceptionConfig = {} ){
|
|
101
99
|
this._bus = config.bus ?? null
|
|
100
|
+
this._boundary = config.endogenous ?? null
|
|
102
101
|
this._maxPerceptsPerTick = config.maxPerceptsPerTick ?? 50
|
|
103
102
|
this._defaultSalience = config.defaultSalience ?? 0.3
|
|
104
|
-
this._emitPerceptEvents = config.emitPerceptEvents ?? true
|
|
105
103
|
this._highPriorityTypes = new Set( config.highPriorityTypes ?? [
|
|
106
104
|
'message', 'notification', 'alert', 'threat', 'goal',
|
|
107
105
|
])
|
|
108
106
|
}
|
|
109
107
|
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
110
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Wire the live sense boundary. Separate from construction because the
|
|
111
|
+
* boundary is derived from the assembled engine list, and this sense is
|
|
112
|
+
* constructed before that list exists (same reason `attachBus` exists).
|
|
113
|
+
*/
|
|
114
|
+
attachBoundary( resolve: () => ReadonlySet<string> ): void { this._boundary = resolve }
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The types that are THIS mind rather than its world.
|
|
118
|
+
*
|
|
119
|
+
* Re-read each tick so a host engine registered after assembly still lands
|
|
120
|
+
* inside the boundary; the memo makes that a set-identity check in the common
|
|
121
|
+
* case, since `endogenousTypes` returns the shipped set unchanged when nothing
|
|
122
|
+
* extra is declared.
|
|
123
|
+
*/
|
|
124
|
+
private _endogenous(): ReadonlySet<string> {
|
|
125
|
+
if( this._boundary ) this._endogenousMemo = this._boundary()
|
|
126
|
+
return this._endogenousMemo
|
|
127
|
+
}
|
|
128
|
+
|
|
111
129
|
// ── Engine interface ─────────────────────────────────────
|
|
112
130
|
|
|
113
131
|
subscribes(): string[] { return ['executive.prediction.formed'] }
|
|
@@ -132,9 +150,7 @@ export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
132
150
|
state: ReadonlySimulationState,
|
|
133
151
|
context: SimulationContext
|
|
134
152
|
): Promise<EngineResult> {
|
|
135
|
-
const
|
|
136
|
-
events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
|
|
137
|
-
commands: StateCommands = { set: [], delete: [], metrics: [] }
|
|
153
|
+
const commands: StateCommands = { set: [], delete: [], metrics: [] }
|
|
138
154
|
|
|
139
155
|
const rawPercepts = this._scanWorld( state )
|
|
140
156
|
|
|
@@ -168,37 +184,27 @@ export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
168
184
|
: entityHit ? rp.salience * CORRESPONDENCE_ATTENUATION
|
|
169
185
|
: rp.salience
|
|
170
186
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
events.push({
|
|
193
|
-
type: `percept.${rp.changeType}.${rp.category}`,
|
|
194
|
-
source: this.name,
|
|
195
|
-
payload: {
|
|
196
|
-
entityId: rp.entityId,
|
|
197
|
-
salience,
|
|
198
|
-
category: rp.category,
|
|
199
|
-
summary: rp.summary,
|
|
200
|
-
},
|
|
201
|
-
})
|
|
187
|
+
// Through the shared constructor (SIGNAL_BOUNDARY P0) rather than a
|
|
188
|
+
// hand-rolled literal. A no-op here — this writer already set every core
|
|
189
|
+
// field — which is exactly why it is the one that proves the shape: four
|
|
190
|
+
// other writers reach it having each forgotten a different one.
|
|
191
|
+
//
|
|
192
|
+
// `provenance` is INFERRED here, legitimately: a match against our own
|
|
193
|
+
// live consequence descriptors is the efference copy doing its job. The
|
|
194
|
+
// sense door asserts instead, because it has nothing to match against.
|
|
195
|
+
commands.set!.push( perceptEntity( {
|
|
196
|
+
id: `percept-${tick}-${i}`,
|
|
197
|
+
tick,
|
|
198
|
+
salience,
|
|
199
|
+
category: rp.category,
|
|
200
|
+
summary: rp.summary,
|
|
201
|
+
provenance: hit ? 'reafferent' : 'exafferent',
|
|
202
|
+
entityId: rp.entityId,
|
|
203
|
+
changeType: rp.changeType,
|
|
204
|
+
...( hit ? { sourceIntentId: hit.intentId } : {} ),
|
|
205
|
+
// affect→percept seam (registry #5): what this percept FEELS like
|
|
206
|
+
...( rp.valence !== undefined ? { valence: rp.valence, valenceSource: rp.valenceSource } : {} ),
|
|
207
|
+
} ) )
|
|
202
208
|
}
|
|
203
209
|
|
|
204
210
|
// Clean up old percepts from previous ticks (keep last 2 ticks of percepts)
|
|
@@ -211,13 +217,12 @@ export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
211
217
|
)
|
|
212
218
|
|
|
213
219
|
|
|
214
|
-
//
|
|
220
|
+
// The percept RATE is still learned — `percept.rate` carries a precision set
|
|
221
|
+
// from `executive.prediction.formed`, and the observation is what keeps that
|
|
222
|
+
// baseline honest. What is gone is the `percept.batch.ingested` event it used
|
|
223
|
+
// to publish: one publisher, zero subscribers, ever.
|
|
215
224
|
const _bus = this._bus
|
|
216
|
-
if(
|
|
217
|
-
const predErr = this._model.observe('percept.rate', capped.length )
|
|
218
|
-
if( !predErr.gated )
|
|
219
|
-
_bus.publish({ type: 'percept.batch.ingested', version: 1, sourceEngine: this.name, salience: Math.max( 0.2, predErr.salience ), payload: { count: capped.length } })
|
|
220
|
-
}
|
|
225
|
+
if( capped.length > 0 ) this._model.observe('percept.rate', capped.length )
|
|
221
226
|
|
|
222
227
|
// percept.category.updated — one event per distinct category so subscribers
|
|
223
228
|
// (aesthetic.evaluator etc.) get a structured signal without scanning percepts
|
|
@@ -229,7 +234,7 @@ export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
229
234
|
_bus.publish({ type: 'percept.category.updated', version: 1, sourceEngine: this.name, salience: Math.min( 1, count * 0.15 + 0.2 ), payload: { category, count } })
|
|
230
235
|
}
|
|
231
236
|
|
|
232
|
-
return {
|
|
237
|
+
return { commands }
|
|
233
238
|
}
|
|
234
239
|
|
|
235
240
|
// ── Internal ─────────────────────────────────────────────
|
|
@@ -243,18 +248,19 @@ export class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
243
248
|
private _scanWorld( state: ReadonlySimulationState ): RawPercept[] {
|
|
244
249
|
const percepts: RawPercept[] = []
|
|
245
250
|
const currentIds = new Set<string>()
|
|
251
|
+
const mine = this._endogenous()
|
|
246
252
|
|
|
247
253
|
for( const [ id, entity ] of state.entities ){
|
|
248
254
|
currentIds.add( id )
|
|
249
255
|
|
|
250
|
-
if(
|
|
251
|
-
// Still track
|
|
252
|
-
//
|
|
253
|
-
this._previousEntityVersions.set( id, entity.updatedAt )
|
|
256
|
+
if( mine.has( entity.type ) ){
|
|
257
|
+
// Still track it, so it never reads as "appeared" if the boundary later
|
|
258
|
+
// widens — but it is machinery, not world, and is not perceived.
|
|
259
|
+
this._previousEntityVersions.set( id, { at: entity.updatedAt, type: entity.type } )
|
|
254
260
|
continue
|
|
255
261
|
}
|
|
256
262
|
|
|
257
|
-
const previousVersion = this._previousEntityVersions.get( id )
|
|
263
|
+
const previousVersion = this._previousEntityVersions.get( id )?.at
|
|
258
264
|
|
|
259
265
|
if( previousVersion === undefined ){
|
|
260
266
|
percepts.push({
|
|
@@ -279,32 +285,27 @@ private _scanWorld( state: ReadonlySimulationState ): RawPercept[] {
|
|
|
279
285
|
})
|
|
280
286
|
}
|
|
281
287
|
|
|
282
|
-
this._previousEntityVersions.set( id, entity.updatedAt )
|
|
288
|
+
this._previousEntityVersions.set( id, { at: entity.updatedAt, type: entity.type } )
|
|
283
289
|
}
|
|
284
290
|
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
summary: `Entity removed: ${id}`,
|
|
304
|
-
})
|
|
305
|
-
}
|
|
306
|
-
this._previousEntityVersions.delete( id )
|
|
307
|
-
}
|
|
291
|
+
// Something that was there is gone. The same question as an appearance —
|
|
292
|
+
// was it mine or the world's? — and answerable now because the remembered
|
|
293
|
+
// type outlives the entity. It used to be guessed from a handful of id
|
|
294
|
+
// prefixes, which is why 17,802 of one quiet run's 36,721 percepts were the
|
|
295
|
+
// mind watching its own affordance field be torn down each tick.
|
|
296
|
+
for( const [ id, seen ] of this._previousEntityVersions ){
|
|
297
|
+
if( currentIds.has( id ) ) continue
|
|
298
|
+
|
|
299
|
+
if( !mine.has( seen.type ) )
|
|
300
|
+
percepts.push({
|
|
301
|
+
entityId: id,
|
|
302
|
+
changeType: 'removed',
|
|
303
|
+
salience: 0.4,
|
|
304
|
+
category: 'removed',
|
|
305
|
+
summary: `Entity removed: ${id}`,
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
this._previousEntityVersions.delete( id )
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
percepts.sort( ( a, b ) => b.salience - a.salience )
|
|
@@ -374,7 +375,7 @@ private _scanWorld( state: ReadonlySimulationState ): RawPercept[] {
|
|
|
374
375
|
? `${name} appears` : `${name} changed`
|
|
375
376
|
}
|
|
376
377
|
if( description ){
|
|
377
|
-
return description.slice( 0,
|
|
378
|
+
return description.slice( 0, PERCEPT_SUMMARY_CAP )
|
|
378
379
|
}
|
|
379
380
|
|
|
380
381
|
return `New ${entity.type}: ${entity.id.slice(0, 30)}`
|