@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
|
@@ -693,7 +693,12 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
693
693
|
.find( g => {
|
|
694
694
|
return g.tags.includes( mapping.tags[0]! )
|
|
695
695
|
&& g.tags.includes( mapping.tags[1]! )
|
|
696
|
-
|
|
696
|
+
// `pending` counts as existing. It did not, and a
|
|
697
|
+
// goal demoted for capacity therefore stopped
|
|
698
|
+
// blocking its own respawn — the drive minted a
|
|
699
|
+
// fresh copy every time its threshold was crossed.
|
|
700
|
+
// Measured: 92 identical copies of one goal.
|
|
701
|
+
&& ( g.status === 'active' || g.status === 'pending')
|
|
697
702
|
})
|
|
698
703
|
|
|
699
704
|
// Never spawn a goal that is *already satisfied* — it would complete on its
|
|
@@ -720,7 +725,9 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
720
725
|
// Dedup by the referent (keid: tag) when present, else by description.
|
|
721
726
|
const keidTag = tags.find( t => t.startsWith('keid:') )
|
|
722
727
|
const existing = Array.from( this._goals.values() ).find( g =>
|
|
723
|
-
|
|
728
|
+
// Same rule as the drive path: a waiting goal still exists.
|
|
729
|
+
( g.status === 'active' || g.status === 'pending')
|
|
730
|
+
&& ( keidTag ? g.tags.includes( keidTag ) : g.description === desc ) )
|
|
724
731
|
|
|
725
732
|
// Don't spawn a goal that's already satisfied (born-done guard — see _activateFromDrives).
|
|
726
733
|
if( !existing && !this._isConditionMet( cCond, state ) )
|
|
@@ -805,6 +812,32 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
805
812
|
return 0
|
|
806
813
|
}
|
|
807
814
|
|
|
815
|
+
/**
|
|
816
|
+
* Reconcile the active set to capacity — in BOTH directions.
|
|
817
|
+
*
|
|
818
|
+
* Demotion alone made `pending` a one-way door, and a mind fell through it.
|
|
819
|
+
* Nothing in this file promoted a goal back (the only other write of `'active'`
|
|
820
|
+
* is `pending_verification`'s, a different status), so a goal demoted for
|
|
821
|
+
* capacity was demoted for good. Worse, `pending` is excluded from every other
|
|
822
|
+
* mechanism that could have retired it: it is not in `getActiveGoals()`, not
|
|
823
|
+
* progress-updated, not reachable by the patience/grit sweep (which skips any
|
|
824
|
+
* status but `'active'`) — yet it IS re-persisted every tick and rehydrated on
|
|
825
|
+
* every restore. Inert and immortal at once.
|
|
826
|
+
*
|
|
827
|
+
* Measured on a live COO: 96 goal entities, of which **83 pending, 0 active**,
|
|
828
|
+
* and 92 of the 96 the same drive-spawned goal. `activeGoalCount` sat at 0, so
|
|
829
|
+
* `goalless_crisis` fired every 20 ticks forever while she carried 83 goals —
|
|
830
|
+
* and the `goal` term, the joint-largest weight in the affordance competition,
|
|
831
|
+
* contributed nothing to any choice she made.
|
|
832
|
+
*
|
|
833
|
+
* Promotion also restores GC reachability, which is the quieter half of the
|
|
834
|
+
* fix: an over-patient stale goal can only be abandoned while it is active.
|
|
835
|
+
*
|
|
836
|
+
* `activatedAt` is deliberately NOT refreshed on promotion. Refreshing it would
|
|
837
|
+
* let a goal cycle demote→promote and reset its own patience each time, making
|
|
838
|
+
* it unretirable — the immortality this fix exists to end, reintroduced by the
|
|
839
|
+
* back door. A goal's age is when it was taken up, not when it last got a slot.
|
|
840
|
+
*/
|
|
808
841
|
private _resolveConflicts(): void {
|
|
809
842
|
const active = this.getActiveGoals()
|
|
810
843
|
|
|
@@ -813,6 +846,21 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
813
846
|
const lowest = active.pop()
|
|
814
847
|
if( lowest ) lowest.status = 'pending'
|
|
815
848
|
}
|
|
849
|
+
|
|
850
|
+
// ...and take the best waiting goals back up when there is room. Only ever
|
|
851
|
+
// reached when UNDER capacity, so this cannot undo the demotion above:
|
|
852
|
+
// the loop leaves the set at exactly capacity, and this one starts below it.
|
|
853
|
+
if( active.length >= this._maxActiveGoals ) return
|
|
854
|
+
|
|
855
|
+
const waiting = Array.from( this._goals.values() )
|
|
856
|
+
.filter( g => g.status === 'pending')
|
|
857
|
+
.sort( ( a, b ) => b.priority - a.priority )
|
|
858
|
+
|
|
859
|
+
for( const goal of waiting ){
|
|
860
|
+
if( active.length >= this._maxActiveGoals ) break
|
|
861
|
+
goal.status = 'active'
|
|
862
|
+
active.push( goal )
|
|
863
|
+
}
|
|
816
864
|
}
|
|
817
865
|
|
|
818
866
|
// ── Internal: progress ───────────────────────────────────
|
|
@@ -108,7 +108,16 @@ const SENTIENT_DOMAINS = new Set<string>([ 'audition' ])
|
|
|
108
108
|
// specific "get to know them" pull; it subsides once resolution reaches RESOLVED.
|
|
109
109
|
const CURIOUS_FAMILIARITY = 0.5
|
|
110
110
|
const CURIOUS_RESOLUTION = 0.4
|
|
111
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Resolved enough to stop wondering — the point the curiosity goal completes at.
|
|
113
|
+
*
|
|
114
|
+
* Exported because the AffordanceSynthesizer offers `inspect` against referents
|
|
115
|
+
* BELOW it. Two readers, one constant, deliberately: if the act that satisfies
|
|
116
|
+
* curiosity and the goal that measures it disagreed about when something is
|
|
117
|
+
* known, a mind could keep being offered a look at what it had already placed —
|
|
118
|
+
* or stop being offered one while the goal it holds is still open.
|
|
119
|
+
*/
|
|
120
|
+
export const CURIOUS_RESOLVED = 0.6
|
|
112
121
|
|
|
113
122
|
// Forgetting (Phase 4). (Reliability rate is now a Channel-A developable field — analytical.)
|
|
114
123
|
const FORGET_FLOOR = 0.02 // below this familiarity, an unidentified blip is forgotten
|
|
@@ -154,6 +163,8 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
154
163
|
keid: string; domain: string; name?: string
|
|
155
164
|
/** The room this encounter happened in, and whether it was a private one. */
|
|
156
165
|
thread?: string; direct?: boolean
|
|
166
|
+
/** What that room is called, where the channel offered a label. */
|
|
167
|
+
threadName?: string
|
|
157
168
|
}> = []
|
|
158
169
|
// Buffered from known.entity.learned (the conscious / reasoning write-path, Phase 2.2).
|
|
159
170
|
private _pendingConscious: Array<{
|
|
@@ -247,12 +258,15 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
247
258
|
// `direct` is the one bit the Discord edge computed (`isDM`) and threw away
|
|
248
259
|
// before the mind could see it — the single fact that decides whether a room
|
|
249
260
|
// is the right place for a given utterance.
|
|
250
|
-
const raw = p?.raw as { speakerName?: unknown; threadId?: unknown; direct?: unknown } | undefined
|
|
261
|
+
const raw = p?.raw as { speakerName?: unknown; threadId?: unknown; direct?: unknown; threadName?: unknown } | undefined
|
|
251
262
|
const name = typeof raw?.speakerName === 'string' ? raw.speakerName : undefined
|
|
252
263
|
const thread = typeof raw?.threadId === 'string' ? raw.threadId : undefined
|
|
253
264
|
const direct = typeof raw?.direct === 'boolean' ? raw.direct : undefined
|
|
265
|
+
// What the room is CALLED. A place has had a dossier since 0.9.0 and no way
|
|
266
|
+
// to be named, so every room the mind knew rendered as "something".
|
|
267
|
+
const threadName = typeof raw?.threadName === 'string' ? raw.threadName : undefined
|
|
254
268
|
|
|
255
|
-
this._pendingEncounters.push({ keid, domain: p!.domain, name, thread, direct })
|
|
269
|
+
this._pendingEncounters.push({ keid, domain: p!.domain, name, thread, direct, threadName })
|
|
256
270
|
}
|
|
257
271
|
|
|
258
272
|
snapshot(): Record<string, unknown> {
|
|
@@ -319,6 +333,11 @@ export class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
|
|
|
319
333
|
place.encounterCount += 1
|
|
320
334
|
place.familiarity = Math.min( 1, place.familiarity + this._growthRate * ( 1 - place.familiarity ) )
|
|
321
335
|
place.lastSeenTick = tick
|
|
336
|
+
// A room learns its name the same way a person does — from what the
|
|
337
|
+
// channel offers, and only while it has none. Re-taken every encounter
|
|
338
|
+
// it would churn the cached prompt on a rename; more to the point, a
|
|
339
|
+
// name the mind already holds is the mind's, not the platform's.
|
|
340
|
+
if( enc.threadName && !place.name ) place.name = enc.threadName
|
|
322
341
|
place.resolutionConfidence = this._resolution( place )
|
|
323
342
|
}
|
|
324
343
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* competition: each tick it projects its ready frontier step(s) as `plan.prior`
|
|
21
21
|
* entities the AffordanceSynthesizer turns into competing affordances (top-down
|
|
22
22
|
* prior, never a bypass — see PLANNING_AS_PRIOR_TODO.md). The ordinary selector
|
|
23
|
-
* enacts the winner; the executor emits `action.outcome{planId,stepId}` (
|
|
23
|
+
* enacts the winner; the executor emits `action.outcome{planId,stepId}` (the plan link
|
|
24
24
|
* carried through the affordance→intent chain), which this engine consumes to
|
|
25
25
|
* advance the frontier. Resolves the prerequisite DAG to choose the ready frontier.
|
|
26
26
|
*
|
|
@@ -207,7 +207,7 @@ export class PlanningEngine implements SimulationEngine, CognitiveEngine {
|
|
|
207
207
|
planId?: string; stepId?: string
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
// Conscious-enaction credit: outcomes carry plan
|
|
210
|
+
// Conscious-enaction credit: outcomes carry a plan link only when
|
|
211
211
|
// the plan's OWN frontier prior won the competition. But the plan is a
|
|
212
212
|
// prior over WHAT to do — if the self does the very thing an active
|
|
213
213
|
// step calls for by any route (executive action via ideomotor, habit),
|
|
@@ -222,7 +222,7 @@ export class PlanningEngine implements SimulationEngine, CognitiveEngine {
|
|
|
222
222
|
if( plan.status !== 'executing') continue
|
|
223
223
|
const step = plan.steps.find( s => s.status === 'active' && s.action === p.actionType )
|
|
224
224
|
if( !step ) continue
|
|
225
|
-
logger.info(`[planning] conscious-enaction credit: ${plan.id}/${step.id}=${step.action} (no
|
|
225
|
+
logger.info(`[planning] conscious-enaction credit: ${plan.id}/${step.id}=${step.action} (no plan link on outcome)`)
|
|
226
226
|
this._onStepOutcome( plan.id, step.id, {
|
|
227
227
|
success: p.success,
|
|
228
228
|
description: p.description ?? ( p.success ? 'Completed' : 'Failed'),
|
|
@@ -656,7 +656,7 @@ export class PlanningEngine implements SimulationEngine, CognitiveEngine {
|
|
|
656
656
|
// pause/escalate, or a terminal goal — it stops projecting its frontier. But an
|
|
657
657
|
// `agency.intent` the competition had ALREADY committed from a frontier prior can
|
|
658
658
|
// still resolve a few ticks later (sync this tick, or an awaiting one timing out).
|
|
659
|
-
// That outcome carries this plan's
|
|
659
|
+
// That outcome carries this plan's link, so it lands here. We must NOT act on
|
|
660
660
|
// it: completion is the executive's call, not the late arrival of a step it has
|
|
661
661
|
// already moved past — and re-running supervision below could re-spawn the facet
|
|
662
662
|
// we just tore down. The enaction itself still happened (and still taught the
|
|
@@ -37,7 +37,7 @@ export function computeReadySet( plan: Plan ): PlanStep[] {
|
|
|
37
37
|
* entities — the top-down bias the AffordanceSynthesizer reads. Rebuilt each tick
|
|
38
38
|
* (cleared then re-emitted, like the affordance field), so a frontier that
|
|
39
39
|
* advances or a plan that ends stops biasing automatically. The prior carries the
|
|
40
|
-
* planId/stepId
|
|
40
|
+
* planId/stepId plan link that flows affordance → intent → action.outcome, and a
|
|
41
41
|
* `planBias` strength from the goal's importance ⊕ the plan's confidence. It never
|
|
42
42
|
* forces an action — if a more pressing affordance wins, the plan re-projects next
|
|
43
43
|
* tick (no orphaning).
|
|
@@ -634,7 +634,7 @@ export class SemanticIntegrator implements SimulationEngine, CognitiveEngine {
|
|
|
634
634
|
|
|
635
635
|
// Seed or preserve history.
|
|
636
636
|
// If the belief arrives with an existing history (e.g. from a PMM load or
|
|
637
|
-
// snapshot restore), preserve it and append the load event so
|
|
637
|
+
// snapshot restore), preserve it and append the load event so the lineage
|
|
638
638
|
// is traceable without losing the full trajectory.
|
|
639
639
|
// For brand-new beliefs (no prior history) seed a single creation entry.
|
|
640
640
|
if( newBelief.history && newBelief.history.length > 0 ){
|
|
@@ -280,7 +280,12 @@ export class WorkingMemory implements SimulationEngine, CognitiveEngine {
|
|
|
280
280
|
this._items.push({
|
|
281
281
|
id: `wm-percept-${entity.id}`,
|
|
282
282
|
type: 'percept',
|
|
283
|
-
|
|
283
|
+
// The data too, not only the label. A percept entity is swept after 2
|
|
284
|
+
// ticks; WM is where it is remembered, and remembering a sentence about
|
|
285
|
+
// the evidence instead of the evidence is how a mind ends up unable to
|
|
286
|
+
// answer a question it already had the answer to.
|
|
287
|
+
content: { summary, entityId: entity.id,
|
|
288
|
+
...( entity.metadata?.data !== undefined ? { data: entity.metadata.data } : {} ) },
|
|
284
289
|
activation: 0.75,
|
|
285
290
|
attendedAt: [],
|
|
286
291
|
createdAt: tick,
|
|
@@ -18,7 +18,7 @@ export interface Instruction {
|
|
|
18
18
|
deadline?: number
|
|
19
19
|
/** Constraints on how to execute */
|
|
20
20
|
constraints?: string[]
|
|
21
|
-
/** Context
|
|
21
|
+
/** Context — why this instruction was given */
|
|
22
22
|
context: string
|
|
23
23
|
/** Whether the Will can refuse */
|
|
24
24
|
isOverridable: boolean
|
|
@@ -67,8 +67,6 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
|
|
|
67
67
|
* and return "no recall" while a mind with six live facets was asking.
|
|
68
68
|
*/
|
|
69
69
|
maxConcurrency?: number
|
|
70
|
-
/** @deprecated use maxConcurrency — kept as its fallback for back-compat. */
|
|
71
|
-
batchSize?: number
|
|
72
70
|
/** Per-request timeout in ms before the connection is aborted. Default 30s. */
|
|
73
71
|
timeoutMs?: number
|
|
74
72
|
/**
|
|
@@ -82,7 +80,7 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
|
|
|
82
80
|
this.dimensions = config.dimensions
|
|
83
81
|
this._apiUrl = config.apiUrl
|
|
84
82
|
this._apiKey = config.apiKey ?? null
|
|
85
|
-
this._maxConcurrency = Math.max( 1, config.maxConcurrency ??
|
|
83
|
+
this._maxConcurrency = Math.max( 1, config.maxConcurrency ?? 4 )
|
|
86
84
|
this._gate = new LLMSemaphore( this._maxConcurrency )
|
|
87
85
|
this._timeoutMs = config.timeoutMs ?? 30_000
|
|
88
86
|
this._tokenTracker = config.tokenTracker ?? null
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/percept.entity.ts — one shape for "something reached me"
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// A `percept` entity is the staging area every downstream consumer of afference
|
|
6
|
+
// reads. Five places write one and none of them agreed on what one is:
|
|
7
|
+
//
|
|
8
|
+
// exteroception tick ✓ provenance ✓ the complete one
|
|
9
|
+
// outbox.controller tick ✗ provenance ✓
|
|
10
|
+
// escalation.buffer tick ✓ provenance ✗ (x2)
|
|
11
|
+
// stem/index.ts tick ✗ provenance ✗ the wake percept
|
|
12
|
+
//
|
|
13
|
+
// Both omissions are silent and both cost something real:
|
|
14
|
+
//
|
|
15
|
+
// • NO `tick` — `exteroception._collectStalePerceptIds` is the ONLY sweeper of
|
|
16
|
+
// this type, and it collects only entities whose `metadata.tick` is a
|
|
17
|
+
// number. Everything else is immortal. `msg-delivered-<id>` therefore leaks
|
|
18
|
+
// one entity per message the mind ever successfully sends, and the wake
|
|
19
|
+
// percept tells the executive "I was offline for 3 hours" for the rest of
|
|
20
|
+
// the mind's life.
|
|
21
|
+
// • NO `provenance` — `action.selector`'s rupture gate counts only percepts
|
|
22
|
+
// tagged `'exafferent'`, so an untagged percept can never rupture a
|
|
23
|
+
// commitment, exactly as the mind's own echo cannot. A mind waking after
|
|
24
|
+
// hours offline cannot be ruptured by noticing that.
|
|
25
|
+
//
|
|
26
|
+
// Neither is a bug in the writers. They are a bug in there being no shape to
|
|
27
|
+
// write: every one of them hand-rolled a metadata literal, and a literal cannot
|
|
28
|
+
// forget a field it was never asked for. This is the shape. It makes `tick` and
|
|
29
|
+
// `provenance` REQUIRED, so the sweeper's precondition and the rupture gate's
|
|
30
|
+
// precondition are both structural rather than remembered.
|
|
31
|
+
//
|
|
32
|
+
// SIGNAL_BOUNDARY P0. Own the contract before widening it — the sense door is
|
|
33
|
+
// about to become a sixth writer, and it must not invent a sixth variant.
|
|
34
|
+
|
|
35
|
+
import type { SignalProvenance } from '#senses/provenance'
|
|
36
|
+
|
|
37
|
+
export const PERCEPT_TYPE = 'percept'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* How long a `percept` survives before `Exteroception` sweeps it. Not a
|
|
41
|
+
* durability window — a *staging* window. Persistence happens downstream
|
|
42
|
+
* (WorkingMemory → EpisodicConsolidator → vector); a percept's job is to be
|
|
43
|
+
* seen once by the faculties that run each tick, then get out of the way.
|
|
44
|
+
*/
|
|
45
|
+
export const PERCEPT_STALE_AFTER_TICKS = 2
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* How much of a percept's `summary` survives.
|
|
49
|
+
*
|
|
50
|
+
* 100 because that is what `exteroception._summarizeEntity` has always used —
|
|
51
|
+
* adopted rather than chosen, so naming it changes no behaviour. It is now one
|
|
52
|
+
* constant instead of a literal per writer, which is the point: the sizing
|
|
53
|
+
* question is real and open (SIGNAL_BOUNDARY §4 asks it of 120/300/700 too, and
|
|
54
|
+
* notes that none of those was ever a budget decision), and answering it should
|
|
55
|
+
* be a one-line change in one place rather than an archaeology exercise.
|
|
56
|
+
*/
|
|
57
|
+
export const PERCEPT_SUMMARY_CAP = 100
|
|
58
|
+
|
|
59
|
+
/** What every percept must say, whatever wrote it. */
|
|
60
|
+
export interface PerceptFacts {
|
|
61
|
+
/** Deterministic and unique. Never `wallClock()` — this lives in state (R2). */
|
|
62
|
+
id: string
|
|
63
|
+
/** The tick it arrived on. REQUIRED: without it the entity is never swept. */
|
|
64
|
+
tick: number
|
|
65
|
+
/** 0–1. The rupture gate reads this. */
|
|
66
|
+
salience: number
|
|
67
|
+
/** Coarse kind, for grouping — 'message-delivery', 'system', 'undertaking'… */
|
|
68
|
+
category: string
|
|
69
|
+
/** The one field the executive prompt renders. If it is not here, it is unread. */
|
|
70
|
+
summary: string
|
|
71
|
+
/** REQUIRED: untagged means unrupturable. See `SignalProvenance`. */
|
|
72
|
+
provenance: SignalProvenance
|
|
73
|
+
/** The intent this is the consequence of, when `provenance` is `'reafferent'`. */
|
|
74
|
+
sourceIntentId?: string
|
|
75
|
+
/** The world entity this is about, where there is one. */
|
|
76
|
+
entityId?: string
|
|
77
|
+
/** What happened to it — 'delivered', 'removed', 'changed'… */
|
|
78
|
+
changeType?: string
|
|
79
|
+
/** What it FELT like, and how much that says about it (registry #5). */
|
|
80
|
+
valence?: number
|
|
81
|
+
valenceSource?: string
|
|
82
|
+
/**
|
|
83
|
+
* What a host sent, whole. Carried into state so it survives to working
|
|
84
|
+
* memory, recall and the prompt — `summary` is a label the engine wrote, and
|
|
85
|
+
* a mind reasoning only from labels is reasoning from somebody else's
|
|
86
|
+
* conclusions.
|
|
87
|
+
*/
|
|
88
|
+
data?: unknown
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The write-side entity shape `stateManager.setEntity` accepts. */
|
|
92
|
+
export interface PerceptEntity {
|
|
93
|
+
id: string
|
|
94
|
+
type: typeof PERCEPT_TYPE
|
|
95
|
+
metadata: Record<string, unknown>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build a `percept` entity. `extra` carries a writer's own fields (`messageId`,
|
|
100
|
+
* `facetId`, `offlineMs`…) and cannot overwrite the core: a writer that could
|
|
101
|
+
* clobber its own `tick` or `provenance` is back where it started.
|
|
102
|
+
*/
|
|
103
|
+
export function perceptEntity( facts: PerceptFacts, extra: Record<string, unknown> = {} ): PerceptEntity {
|
|
104
|
+
return {
|
|
105
|
+
id: facts.id,
|
|
106
|
+
type: PERCEPT_TYPE,
|
|
107
|
+
metadata: {
|
|
108
|
+
...extra,
|
|
109
|
+
tick: facts.tick,
|
|
110
|
+
salience: facts.salience,
|
|
111
|
+
category: facts.category,
|
|
112
|
+
summary: facts.summary,
|
|
113
|
+
provenance: facts.provenance,
|
|
114
|
+
...( facts.sourceIntentId !== undefined ? { sourceIntentId: facts.sourceIntentId } : {} ),
|
|
115
|
+
...( facts.entityId !== undefined ? { entityId: facts.entityId } : {} ),
|
|
116
|
+
...( facts.changeType !== undefined ? { changeType: facts.changeType } : {} ),
|
|
117
|
+
...( facts.valence !== undefined ? { valence: facts.valence } : {} ),
|
|
118
|
+
...( facts.valenceSource !== undefined ? { valenceSource: facts.valenceSource } : {} ),
|
|
119
|
+
...( facts.data !== undefined ? { data: facts.data } : {} ),
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/sense.boundary.ts — where I end and the world begins
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// A body needs no rule saying "do not see your own premotor cortex." You do not
|
|
6
|
+
// see it because the retina sits ON the boundary and motor planning is inside
|
|
7
|
+
// it. Efference reaches you as a different signal, in a different modality,
|
|
8
|
+
// already marked as yours — which this engine knows: that is what EXAFFERENCE
|
|
9
|
+
// P1/P2 built.
|
|
10
|
+
//
|
|
11
|
+
// Will has no such boundary. Cognition and world share one `state.entities` map,
|
|
12
|
+
// so the mind's own bookkeeping sits in the same place as the things it is
|
|
13
|
+
// bookkeeping ABOUT, and Exteroception — the outward sense — walks the lot.
|
|
14
|
+
//
|
|
15
|
+
// THE COST, MEASURED. A mind booted offline for 300 quiet ticks — no host, no
|
|
16
|
+
// channel, nothing outside it at all — produced 36,721 percepts, every one about
|
|
17
|
+
// itself:
|
|
18
|
+
//
|
|
19
|
+
// 17802 removed its own affordances being torn down each tick
|
|
20
|
+
// 16950 affordance its own affordances being rebuilt each tick
|
|
21
|
+
// 546 calibration.state
|
|
22
|
+
// 496 agency.intent
|
|
23
|
+
// 287 agency.outcome
|
|
24
|
+
// 286 agency.skill
|
|
25
|
+
// 135 action.unresolved
|
|
26
|
+
// 120 engine.config its own configuration
|
|
27
|
+
// 96 attention.demand
|
|
28
|
+
// 3 will.identity its own identity entity
|
|
29
|
+
//
|
|
30
|
+
// `maxPerceptsPerTick` is 50. The affordance field alone churns ~116 entities a
|
|
31
|
+
// tick, so the sensory cap was consumed by self-noise before the world said
|
|
32
|
+
// anything — and nothing downstream filters by category, so working memory, the
|
|
33
|
+
// novelty detector, the attention allocator and episodic consolidation all ate
|
|
34
|
+
// it. The visible symptom was elsewhere: a fresh Will proceduralized `inspect`
|
|
35
|
+
// to habit 0.64 within fifteen ticks and spent five of its first eight decisions
|
|
36
|
+
// examining its own affordance entities, because a percept-bound act offered
|
|
37
|
+
// against phantom percepts is a real act aimed at nothing.
|
|
38
|
+
//
|
|
39
|
+
// WHY IT DRIFTED. The boundary was a denylist living inside the sense
|
|
40
|
+
// (`internalTypes`), correct for the faculties it shipped beside in v0.1.0 and
|
|
41
|
+
// silently wrong for every entity type invented after. `git log -L` on that
|
|
42
|
+
// block shows two edits in five weeks — EXAFFERENCE P2 and P4 — each adding the
|
|
43
|
+
// one agency type in front of its author at that moment. A hand-kept list is
|
|
44
|
+
// correct for whatever you were last thinking about.
|
|
45
|
+
//
|
|
46
|
+
// THE ASYMMETRY THAT MAKES THIS TRACTABLE. The mind's own types are a CLOSED
|
|
47
|
+
// set: it knows its own anatomy. The world's types are OPEN and unknowable —
|
|
48
|
+
// that is the whole point of a container anything can rent. So enumerate the
|
|
49
|
+
// self, and let everything else be world by default. Openness is preserved
|
|
50
|
+
// exactly where it must be: a host introduces any entity type, any shape, and
|
|
51
|
+
// the mind perceives it, with no act narrowed and no schema gated.
|
|
52
|
+
//
|
|
53
|
+
// Two sources, unioned:
|
|
54
|
+
// • MIND_OWN_ENTITY_TYPES — the anatomy this package ships.
|
|
55
|
+
// • each registered engine's `writes` — so a tenant that brings its OWN
|
|
56
|
+
// cognitive engine declares its own machinery beside the code that writes
|
|
57
|
+
// it, and gets this right without editing the container.
|
|
58
|
+
//
|
|
59
|
+
// The guard that keeps it true is behavioural, not a source scanner:
|
|
60
|
+
// `sense.boundary.test.ts` boots a mind into an empty world and asserts it
|
|
61
|
+
// perceives nothing. A new engine writing a new undeclared type fails it.
|
|
62
|
+
// ─────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
import { CONSEQUENCE_TYPE, ENACTED_TYPE } from '#agency/consequence'
|
|
65
|
+
import { AVAILABILITY_ENTITY_TYPE } from '#agency/schemas/repertoire'
|
|
66
|
+
import { REVOCATION_TYPE } from '#agency/revocation'
|
|
67
|
+
import { SETTLEMENT_TYPE } from '#agency/settlement'
|
|
68
|
+
import { ACTION_RECORD_TYPE } from '#faculties/executive.engine/action.record'
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Entity types that ARE the mind — written by its own engines about its own
|
|
72
|
+
* operation. Not perceptible: a mind does not encounter its own machinery as an
|
|
73
|
+
* event in the world.
|
|
74
|
+
*
|
|
75
|
+
* Grouped by the faculty that writes them so a reader can check the list
|
|
76
|
+
* against the anatomy. Everything absent here is world.
|
|
77
|
+
*/
|
|
78
|
+
export const MIND_OWN_ENTITY_TYPES: ReadonlySet<string> = new Set([
|
|
79
|
+
// ── perception ────────────────────────────────────────────────
|
|
80
|
+
// Percepts about percepts are the original feedback loop this guarded.
|
|
81
|
+
'percept', 'percept.social',
|
|
82
|
+
|
|
83
|
+
// ── attention, interoception, control ─────────────────────────
|
|
84
|
+
'interoception', 'attention.focus', 'attention.demand',
|
|
85
|
+
'task.focus', 'decision.record', 'self_observation',
|
|
86
|
+
// What became of what it did. Undeclared, a mind perceives its own history as
|
|
87
|
+
// events in the world — which is how #127 was caught, recursively.
|
|
88
|
+
ACTION_RECORD_TYPE,
|
|
89
|
+
|
|
90
|
+
// ── memory ────────────────────────────────────────────────────
|
|
91
|
+
'working_memory.item', 'episodic_memory', 'spaced_repetition_record',
|
|
92
|
+
'belief', 'belief.integrate',
|
|
93
|
+
|
|
94
|
+
// ── deliberative structure ────────────────────────────────────
|
|
95
|
+
'goal', 'plan', 'plan.prior',
|
|
96
|
+
|
|
97
|
+
// ── narrative + metacognition ─────────────────────────────────
|
|
98
|
+
'narrative_chapter', 'introspection', 'self_narrative', 'cognitive_bias',
|
|
99
|
+
'calibration.state', 'executive.summary', 'executive.cache',
|
|
100
|
+
|
|
101
|
+
// ── affect + social models ────────────────────────────────────
|
|
102
|
+
// A dossier is the mind's record OF someone, not the someone. The person
|
|
103
|
+
// arrives through audition and social perception; the dossier is what the
|
|
104
|
+
// mind then holds about them, and re-perceiving it double-counts.
|
|
105
|
+
'affect.blends', 'empathic_state', 'attachment.bond',
|
|
106
|
+
'theory_of_mind', 'reputation',
|
|
107
|
+
'known-entity', 'known-entity-alias',
|
|
108
|
+
|
|
109
|
+
// ── conversation records ──────────────────────────────────────
|
|
110
|
+
// SocialPerception is the sense that owns these (it reads
|
|
111
|
+
// `conversation.received` and emits `percept.social` from it, then sweeps it).
|
|
112
|
+
// Exteroception perceiving them too was the same turn counted twice.
|
|
113
|
+
'conversation.received', 'conversation.sent',
|
|
114
|
+
|
|
115
|
+
// ── agency ────────────────────────────────────────────────────
|
|
116
|
+
// The whole pipeline: the field it synthesizes, what it committed to, what
|
|
117
|
+
// came back, what it learned, and what it merely imagined.
|
|
118
|
+
'affordance', 'agency.intent', 'agency.outcome', 'agency.skill',
|
|
119
|
+
'agency.schema', 'ideomotor.intent',
|
|
120
|
+
// What it HAS DONE — the enaction footprint satiation reads to know it has
|
|
121
|
+
// already tried this. Undeclared, it was the loudest thing in the mind's
|
|
122
|
+
// perceptual field: measured on a live Will, `New agency.enacted: agency-
|
|
123
|
+
// enacted-discord_lookup_…` sat at salience 0.5, ABOVE every real percept,
|
|
124
|
+
// and each enaction produced two of them — one when the record appeared, one
|
|
125
|
+
// when the retention sweep removed it. A mind watching its own footprints
|
|
126
|
+
// being laid down and swept away, and calling that the world.
|
|
127
|
+
ENACTED_TYPE,
|
|
128
|
+
// What it may currently do, and why not. The availability ledger is empty
|
|
129
|
+
// until a refusal lands (POLICY_REAFFERENCE P2), which is exactly why nothing
|
|
130
|
+
// caught it: a mind that has never been told no writes none of these, so the
|
|
131
|
+
// quiet path and every test that stays on it are blind to the omission. Give
|
|
132
|
+
// a Will a PolicyArbiter that refuses, and it starts perceiving its own
|
|
133
|
+
// permissions changing as events in the world.
|
|
134
|
+
AVAILABILITY_ENTITY_TYPE,
|
|
135
|
+
'action.unresolved', 'action.unaddressed',
|
|
136
|
+
CONSEQUENCE_TYPE, // forward-model records (EXAFFERENCE P1/P2)
|
|
137
|
+
REVOCATION_TYPE, // commitment tombstones (EXAFFERENCE P4)
|
|
138
|
+
SETTLEMENT_TYPE, // verdicts System 2 reached — having thought about it
|
|
139
|
+
|
|
140
|
+
// ── substrate ─────────────────────────────────────────────────
|
|
141
|
+
// Its configuration and its identity are constitutive of it, not events in
|
|
142
|
+
// its world. It had been perceiving both.
|
|
143
|
+
'engine.config', 'will.identity', 'effector.created',
|
|
144
|
+
'dream.activity',
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
/** An engine that declares the entity types it writes about the mind itself. */
|
|
148
|
+
export interface DeclaresWrites {
|
|
149
|
+
/**
|
|
150
|
+
* Entity types this engine writes as part of the mind's own operation.
|
|
151
|
+
*
|
|
152
|
+
* Declare here anything the mind should NOT re-encounter through its outward
|
|
153
|
+
* senses. Omit it for engines that write about the WORLD — a host engine
|
|
154
|
+
* maintaining rooms, documents, or sensor readings wants those perceived, and
|
|
155
|
+
* silence is the right default for them.
|
|
156
|
+
*/
|
|
157
|
+
readonly writes?: readonly string[]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The full endogenous set for a given assembly: the shipped anatomy plus
|
|
162
|
+
* whatever the registered engines declare.
|
|
163
|
+
*
|
|
164
|
+
* Cheap enough to call per tick, but Exteroception memoizes on the engine list
|
|
165
|
+
* so a 50-engine union isn't rebuilt in the perceptual hot path.
|
|
166
|
+
*/
|
|
167
|
+
export function endogenousTypes( engines: readonly DeclaresWrites[] ): ReadonlySet<string> {
|
|
168
|
+
let extra: Set<string> | null = null
|
|
169
|
+
for( const e of engines )
|
|
170
|
+
for( const t of e.writes ?? [] )
|
|
171
|
+
if( !MIND_OWN_ENTITY_TYPES.has( t ) )
|
|
172
|
+
( extra ??= new Set() ).add( t )
|
|
173
|
+
|
|
174
|
+
if( !extra ) return MIND_OWN_ENTITY_TYPES
|
|
175
|
+
return new Set([ ...MIND_OWN_ENTITY_TYPES, ...extra ])
|
|
176
|
+
}
|