@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
|
@@ -23,6 +23,7 @@ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
|
|
|
23
23
|
import { GenerativeModel } from '#cognition/generative.model'
|
|
24
24
|
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
25
25
|
import { ExecutiveEngine } from '#faculties/executive.engine'
|
|
26
|
+
import { identityCommand } from '#cognition/identity.entity'
|
|
26
27
|
|
|
27
28
|
export interface AutobiographicalNarratorConfig {
|
|
28
29
|
minIntervalTicks?: number
|
|
@@ -171,16 +172,10 @@ export class AutobiographicalNarrator implements SimulationEngine, CognitiveEngi
|
|
|
171
172
|
for( const { key: trait, value: delta } of executiveOutput.identityUpdates.traits )
|
|
172
173
|
updatedTraits[ trait ] = Math.max( 0, Math.min( 1, ( updatedTraits[ trait ] ?? 0.5 ) + delta ) )
|
|
173
174
|
|
|
174
|
-
commands.set!.push({
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
metadata: {
|
|
179
|
-
...existingIdentity.metadata,
|
|
180
|
-
traits: updatedTraits,
|
|
181
|
-
version: ( ( existingIdentity.metadata?.version as number ) ?? 1 ) + 1,
|
|
182
|
-
},
|
|
183
|
-
})
|
|
175
|
+
commands.set!.push( identityCommand( state, {
|
|
176
|
+
traits: updatedTraits,
|
|
177
|
+
version: ( ( existingIdentity.metadata?.version as number ) ?? 1 ) + 1,
|
|
178
|
+
} ) )
|
|
184
179
|
}
|
|
185
180
|
}
|
|
186
181
|
|
|
@@ -41,7 +41,22 @@ export interface CircadianConfig {
|
|
|
41
41
|
/** Whether external light signals entrain the oscillator */
|
|
42
42
|
entrainable?: boolean
|
|
43
43
|
/** Current simulated time of day in hours (0-24). If not provided, derived from tick. */
|
|
44
|
-
|
|
44
|
+
/**
|
|
45
|
+
* The hour of day, 0–24. A NUMBER freezes it; a FUNCTION is read every tick,
|
|
46
|
+
* which is what a host with a real clock should pass.
|
|
47
|
+
*
|
|
48
|
+
* WHY THIS IS THE HOST'S TO SUPPLY. Left unset, the oscillator derives the
|
|
49
|
+
* hour from the tick count (1 tick = 1 second), which makes it *hours since
|
|
50
|
+
* tick zero, mod 24* — and after a snapshot restore, tick zero is an
|
|
51
|
+
* arbitrary point in a previous life. A mind reasoning about whether anyone
|
|
52
|
+
* is awake right now was reasoning from that.
|
|
53
|
+
*
|
|
54
|
+
* A circadian oscillator is a BODY RHYTHM, not a clock. It models what a body
|
|
55
|
+
* does over a day; it cannot know what day it is or where on Earth it runs.
|
|
56
|
+
* What time it is, is a fact about the world — afference — and arrives the
|
|
57
|
+
* way `environment.light_level` already does: from whoever can see it.
|
|
58
|
+
*/
|
|
59
|
+
timeOfDayHours?: number | ( () => number )
|
|
45
60
|
bus?: CognitiveBus
|
|
46
61
|
}
|
|
47
62
|
|
|
@@ -51,7 +66,7 @@ export class CircadianOscillator implements SimulationEngine, CognitiveEngine {
|
|
|
51
66
|
private _periodHours: number
|
|
52
67
|
private _phaseOffsetHours: number
|
|
53
68
|
private _entrainable: boolean
|
|
54
|
-
private _timeOfDayHours?: number
|
|
69
|
+
private _timeOfDayHours?: number | ( () => number )
|
|
55
70
|
|
|
56
71
|
private _bus: CognitiveBus | null = null
|
|
57
72
|
|
|
@@ -100,8 +115,11 @@ export class CircadianOscillator implements SimulationEngine, CognitiveEngine {
|
|
|
100
115
|
events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
|
|
101
116
|
commands: StateCommands = { metrics: [] }
|
|
102
117
|
|
|
103
|
-
// Determine current time of day
|
|
104
|
-
|
|
118
|
+
// Determine current time of day. A function is re-read every tick — a host
|
|
119
|
+
// clock moves; a config value does not.
|
|
120
|
+
let timeOfDay = typeof this._timeOfDayHours === 'function'
|
|
121
|
+
? normalizeHour( this._timeOfDayHours() )
|
|
122
|
+
: this._timeOfDayHours
|
|
105
123
|
|
|
106
124
|
if( timeOfDay === undefined ){
|
|
107
125
|
// Derive from tick, assuming 1 tick = 1 second
|
|
@@ -278,17 +296,28 @@ export class CircadianOscillator implements SimulationEngine, CognitiveEngine {
|
|
|
278
296
|
* Set the current time of day explicitly (for scenarios with controlled time).
|
|
279
297
|
*/
|
|
280
298
|
setTimeOfDay( hours: number ): void {
|
|
281
|
-
this._timeOfDayHours = ( hours
|
|
299
|
+
this._timeOfDayHours = normalizeHour( hours )
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/** Read a live clock every tick instead of holding a fixed hour. */
|
|
303
|
+
setClock( clock: ( () => number ) | null ): void {
|
|
304
|
+
this._timeOfDayHours = clock ?? undefined
|
|
282
305
|
}
|
|
283
306
|
|
|
284
307
|
/**
|
|
285
308
|
* Get the current circadian phase.
|
|
286
309
|
*/
|
|
287
310
|
getPhase(): CircadianPhase {
|
|
288
|
-
const h = this._timeOfDayHours
|
|
311
|
+
const h = typeof this._timeOfDayHours === 'function'
|
|
312
|
+
? normalizeHour( this._timeOfDayHours() )
|
|
313
|
+
: this._timeOfDayHours ?? 12
|
|
289
314
|
return h >= 6 && h < 12 ? 'morning'
|
|
290
315
|
: h >= 12 && h < 17 ? 'afternoon'
|
|
291
316
|
: h >= 17 && h < 21 ? 'evening'
|
|
292
317
|
: 'night'
|
|
293
318
|
}
|
|
294
|
-
}
|
|
319
|
+
}
|
|
320
|
+
/** 0–24, wrapping — a host clock that hands over 25 or −1 must not poison the rhythm. */
|
|
321
|
+
function normalizeHour( h: number ): number {
|
|
322
|
+
return Number.isFinite( h ) ? ( ( h % 24 ) + 24 ) % 24 : 12
|
|
323
|
+
}
|
|
@@ -114,6 +114,13 @@ export class EpisodicConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
114
114
|
|
|
115
115
|
// Vector memory integration
|
|
116
116
|
private _vectorMemory: VectorMemoryAdapter | null = null
|
|
117
|
+
/**
|
|
118
|
+
* In-flight background indexing. Indexing is deliberately not awaited inside
|
|
119
|
+
* react() (a rate-limit retry chain would stall the whole tick loop), so this is
|
|
120
|
+
* the handle for the two callers that genuinely must wait for it: shutdown,
|
|
121
|
+
* before persisting the index, and tests asserting on it.
|
|
122
|
+
*/
|
|
123
|
+
private _indexing: Promise<void> = Promise.resolve()
|
|
117
124
|
private _embedder: EmbeddingProvider | null = null
|
|
118
125
|
private _autoIndex: boolean
|
|
119
126
|
|
|
@@ -278,7 +285,35 @@ export class EpisodicConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
278
285
|
content: ep.content
|
|
279
286
|
} ) )
|
|
280
287
|
|
|
281
|
-
|
|
288
|
+
// Indexing is BEST-EFFORT, exactly like semanticQuery. It calls an embedding
|
|
289
|
+
// provider, so a rate limit or an outage is an ordinary condition, not a
|
|
290
|
+
// cognitive fault: the episode is already consolidated and in the store, and
|
|
291
|
+
// only its vector is missing. Letting that throw took the whole engine down
|
|
292
|
+
// mid-tick — observed live as `Engine "episodic-consolidator" threw at tick
|
|
293
|
+
// 107: Embedding failed: 429`, which also aborted the rest of this react()
|
|
294
|
+
// (store sync, forgetting-curve decay) for that tick.
|
|
295
|
+
//
|
|
296
|
+
// The episode itself is NOT lost: it is in `_store` and is written to state by
|
|
297
|
+
// the periodic full-store sync below, so it survives snapshot/restore. Only its
|
|
298
|
+
// VECTOR is missing, which costs semantic recall of that episode for the rest of
|
|
299
|
+
// the session — `rebuildFromStore()` re-indexes the whole store on a later
|
|
300
|
+
// restore that finds no persisted index. Nothing re-indexes it in-session; that
|
|
301
|
+
// is a known gap, not a claim that this self-heals.
|
|
302
|
+
// NOT awaited. Nothing in this tick reads the vector index, and the embedding
|
|
303
|
+
// call is a network round-trip behind a rate-limit gate whose retry chain runs
|
|
304
|
+
// ~60s (4+8+16+32s) before giving up. Awaiting it made a single 429 stall the
|
|
305
|
+
// WHOLE TICK LOOP for a minute — measured: ticks 3→4 and 4→5 took 64.9s and
|
|
306
|
+
// 63.5s, one per deferred batch. That is not merely slow: `AWAIT_TIMEOUT` and
|
|
307
|
+
// every other agency deadline are denominated in TICKS, so a 60s tick silently
|
|
308
|
+
// turned a 15-tick timeout into 15 minutes, left one communicate intent stuck
|
|
309
|
+
// 'awaiting', and — because the selector is serial — blocked every subsequent
|
|
310
|
+
// action. 45 executive decisions produced 1 intent and 0 delivered messages.
|
|
311
|
+
this._indexing = this._vectorMemory.indexBatch( episodesWithContent ).catch( ( err: unknown ) => {
|
|
312
|
+
logger.warn(
|
|
313
|
+
`[EpisodicConsolidator] indexing deferred for ${ newEpisodes.length } episode(s) — ` +
|
|
314
|
+
`${ err instanceof Error ? err.message : String( err ) }`
|
|
315
|
+
)
|
|
316
|
+
} )
|
|
282
317
|
}
|
|
283
318
|
|
|
284
319
|
// 5. Periodic full-store sync — captures activationStrength decay (forgetting curve),
|
|
@@ -385,6 +420,9 @@ export class EpisodicConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
385
420
|
* Other metadata narrowing (sourceType / tags) remains the caller's job on the
|
|
386
421
|
* returned episodes (which carry all metadata).
|
|
387
422
|
*/
|
|
423
|
+
/** Await any background indexing still in flight. Shutdown and tests only. */
|
|
424
|
+
async flushIndexing(): Promise<void> { await this._indexing }
|
|
425
|
+
|
|
388
426
|
async semanticQuery(
|
|
389
427
|
query: unknown,
|
|
390
428
|
filters?: {
|
|
@@ -417,10 +455,18 @@ export class EpisodicConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
417
455
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
418
456
|
const timedOut = Symbol('recall-timeout')
|
|
419
457
|
|
|
458
|
+
// The search keeps running after the race is lost — its own rate-limit retry
|
|
459
|
+
// chain can outlive this timeout by a minute. Its rejection is caught HERE
|
|
460
|
+
// rather than left to the race: a promise that loses a Promise.race still
|
|
461
|
+
// settles, and an unhandled 429 rejection surfaces as a process-level warning
|
|
462
|
+
// (or a crash, depending on host) long after the recall it belonged to gave up.
|
|
420
463
|
const results = await Promise.race( [
|
|
421
464
|
this._vectorMemory.search( query, {
|
|
422
465
|
maxResults: fetch,
|
|
423
466
|
minSimilarity: filters?.minSimilarity,
|
|
467
|
+
} ).catch( ( err: unknown ) => {
|
|
468
|
+
logger.warn(`[EpisodicConsolidator] recall search failed — ${ err instanceof Error ? err.message : String( err ) }`)
|
|
469
|
+
return []
|
|
424
470
|
} ),
|
|
425
471
|
new Promise<typeof timedOut>( resolve => { timer = setTimeout( () => resolve( timedOut ), timeoutMs ) } ),
|
|
426
472
|
] ).finally( () => clearTimeout( timer ) )
|
|
@@ -658,8 +704,18 @@ export class EpisodicConsolidator implements SimulationEngine, CognitiveEngine {
|
|
|
658
704
|
if( this._vectorMemory ){
|
|
659
705
|
await this._vectorMemory.load()
|
|
660
706
|
if( this._vectorMemory.size === 0 && this._store.length > 0 ){
|
|
661
|
-
|
|
662
|
-
|
|
707
|
+
// Re-embedding the whole store is a bulk network operation, and this runs on
|
|
708
|
+
// the FIRST TICK after restore. Awaited, it held tick 1 for 66.7s against a
|
|
709
|
+
// 500ms budget and threw on a rate limit — a mind spending its first waking
|
|
710
|
+
// minute frozen, with every tick-denominated deadline stretched around it.
|
|
711
|
+
// Detached: recall degrades to "not yet indexed" until it lands, which is the
|
|
712
|
+
// same best-effort contract semanticQuery already has. `flushIndexing()` is
|
|
713
|
+
// what shutdown drains, so a rebuild in flight is still written out.
|
|
714
|
+
this._indexing = this._vectorMemory.rebuildFromStore( this._store )
|
|
715
|
+
.then( () => { logger.info(`[episodic] vector index rebuilt with ${ this._store.length } episodes`) } )
|
|
716
|
+
.catch( ( err: unknown ) => {
|
|
717
|
+
logger.warn(`[episodic] vector index rebuild deferred — ${ err instanceof Error ? err.message : String( err ) }`)
|
|
718
|
+
} )
|
|
663
719
|
} else if( this._vectorMemory.size > 0 ){
|
|
664
720
|
logger.info(`[episodic] vector index loaded from disk (${this._vectorMemory.size} entries)`)
|
|
665
721
|
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/executive.engine/action.record.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// What became of what I did.
|
|
6
|
+
//
|
|
7
|
+
// The executive's prompt has a `## What Became Of What I Did` section (called
|
|
8
|
+
// `## Recent Action Outcomes` until P3 renamed it after what it renders), a builder
|
|
9
|
+
// for it, and a type for its data. `context.ts` fills it by scanning
|
|
10
|
+
// `decision.record` entities that carry an `actionStatus`. Nothing in the engine
|
|
11
|
+
// has ever written `actionStatus` — it is read in exactly one place and set in
|
|
12
|
+
// none — so the section has never had a single row. Measured across every
|
|
13
|
+
// prompt a live COO received over two runs: that section appears
|
|
14
|
+
// 0 times, while `## What I've Said Lately` appears in all of them.
|
|
15
|
+
//
|
|
16
|
+
// So the mind could see what it had SAID and never what it had DONE. Its only
|
|
17
|
+
// other record of its own doing is `## What I Have Been Choosing` (`## Recent
|
|
18
|
+
// Actions` at the time), which is fed from
|
|
19
|
+
// `output.actions` — the executive's DECISIONS — and is therefore a list of
|
|
20
|
+
// intentions wearing the name of a history.
|
|
21
|
+
//
|
|
22
|
+
// A mind whose record of acting contains only intentions cannot tell an
|
|
23
|
+
// intention from an act. Live, asked directly "have you completed that?", a COO
|
|
24
|
+
// answered "Yes — it's done. I drafted the full v0.1 spec: user stories, command
|
|
25
|
+
// surface, integration points, success metrics. Posted it to FKEM." She had
|
|
26
|
+
// posted nothing; she had no effectors at all and could not have drafted
|
|
27
|
+
// anything anywhere. Her deliberation history said "I produce the scoping doc
|
|
28
|
+
// now" across twenty cycles, and nothing in her state said whether she had.
|
|
29
|
+
//
|
|
30
|
+
// This is the same distinction `pending` draws for utterances — attempting to
|
|
31
|
+
// speak is not speaking (#124) — carried to acts in general. It is written from
|
|
32
|
+
// the `action.outcome` the executive ALREADY receives (it subscribes to `*`) and
|
|
33
|
+
// from an explicit withheld signal, and it is an ordinary entity, so it
|
|
34
|
+
// snapshots and replays with the rest of the mind.
|
|
35
|
+
// ─────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
import type { EntityInput, Tick } from '#core/types'
|
|
38
|
+
|
|
39
|
+
export const ACTION_RECORD_TYPE = 'action.record'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* How many outcomes the mind carries.
|
|
43
|
+
*
|
|
44
|
+
* Matches the cap `context.ts` already applies when rendering, so the state
|
|
45
|
+
* holds what the prompt shows and no more — an unbounded history of everything
|
|
46
|
+
* ever done is a memory system, not a working record, and this is the latter.
|
|
47
|
+
*/
|
|
48
|
+
export const ACTION_RECORD_KEEP = 6
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `withheld` is a first-class outcome, not a flavour of failure.
|
|
52
|
+
*
|
|
53
|
+
* The mind considered an act, formed it, and chose not to complete it. Folding
|
|
54
|
+
* that into `failed` is the mistake #123 was written to correct — it taught a
|
|
55
|
+
* COO it was bad at speaking from the times it decided not to speak.
|
|
56
|
+
*/
|
|
57
|
+
export type ActionStatus = 'completed' | 'failed' | 'withheld'
|
|
58
|
+
|
|
59
|
+
export interface ActionRecord {
|
|
60
|
+
type: string
|
|
61
|
+
status: ActionStatus
|
|
62
|
+
tick: Tick
|
|
63
|
+
targetEntityId?: string
|
|
64
|
+
/** The mind's own account of what happened, in its words where it has any. */
|
|
65
|
+
outcome: string
|
|
66
|
+
planId?: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Stable id — one record per (tick, act), so two acts on a tick both survive. */
|
|
70
|
+
export function actionRecordId( tick: Tick, type: string, targetEntityId?: string ): string {
|
|
71
|
+
return `action-record-${ tick }-${ type }${ targetEntityId ? `-${ targetEntityId }` : '' }`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function actionRecordEntity( r: ActionRecord ): EntityInput {
|
|
75
|
+
return {
|
|
76
|
+
id: actionRecordId( r.tick, r.type, r.targetEntityId ),
|
|
77
|
+
type: ACTION_RECORD_TYPE,
|
|
78
|
+
metadata: { ...r },
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Read a record back off entity metadata — every field, including the ones it is easy to forget. */
|
|
83
|
+
export function readActionRecord(
|
|
84
|
+
m: ReadonlyMap<string, unknown> | Record<string, unknown> | undefined,
|
|
85
|
+
): ActionRecord | null {
|
|
86
|
+
const meta = ( m instanceof Map ? Object.fromEntries( m ) : m ?? {} ) as Record<string, unknown>
|
|
87
|
+
const type = typeof meta['type'] === 'string' ? meta['type'] as string : undefined
|
|
88
|
+
const status = meta['status']
|
|
89
|
+
if( !type || ( status !== 'completed' && status !== 'failed' && status !== 'withheld') ) return null
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
type, status,
|
|
93
|
+
tick: typeof meta['tick'] === 'number' ? meta['tick'] as number : 0,
|
|
94
|
+
targetEntityId: typeof meta['targetEntityId'] === 'string' ? meta['targetEntityId'] as string : undefined,
|
|
95
|
+
outcome: typeof meta['outcome'] === 'string' ? meta['outcome'] as string : '',
|
|
96
|
+
planId: typeof meta['planId'] === 'string' ? meta['planId'] as string : undefined,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The mind's recent acts, newest first. */
|
|
101
|
+
export function recentActionRecords(
|
|
102
|
+
entities: ReadonlyMap<string, { type: string; metadata?: ReadonlyMap<string, unknown> | Record<string, unknown> }>,
|
|
103
|
+
keep: number = ACTION_RECORD_KEEP,
|
|
104
|
+
): ActionRecord[] {
|
|
105
|
+
const out: ActionRecord[] = []
|
|
106
|
+
for( const [ , e ] of entities ){
|
|
107
|
+
if( e.type !== ACTION_RECORD_TYPE ) continue
|
|
108
|
+
const r = readActionRecord( e.metadata )
|
|
109
|
+
if( r ) out.push( r )
|
|
110
|
+
}
|
|
111
|
+
// Newest first, ties broken by act name so a run and its replay agree.
|
|
112
|
+
out.sort( ( a, b ) => b.tick - a.tick || ( a.type < b.type ? -1 : a.type > b.type ? 1 : 0 ) )
|
|
113
|
+
return out.slice( 0, keep )
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Records that have fallen off the end of the window — swept by the writer. */
|
|
117
|
+
export function staleActionRecordIds(
|
|
118
|
+
entities: ReadonlyMap<string, { type: string; metadata?: ReadonlyMap<string, unknown> | Record<string, unknown> }>,
|
|
119
|
+
keep: number = ACTION_RECORD_KEEP,
|
|
120
|
+
): string[] {
|
|
121
|
+
const all: Array<{ id: string; r: ActionRecord }> = []
|
|
122
|
+
for( const [ id, e ] of entities ){
|
|
123
|
+
if( e.type !== ACTION_RECORD_TYPE ) continue
|
|
124
|
+
const r = readActionRecord( e.metadata )
|
|
125
|
+
if( r ) all.push({ id, r })
|
|
126
|
+
}
|
|
127
|
+
all.sort( ( a, b ) => b.r.tick - a.r.tick || ( a.r.type < b.r.type ? -1 : a.r.type > b.r.type ? 1 : 0 ) )
|
|
128
|
+
return all.slice( keep ).map( x => x.id )
|
|
129
|
+
}
|
|
@@ -9,6 +9,9 @@ import type { ExecutiveSummarizer } from '#llm/summarizer'
|
|
|
9
9
|
import type { GoalManager } from '#faculties/goal.manager'
|
|
10
10
|
import type { GenerativeModel } from '#cognition/generative.model'
|
|
11
11
|
import type { SemanticIntegrator } from '#faculties/semantic.engine/integrator'
|
|
12
|
+
import { INNATE_SCHEMA_BY_ID } from '#agency/schemas/innate'
|
|
13
|
+
import { logger } from '#core/logger'
|
|
14
|
+
import { resolveKeid } from '#cognition/social.identity'
|
|
12
15
|
|
|
13
16
|
/** Maps the LLM's evidence enum to a numeric supportingEpisodes value for the belief store. */
|
|
14
17
|
export const EVIDENCE_TO_COUNT: Record<string, number> = {
|
|
@@ -168,10 +171,13 @@ export function buildStateCommands(
|
|
|
168
171
|
supportingEpisodes: belief.supportingEpisodes, tags: belief.tags } })
|
|
169
172
|
})
|
|
170
173
|
|
|
171
|
-
if( bus && ( u.name || u.feeling != null ) )
|
|
174
|
+
if( bus && ( u.name || u.feeling != null || u.sameAs ) )
|
|
172
175
|
effects.push( () => bus.publish({
|
|
173
176
|
type: 'known.entity.learned', version: 1, sourceEngine: 'executive',
|
|
174
|
-
|
|
177
|
+
// An identity verdict is worth more attention than a learned name: it
|
|
178
|
+
// reorganises everything the mind holds about two referents at once.
|
|
179
|
+
salience: u.sameAs ? 0.7 : 0.5,
|
|
180
|
+
payload: { keid: u.keid, name: u.name, feeling: u.feeling, sameAs: u.sameAs },
|
|
175
181
|
}) )
|
|
176
182
|
})
|
|
177
183
|
}
|
|
@@ -321,6 +327,32 @@ export function publishCognitiveEvents(
|
|
|
321
327
|
}
|
|
322
328
|
})
|
|
323
329
|
|
|
330
|
+
// agency.composite.proposed — the mind names a compound action as one skill.
|
|
331
|
+
//
|
|
332
|
+
// This is the creation seam for the instrumental→habitual gradient, and it had
|
|
333
|
+
// no producer: ReafferenceEngine subscribes to this event and its handler is the
|
|
334
|
+
// ONLY caller of SchemaRepertoire.registerComposite() anywhere in the tree, so
|
|
335
|
+
// until now no Will could hold a skill beyond the innate floor, for its entire
|
|
336
|
+
// life (#114). A capability the container offered and no tenant could reach.
|
|
337
|
+
//
|
|
338
|
+
// One event per proposal — the consumer registers them individually and drops
|
|
339
|
+
// anything with fewer than two sub-schemas, since a "composite" of one is just
|
|
340
|
+
// the schema it already had.
|
|
341
|
+
for( const skill of output.newSkills ?? [] )
|
|
342
|
+
bus.publish({
|
|
343
|
+
type: 'agency.composite.proposed',
|
|
344
|
+
version: 1,
|
|
345
|
+
sourceEngine: 'executive-engine',
|
|
346
|
+
salience: 0.7,
|
|
347
|
+
payload: {
|
|
348
|
+
id: skill.id,
|
|
349
|
+
composedOf: skill.composedOf,
|
|
350
|
+
...( skill.tags ? { tags: skill.tags } : {} ),
|
|
351
|
+
...( typeof skill.cost === 'number' ? { cost: skill.cost } : {} ),
|
|
352
|
+
tick: footprint.tickObserved,
|
|
353
|
+
}
|
|
354
|
+
})
|
|
355
|
+
|
|
324
356
|
// goal.proposed — when executive proposes new goals
|
|
325
357
|
if( output.newGoals?.length )
|
|
326
358
|
bus.publish({
|
|
@@ -372,20 +404,45 @@ export function publishCognitiveEvents(
|
|
|
372
404
|
})
|
|
373
405
|
}
|
|
374
406
|
|
|
375
|
-
|
|
407
|
+
/**
|
|
408
|
+
* Action names that mean "say something to someone". Exported because the
|
|
409
|
+
* conversation facet partitions its OWN actions by the same rule (an action
|
|
410
|
+
* aimed at a third party is an intention the master owns, not a reply the facet
|
|
411
|
+
* may deliver) — one definition, so the two ends cannot drift apart.
|
|
412
|
+
*/
|
|
413
|
+
export const COMMUNICATE_ACTION_TYPES = new Set([
|
|
376
414
|
'communicate', 'speak', 'initiate_conversation', 'reach-out', 'reach_out', 'talk', 'text', 'message',
|
|
377
415
|
])
|
|
378
416
|
|
|
417
|
+
/** Arg keys a mind uses for the words themselves — folded into `gist` (see below). */
|
|
418
|
+
const WORDS_ARG_KEYS = new Set([ 'content', 'message', 'text', 'body' ])
|
|
419
|
+
/** Arg keys naming the addressee — already resolved into `targetEntityId`. */
|
|
420
|
+
const ADDRESS_ARG_KEYS = new Set([ 'to', 'recipient', 'target', 'targetEntityId', 'entityId' ])
|
|
421
|
+
|
|
379
422
|
/** Resolve an executive action target (a display name OR a keid) to a known-entity keid. */
|
|
380
423
|
function resolveKnownEntity( target: string, state: ReadonlySimulationState ): string | undefined {
|
|
381
|
-
|
|
424
|
+
// THE resolver, shared with everything else that has to turn a reference into a
|
|
425
|
+
// referent. This was a private scan matching an exact keid or an exact name and
|
|
426
|
+
// consulting no alias table — so once dossiers were keyed by an anchor, naming
|
|
427
|
+
// someone by the address they were met at (`discord:1019…`) matched nothing and
|
|
428
|
+
// the whole intention evaporated silently, which is the failure this function's
|
|
429
|
+
// own logging exists to make visible.
|
|
430
|
+
return resolveKeid( state.entities as never, target )
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* The name the mind has LEARNED for this entity. The outreach facet addresses someone
|
|
435
|
+
* by it ("I have decided to reach out to ${ name }"), so a keid leaking through here
|
|
436
|
+
* would have the mind reaching out to `discord:1019376031150379101`. Undefined when
|
|
437
|
+
* unlearned — the caller omits it rather than substituting a placeholder.
|
|
438
|
+
*/
|
|
439
|
+
function knownEntityName( keid: string, state: ReadonlySimulationState ): string | undefined {
|
|
382
440
|
for( const e of state.entities.values() ){
|
|
383
441
|
if( e.type !== 'known-entity') continue
|
|
384
|
-
const m
|
|
385
|
-
|
|
386
|
-
const name = typeof m
|
|
387
|
-
|
|
388
|
-
if( name && name.toLowerCase() === t ) return keid
|
|
442
|
+
const m = e.metadata as Record<string, unknown> | undefined
|
|
443
|
+
if( m?.['keid'] !== keid ) continue
|
|
444
|
+
const name = typeof m['name'] === 'string' ? m['name'].trim() : ''
|
|
445
|
+
return name.length > 0 ? name : undefined
|
|
389
446
|
}
|
|
390
447
|
return undefined
|
|
391
448
|
}
|
|
@@ -408,6 +465,10 @@ function buildIdeomotorIntents(
|
|
|
408
465
|
): { set: EntityInput[]; delete: string[] } {
|
|
409
466
|
const set: EntityInput[] = []
|
|
410
467
|
const seen = new Set<string>()
|
|
468
|
+
/** Action names that named nothing this cycle — surfaced back to the mind below. */
|
|
469
|
+
const unresolved = new Set<string>()
|
|
470
|
+
/** Addressees the mind meant to reach but cannot resolve to anyone it knows. */
|
|
471
|
+
const unaddressed = new Set<string>()
|
|
411
472
|
const priority = clamp01( output.confidence ?? 0.8 )
|
|
412
473
|
|
|
413
474
|
// The host abilities currently afforded (source 'external' in the live field) —
|
|
@@ -425,21 +486,83 @@ function buildIdeomotorIntents(
|
|
|
425
486
|
const t = action.type.toLowerCase()
|
|
426
487
|
|
|
427
488
|
if( COMMUNICATE_ACTION_TYPES.has( t ) ){
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
489
|
+
// The addressee may be named on the action OR inside the args the executive
|
|
490
|
+
// authored. This used to read `action.target` alone and `continue` when it
|
|
491
|
+
// was absent — but the output guidelines document actions as
|
|
492
|
+
// `{type, reasoning, expectedOutcome}` and tell the mind to put specifics
|
|
493
|
+
// in `args`, so `args.to` is precisely what a well-behaved mind produces.
|
|
494
|
+
// A Will would write real sentences into `args.to`/`args.content`, the
|
|
495
|
+
// intent was never created, nothing competed, nothing was ever enqueued —
|
|
496
|
+
// and reafference then taught it that talking to that PERSON does not work.
|
|
497
|
+
const args = ( action.args && typeof action.args === 'object' ? action.args : {} ) as Record<string, unknown>
|
|
498
|
+
const named = [ action.target, args['to'], args['recipient'], args['target'], args['targetEntityId'], args['entityId'] ]
|
|
499
|
+
.find( v => typeof v === 'string' && v.trim().length > 0 ) as string | undefined
|
|
500
|
+
// Naming NOBODY and naming someone unreachable are different failures, and
|
|
501
|
+
// both used to `continue` in silence — the intent was never created, nothing
|
|
502
|
+
// competed, and the mind had no way to find out. Observed: a facet decided to
|
|
503
|
+
// contact a colleague by a name the mind had heard in conversation but never
|
|
504
|
+
// bound to a dossier; the whole intention evaporated without a trace, and the
|
|
505
|
+
// person it had just promised never heard from it.
|
|
506
|
+
if( !named ){ unaddressed.add('(no one)'); continue }
|
|
507
|
+
const keid = resolveKnownEntity( named, state )
|
|
508
|
+
if( !keid ){ unaddressed.add( named ); continue }
|
|
509
|
+
if( seen.has( keid ) ) continue
|
|
431
510
|
seen.add( keid )
|
|
511
|
+
// The master forms the INTENT; it does not author the words. Whatever it wrote
|
|
512
|
+
// is the DIRECTION for the outreach facet (AuditionEngine.authorOutreach) to
|
|
513
|
+
// speak in — so it lands in `gist`, never `content`. This is load-bearing:
|
|
514
|
+
// MotorSchemaExecutor._deliver sends `parameters.content` VERBATIM and only
|
|
515
|
+
// falls back to the facet when it is empty, so carrying the master's sentences
|
|
516
|
+
// as `content` would put the master itself in a second, parallel conversation
|
|
517
|
+
// with someone a conversation facet may already be talking to — one mind
|
|
518
|
+
// holding two threads with one person about one thing. `gist` was read in
|
|
519
|
+
// three places and written nowhere; this is what writes it.
|
|
520
|
+
const said = [ ...WORDS_ARG_KEYS ]
|
|
521
|
+
.map( k => args[ k ] )
|
|
522
|
+
.find( v => typeof v === 'string' && v.trim().length > 0 ) as string | undefined
|
|
523
|
+
const parameters: Record<string, unknown> = {}
|
|
524
|
+
for( const [ k, v ] of Object.entries( args ) )
|
|
525
|
+
if( !WORDS_ARG_KEYS.has( k ) && !ADDRESS_ARG_KEYS.has( k ) ) parameters[ k ] = v
|
|
526
|
+
if( said ) parameters['gist'] = said
|
|
527
|
+
const targetName = knownEntityName( keid, state )
|
|
528
|
+
if( targetName ) parameters['targetEntityName'] = targetName
|
|
529
|
+
|
|
530
|
+
// Named at INFO because this is the seam where a decision to contact someone
|
|
531
|
+
// either becomes a competing intention or disappears. When a Will named a
|
|
532
|
+
// colleague seven times over ten minutes and he never heard from it, nothing
|
|
533
|
+
// in the logs could distinguish "the intent was never created" from "it was
|
|
534
|
+
// created and lost every competition" — the two have completely different
|
|
535
|
+
// fixes, and the archaeology to tell them apart needed state snapshots that
|
|
536
|
+
// sample too coarsely to catch a cycle.
|
|
537
|
+
logger.info(
|
|
538
|
+
`[executive] willed reach-out → ${ targetName ?? keid } ` +
|
|
539
|
+
`(named '${ named }' → ${ keid }, priority=${ priority.toFixed( 2 ) })`
|
|
540
|
+
)
|
|
541
|
+
|
|
432
542
|
set.push({
|
|
433
543
|
id: `ideomotor-reach-out-${ keid }`,
|
|
434
544
|
type: 'ideomotor.intent',
|
|
435
|
-
metadata: {
|
|
545
|
+
metadata: {
|
|
546
|
+
schema: 'reach-out', targetEntityId: keid,
|
|
547
|
+
...( Object.keys( parameters ).length > 0 ? { parameters } : {} ),
|
|
548
|
+
priority, origin: 'executive', tick: footprint.tickObserved,
|
|
549
|
+
},
|
|
436
550
|
})
|
|
437
551
|
continue
|
|
438
552
|
}
|
|
439
553
|
|
|
440
554
|
// A host ability the executive imagines enacting, with its conscious args.
|
|
441
555
|
const schema = externalBySchema.get( t )
|
|
442
|
-
if( !schema
|
|
556
|
+
if( !schema ){
|
|
557
|
+
// The name resolves to NOTHING: not a communicate type, not an innate
|
|
558
|
+
// stance, not an ability the field affords. Record it so the mind can find
|
|
559
|
+
// out. Silence here is what let a Will spend eleven consecutive actions on
|
|
560
|
+
// an invented `query`, observe that nothing ever came of them, and conclude
|
|
561
|
+
// its MEMORY was broken — the one explanation that was not true.
|
|
562
|
+
if( !INNATE_SCHEMA_BY_ID.has( t ) ) unresolved.add( action.type )
|
|
563
|
+
continue
|
|
564
|
+
}
|
|
565
|
+
if( seen.has(`ability:${ schema }`) ) continue
|
|
443
566
|
seen.add(`ability:${ schema }`)
|
|
444
567
|
const keid = action.target ? resolveKnownEntity( action.target, state ) : undefined
|
|
445
568
|
set.push({
|
|
@@ -454,6 +577,51 @@ function buildIdeomotorIntents(
|
|
|
454
577
|
})
|
|
455
578
|
}
|
|
456
579
|
|
|
580
|
+
// An action that named nothing is REPORTED, not swallowed.
|
|
581
|
+
//
|
|
582
|
+
// The executive's actions bias the agency competition; they are not commands, so
|
|
583
|
+
// a name that matches no schema is not a dispatch error and nothing downstream
|
|
584
|
+
// ever objected. But an unopposed no-op is indistinguishable from an act that
|
|
585
|
+
// was tried and achieved nothing, and the mind reasons from the difference.
|
|
586
|
+
// Observed: eleven consecutive `query` actions (a name that does not exist),
|
|
587
|
+
// then "five consecutive queries with no memory trace is a failure mode" and a
|
|
588
|
+
// plan to diagnose its own memory. Telling it the name was not real costs one
|
|
589
|
+
// entity and removes a whole class of false self-belief.
|
|
590
|
+
if( unresolved.size > 0 )
|
|
591
|
+
set.push({
|
|
592
|
+
id: 'action.unresolved',
|
|
593
|
+
type: 'action.unresolved',
|
|
594
|
+
metadata: {
|
|
595
|
+
names: [ ...unresolved ],
|
|
596
|
+
summary: `I named ${ [ ...unresolved ].map( n => `'${ n }'` ).join(', ') } as an action, but ${ unresolved.size > 1 ? 'those are not things' : 'that is not a thing' } I can do — no such ability is in my repertoire or afforded right now. Nothing happened. To act I have to name something I actually have.`,
|
|
597
|
+
salience: 0.75,
|
|
598
|
+
origin: 'executive',
|
|
599
|
+
tick: footprint.tickObserved,
|
|
600
|
+
},
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
// An addressee that resolves to nobody is REPORTED, not swallowed.
|
|
604
|
+
//
|
|
605
|
+
// Same principle as the unresolved-name report above, one layer down: there the
|
|
606
|
+
// *verb* named nothing, here the *person* does. The mind can hear a name in
|
|
607
|
+
// conversation ("coordinate that through FKEM") long before that name is bound
|
|
608
|
+
// to anyone it can actually reach, and reaching-out to an unbound name simply
|
|
609
|
+
// does not happen. Told, it can do the human thing — ask how to reach them, or
|
|
610
|
+
// ask whoever mentioned them to make the introduction. Untold, it believes it
|
|
611
|
+
// made contact and follows up on a message it never sent.
|
|
612
|
+
if( unaddressed.size > 0 )
|
|
613
|
+
set.push({
|
|
614
|
+
id: 'action.unaddressed',
|
|
615
|
+
type: 'action.unaddressed',
|
|
616
|
+
metadata: {
|
|
617
|
+
names: [ ...unaddressed ],
|
|
618
|
+
summary: `I meant to reach ${ [ ...unaddressed ].map( n => `'${ n }'` ).join(', ') }, but ${ unaddressed.size > 1 ? 'those names match no one' : 'that name matches no one' } I know how to contact — no message went out. If I want to reach them I need a way to: someone can introduce us, or tell me where to find them.`,
|
|
619
|
+
salience: 0.8,
|
|
620
|
+
origin: 'executive',
|
|
621
|
+
tick: footprint.tickObserved,
|
|
622
|
+
},
|
|
623
|
+
})
|
|
624
|
+
|
|
457
625
|
// Clear stale executive-sourced intents the executive no longer imagines this cycle.
|
|
458
626
|
const currentIds = new Set( set.map( s => s.id ) )
|
|
459
627
|
const del: string[] = []
|
|
@@ -463,6 +631,13 @@ function buildIdeomotorIntents(
|
|
|
463
631
|
&& !currentIds.has( id ) )
|
|
464
632
|
del.push( id )
|
|
465
633
|
|
|
634
|
+
// Clear the report once the mind names only real actions again — it should read
|
|
635
|
+
// as "that last attempt was not a thing", not as a permanent defect in itself.
|
|
636
|
+
if( unresolved.size === 0 && state.entities.has('action.unresolved') )
|
|
637
|
+
del.push('action.unresolved')
|
|
638
|
+
if( unaddressed.size === 0 && state.entities.has('action.unaddressed') )
|
|
639
|
+
del.push('action.unaddressed')
|
|
640
|
+
|
|
466
641
|
return { set, delete: del }
|
|
467
642
|
}
|
|
468
643
|
|