@mindot/will 0.1.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.
Files changed (178) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +777 -0
  3. package/dist/index.d.ts +8144 -0
  4. package/dist/index.js +26231 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +73 -0
  7. package/src/cognition/agency/access.grants.ts +59 -0
  8. package/src/cognition/agency/competence.codec.ts +89 -0
  9. package/src/cognition/agency/engines/action.selector.ts +462 -0
  10. package/src/cognition/agency/engines/affordance.synthesizer.ts +384 -0
  11. package/src/cognition/agency/engines/deliberation.engine.ts +241 -0
  12. package/src/cognition/agency/engines/instruction.intake.ts +54 -0
  13. package/src/cognition/agency/engines/motor.schema.executor.ts +532 -0
  14. package/src/cognition/agency/engines/reafference.engine.ts +241 -0
  15. package/src/cognition/agency/execution.primitives.ts +118 -0
  16. package/src/cognition/agency/index.ts +58 -0
  17. package/src/cognition/agency/proactive.communicator.ts +274 -0
  18. package/src/cognition/agency/reconcile.learning.ts +82 -0
  19. package/src/cognition/agency/schemas/external.ts +59 -0
  20. package/src/cognition/agency/schemas/innate.ts +111 -0
  21. package/src/cognition/agency/schemas/repertoire.ts +246 -0
  22. package/src/cognition/agency/selection.scoring.ts +191 -0
  23. package/src/cognition/agency/types.ts +165 -0
  24. package/src/cognition/bus.ts +391 -0
  25. package/src/cognition/completion.inbox.ts +86 -0
  26. package/src/cognition/config.mirror.entities.ts +475 -0
  27. package/src/cognition/conversation.memory.ts +81 -0
  28. package/src/cognition/event.log.ts +116 -0
  29. package/src/cognition/event.schemas.ts +568 -0
  30. package/src/cognition/faculties/aesthetic.evaluator.ts +313 -0
  31. package/src/cognition/faculties/affective.blender.ts +562 -0
  32. package/src/cognition/faculties/attachment.evaluator.ts +432 -0
  33. package/src/cognition/faculties/attention.allocator.ts +422 -0
  34. package/src/cognition/faculties/autobiographical.narrator.ts +300 -0
  35. package/src/cognition/faculties/bias.detector.ts +363 -0
  36. package/src/cognition/faculties/circadian.oscillator.ts +294 -0
  37. package/src/cognition/faculties/confidence.calibrator.ts +324 -0
  38. package/src/cognition/faculties/dream.simulator.ts +251 -0
  39. package/src/cognition/faculties/empathy.simulator.ts +253 -0
  40. package/src/cognition/faculties/energy.regulator.ts +320 -0
  41. package/src/cognition/faculties/episodic.consolidator.ts +728 -0
  42. package/src/cognition/faculties/executive.engine/commands.ts +459 -0
  43. package/src/cognition/faculties/executive.engine/config.ts +46 -0
  44. package/src/cognition/faculties/executive.engine/context.ts +605 -0
  45. package/src/cognition/faculties/executive.engine/deferred.effects.ts +104 -0
  46. package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +60 -0
  47. package/src/cognition/faculties/executive.engine/effort.gate.ts +114 -0
  48. package/src/cognition/faculties/executive.engine/engine.ts +1028 -0
  49. package/src/cognition/faculties/executive.engine/escalation.buffer.ts +90 -0
  50. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +271 -0
  51. package/src/cognition/faculties/executive.engine/facet.ts +628 -0
  52. package/src/cognition/faculties/executive.engine/gating.ts +222 -0
  53. package/src/cognition/faculties/executive.engine/index.ts +6 -0
  54. package/src/cognition/faculties/executive.engine/messages.ts +102 -0
  55. package/src/cognition/faculties/executive.engine/parser.ts +380 -0
  56. package/src/cognition/faculties/executive.engine/prompt.factory.ts +1053 -0
  57. package/src/cognition/faculties/executive.engine/types.ts +323 -0
  58. package/src/cognition/faculties/exteroception.ts +338 -0
  59. package/src/cognition/faculties/forgetting.curve.ts +202 -0
  60. package/src/cognition/faculties/frustration.evaluator.ts +280 -0
  61. package/src/cognition/faculties/goal.manager.ts +1008 -0
  62. package/src/cognition/faculties/inhibition.controller.ts +351 -0
  63. package/src/cognition/faculties/interoception.ts +417 -0
  64. package/src/cognition/faculties/introspection.engine.ts +246 -0
  65. package/src/cognition/faculties/known.entity.tracker.ts +439 -0
  66. package/src/cognition/faculties/loss.evaluator.ts +224 -0
  67. package/src/cognition/faculties/moral.evaluator.ts +437 -0
  68. package/src/cognition/faculties/novelty.detector.ts +254 -0
  69. package/src/cognition/faculties/persona.consolidator.ts +845 -0
  70. package/src/cognition/faculties/planning.engine/engine.ts +859 -0
  71. package/src/cognition/faculties/planning.engine/plan.frontier.ts +83 -0
  72. package/src/cognition/faculties/planning.engine/plan.store.ts +174 -0
  73. package/src/cognition/faculties/planning.engine/plan.supervision.ts +494 -0
  74. package/src/cognition/faculties/planning.engine/types.ts +143 -0
  75. package/src/cognition/faculties/reputation.tracker.ts +294 -0
  76. package/src/cognition/faculties/reward.evaluator.ts +304 -0
  77. package/src/cognition/faculties/self.model.updater.ts +677 -0
  78. package/src/cognition/faculties/semantic.engine/clustering.ts +673 -0
  79. package/src/cognition/faculties/semantic.engine/index.ts +6 -0
  80. package/src/cognition/faculties/semantic.engine/integrator.ts +750 -0
  81. package/src/cognition/faculties/semantic.engine/types.ts +53 -0
  82. package/src/cognition/faculties/sleep.pressure.regulator.ts +262 -0
  83. package/src/cognition/faculties/social.perception.ts +333 -0
  84. package/src/cognition/faculties/spaced.repetition.ts +605 -0
  85. package/src/cognition/faculties/stress.regulator.ts +375 -0
  86. package/src/cognition/faculties/task.switcher.ts +239 -0
  87. package/src/cognition/faculties/theory.of.mind.ts +333 -0
  88. package/src/cognition/faculties/threat.evaluator.ts +312 -0
  89. package/src/cognition/faculties/working.memory.ts +406 -0
  90. package/src/cognition/generative.model.ts +344 -0
  91. package/src/cognition/heartbeat.ts +70 -0
  92. package/src/cognition/index.ts +291 -0
  93. package/src/cognition/instruction.handler.ts +146 -0
  94. package/src/cognition/memory/index.ts +35 -0
  95. package/src/cognition/memory/vector.adapter.ts +316 -0
  96. package/src/cognition/memory/vector.content.ts +47 -0
  97. package/src/cognition/memory/vector.embedder.ts +212 -0
  98. package/src/cognition/memory/vector.index.ts +459 -0
  99. package/src/cognition/memory/vector.types.ts +65 -0
  100. package/src/cognition/orchestrator.ts +142 -0
  101. package/src/cognition/persona.prior.ts +265 -0
  102. package/src/cognition/schema.registry.ts +145 -0
  103. package/src/cognition/senses/audition.engine/engine.ts +976 -0
  104. package/src/cognition/senses/audition.engine/salience.ts +59 -0
  105. package/src/cognition/senses/base.sense.engine.ts +117 -0
  106. package/src/cognition/senses/gustation.engine.ts +24 -0
  107. package/src/cognition/senses/index.ts +172 -0
  108. package/src/cognition/senses/olfaction.engine.ts +24 -0
  109. package/src/cognition/senses/somatosensation.engine.ts +23 -0
  110. package/src/cognition/senses/vision.engine.ts +23 -0
  111. package/src/cognition/types.ts +63 -0
  112. package/src/cognition/utilities/token.tracker.ts +492 -0
  113. package/src/core/abstracts.ts +60 -0
  114. package/src/core/async.engine.ts +539 -0
  115. package/src/core/clock.ts +196 -0
  116. package/src/core/completion.recorder.ts +122 -0
  117. package/src/core/conflict.detector.ts +140 -0
  118. package/src/core/distributed.ts +626 -0
  119. package/src/core/event.bus.ts +292 -0
  120. package/src/core/inbound.recorder.ts +100 -0
  121. package/src/core/index.ts +167 -0
  122. package/src/core/logger.ts +85 -0
  123. package/src/core/metrics.ts +107 -0
  124. package/src/core/orchestrator.ts +625 -0
  125. package/src/core/replay.ts +589 -0
  126. package/src/core/scenario.ts +120 -0
  127. package/src/core/serialization.ts +448 -0
  128. package/src/core/simulation.ts +197 -0
  129. package/src/core/snapshot.manager.ts +293 -0
  130. package/src/core/state.manager.ts +283 -0
  131. package/src/core/types.ts +274 -0
  132. package/src/core/utils.ts +82 -0
  133. package/src/core/wall.clock.ts +23 -0
  134. package/src/extensions/livestream.ext.ts +570 -0
  135. package/src/extensions/time.ext.ts +339 -0
  136. package/src/index.ts +91 -0
  137. package/src/llm/gate.ts +140 -0
  138. package/src/llm/index.ts +716 -0
  139. package/src/llm/summarizer.ts +170 -0
  140. package/src/llm/wire.contracts.ts +67 -0
  141. package/src/pma/eval.ts +651 -0
  142. package/src/pma/index.ts +1082 -0
  143. package/src/profiles/built-in.ts +7 -0
  144. package/src/profiles/companion.ts +28 -0
  145. package/src/profiles/company-brain.ts +54 -0
  146. package/src/profiles/customer-service.ts +30 -0
  147. package/src/profiles/game-npc.ts +20 -0
  148. package/src/profiles/index.ts +42 -0
  149. package/src/profiles/smart-home.ts +34 -0
  150. package/src/runners/coherence.runner.ts +49 -0
  151. package/src/runners/outreach.runner.ts +158 -0
  152. package/src/runners/social.runner.ts +182 -0
  153. package/src/runners/thin-shim.runner.ts +195 -0
  154. package/src/sdk/will.ts +334 -0
  155. package/src/stem/assembly.audit.ts +88 -0
  156. package/src/stem/distribution.ts +203 -0
  157. package/src/stem/guards/identity.coherence.ts +152 -0
  158. package/src/stem/guards/identity.guard.ts +227 -0
  159. package/src/stem/index.ts +1145 -0
  160. package/src/stem/mind.ts +1090 -0
  161. package/src/stem/tracts/ack.reconciler.ts +55 -0
  162. package/src/stem/tracts/biography.writer.ts +190 -0
  163. package/src/stem/tracts/effector.controller.ts +143 -0
  164. package/src/stem/tracts/health.reporter.ts +113 -0
  165. package/src/stem/tracts/inbound.queue.ts +54 -0
  166. package/src/stem/tracts/outbox.controller.ts +167 -0
  167. package/src/stem/tracts/outbox.writer.ts +133 -0
  168. package/src/stem/tracts/pma.controller.ts +109 -0
  169. package/src/stem/tracts/replay.controller.ts +177 -0
  170. package/src/stem/tracts/sensory.controller.ts +168 -0
  171. package/src/stem/tracts/session.logger.ts +157 -0
  172. package/src/stem/tracts/transport/index.ts +38 -0
  173. package/src/stem/tracts/transport/loopback.transport.ts +86 -0
  174. package/src/stem/tracts/transport/socketio.transport.ts +200 -0
  175. package/src/stem/tracts/transport/stream.transport.ts +152 -0
  176. package/src/stem/tracts/transport/types.ts +193 -0
  177. package/src/stem/tracts/transport.controller.ts +380 -0
  178. package/src/types.ts +116 -0
@@ -0,0 +1,323 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/executive.engine/types.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ import type { CognitiveBus } from '#cognition/bus'
6
+ import type { PlanStep } from '#cognition/faculties/planning.engine/engine'
7
+
8
+ // ── Full executive output ────────────────────────────────────
9
+
10
+ export interface ExecutiveOutputFull {
11
+ actions: Array<{ type: string; reasoning: string; expectedOutcome: string; target?: string }>
12
+ reasoning: string
13
+ confidence: number
14
+ /** Plans — the executive controls lifecycle via status + action fields */
15
+ plans?: ExecutivePlanOutput[]
16
+ newBeliefs?: Array<{
17
+ statement: string
18
+ category: string
19
+ confidence: number
20
+ /**
21
+ * Evidence strength — replaces the hallucination-prone numeric count.
22
+ * The LLM picks a categorical label it can honestly assign; the runtime
23
+ * maps it to a numeric `supportingEpisodes` value for the belief store.
24
+ */
25
+ evidence: 'single_observation' | 'recurring_pattern' | 'strong_pattern'
26
+ tags: string[]
27
+ }>
28
+ introspection?: {
29
+ explanation: string
30
+ identifiedBiases: string[]
31
+ lessonsLearned: string[]
32
+ recommendations: string[]
33
+ }
34
+ narrative?: string
35
+ narrativeThemes?: string[]
36
+ currentSelfView?: string
37
+ identityUpdates?: {
38
+ traits: Array<{ key: string; value: number }>
39
+ values: string[]
40
+ }
41
+ /**
42
+ * What the Will consciously learned about the *others* it is dealing with (the analogue
43
+ * of identityUpdates, but about someone/something else). `keid` is the referent from the
44
+ * known-entity dossier / "## People You Know" context. `name` is a learned identifying
45
+ * name; `learned` are facts (→ keid-tagged social beliefs, so they ride the memory
46
+ * pipeline); `feeling` is a felt valence toward them (a bounded nudge).
47
+ */
48
+ knownEntityUpdates?: Array<{
49
+ keid: string
50
+ name?: string
51
+ learned?: string[]
52
+ feeling?: number
53
+ }>
54
+ newGoals?: Array<{
55
+ description: string
56
+ priority: number
57
+ tags: string[]
58
+ completionType: string
59
+ completionCondition?: string
60
+ }>
61
+ goalsToAbandon?: Array<{ goalId: string; reason: string }>
62
+ goalsToReprioritize?: Array<{ goalId: string; newPriority: number; reason: string }>
63
+ selfObservations?: string[]
64
+ /**
65
+ * Plain-text reply from a conversation facet — populated by parseResponse()
66
+ * from the [REPLY_TEXT]...[/REPLY_TEXT] block.
67
+ * Only present in facet mode (AuditionEngine). Undefined for master cycles.
68
+ * Paragraphs (double-newline separated) map to separate reply bubbles.
69
+ */
70
+ replyText?: string
71
+ /**
72
+ * @deprecated Legacy JSON reply format — no longer emitted by conversation facets.
73
+ * Kept for backward compatibility with any tests/tooling that inspect parsed output.
74
+ */
75
+ conversationReplies?: Array<{
76
+ targetEntityId: string
77
+ targetEntityName: string
78
+ messages: string[]
79
+ }>
80
+ /**
81
+ * System 2 only — the distinct approaches the master generated and weighed before
82
+ * committing, retained for explainability/auditability (and a future regret /
83
+ * counterfactual substrate). Populated on the deliberate (propose→evaluate) path;
84
+ * undefined for System 1 (the fast single-shot).
85
+ */
86
+ consideredAlternatives?: string[]
87
+ }
88
+
89
+ // ── Ideation (System 2 propose pass) ─────────────────────────
90
+
91
+ /**
92
+ * One candidate approach from the deliberate path's ideation (propose) pass — a
93
+ * divergent option the master generated *before* deciding. The evaluate pass weighs
94
+ * these and commits; the chosen-against set is retained as `consideredAlternatives`.
95
+ */
96
+ export interface IdeationCandidate {
97
+ /** Short handle for the option (e.g. "ask for clarification"). */
98
+ approach: string
99
+ /** One-line description of what this option concretely entails. */
100
+ description: string
101
+ /** Its main upside. */
102
+ upside: string
103
+ /** Its main risk or downside. */
104
+ risk: string
105
+ }
106
+
107
+ /** Output of the ideation (propose) pass — a divergent candidate set, not a decision. */
108
+ export interface IdeationOutput {
109
+ candidates: IdeationCandidate[]
110
+ }
111
+
112
+ /**
113
+ * Plan output from the executive LLM.
114
+ *
115
+ * This is what the LLM produces in the [PLANS] tagged block.
116
+ * It includes lifecycle control fields (status, action, executionTier,
117
+ * expectedOutcome) that only the executive sets, plus the step structure
118
+ * that feeds into PlanningEngine's Plan type.
119
+ */
120
+ export interface ExecutivePlanOutput {
121
+ /**
122
+ * Target an EXISTING plan for validate/execute/revise/cancel. Omit on a fresh
123
+ * `draft` to create a new plan. Enables managing multiple plans per goal (P4);
124
+ * when omitted, the op falls back to the goal's active plan.
125
+ */
126
+ planId?: string
127
+ goalId: string
128
+ /** Lifecycle stage the executive is setting */
129
+ status: 'draft' | 'validated' | 'approved' | 'revised' | 'rejected'
130
+ /** What PlanningEngine should do with this plan */
131
+ action: 'draft' | 'validate' | 'execute' | 'revise' | 'cancel'
132
+ /** Concrete description of what successful completion looks like */
133
+ expectedOutcome?: string
134
+ /** The steps — mirrors PlanStep structure from PlanningEngine */
135
+ steps: Array<Pick<PlanStep, 'action' | 'description' | 'expectedOutcome' | 'estimatedDuration' | 'prerequisites'>>
136
+ estimatedCost: number
137
+ feasibility: number
138
+ }
139
+
140
+ // ── Minimal output from LLM (before tagged-block parsing) ────
141
+
142
+ export interface ExecutiveOutputMinimal {
143
+ actions: Array<{ type: string; reasoning: string; expectedOutcome: string; target?: string }>
144
+ reasoning: string
145
+ confidence: number
146
+ }
147
+
148
+ // ── Engine config ────────────────────────────────────────────
149
+
150
+ export interface ExecutiveEngineConfig {
151
+ executiveInterval?: number
152
+ cooldownTicks?: number
153
+ bus?: CognitiveBus
154
+ }
155
+
156
+ // ── Context ──────────────────────────────────────────────────
157
+
158
+ export type ExecutiveAttentionType = 'available' | 'full'
159
+ export interface ExecutiveContext {
160
+ identity: {
161
+ /** Canonical persona name — from WillConfig.name (e.g. "Aria", "Guard-1"). */
162
+ name: string
163
+ prompt: string
164
+ values: string[]
165
+ traits: Record<string, number>
166
+ /**
167
+ * Per-trait self-knowledge (personal baseline + recent-shift direction) accumulated
168
+ * by the self-model, for graded-salience options B (relative to my own norm) and C
169
+ * ("rising/easing lately"). Absent until the self-model has evaluated at least once.
170
+ */
171
+ traitStats?: Record<string, { mean: number; shiftDir: number; shiftTick: number }>
172
+ style: string
173
+ }
174
+ worldState: {
175
+ energyLevel: number
176
+ sleepPressure: number
177
+ stressLoad: number
178
+ circadianPhase: number
179
+ timeOfDay: number
180
+ /**
181
+ * Tonic threat level (0–1). Always present in the standing context so a
182
+ * *sustained* threat stays represented even after its events habituate out of
183
+ * the workspace — representation (this) is kept separate from attention
184
+ * (event salience). See reward/threat Option-B salience.
185
+ */
186
+ threatLevel: number
187
+ }
188
+ affect: {
189
+ dominantEmotion: string
190
+ valence: number
191
+ arousal: number
192
+ dominance: number
193
+ blends: string[]
194
+ }
195
+ goals: Array<{
196
+ id: string
197
+ description: string
198
+ priority: number
199
+ progress: number
200
+ status: string
201
+ deadline?: number
202
+ lastActionAttemptTick?: number
203
+ lastActionType?: string
204
+ }>
205
+ /**
206
+ * Active/known plans, read from persisted `plan` entities — gives the executive
207
+ * execution awareness: which plans exist per goal, their status and step
208
+ * progress, so it can target a specific plan by id when managing several. (P4)
209
+ */
210
+ plans: Array<{
211
+ id: string
212
+ goalId: string
213
+ status: string
214
+ executionTier: string
215
+ totalSteps: number
216
+ completedSteps: number
217
+ expectedOutcome: string
218
+ /** Causal link to the requester whose message spawned the plan (for scoping). */
219
+ requestingEntityId?: string
220
+ }>
221
+ /**
222
+ * Plan ids surfaced by the latest episodic recall (plan descriptors carry their
223
+ * planId) — drives recall-scoped plan awareness (AWARENESS Stage 2). Generalizes
224
+ * the scope filter from `requestingEntityId` to relevance.
225
+ */
226
+ relevantPlanIds: string[]
227
+ percepts: Array<{
228
+ category: string
229
+ summary: string
230
+ salience: number
231
+ }>
232
+ workingMemory: Array<{
233
+ type: string
234
+ summary: string
235
+ activation: number
236
+ }>
237
+ memories: Array<{
238
+ content: string
239
+ relevance: number
240
+ emotionalContext: string
241
+ /** Simulation tick when this memory was consolidated. Used to render approximate age. */
242
+ tick?: number
243
+ }>
244
+ beliefs: Array<{
245
+ statement: string
246
+ category: string
247
+ confidence: number
248
+ }>
249
+ /** How many beliefs exist but were not included due to the relevance cap. */
250
+ beliefsOmitted: number
251
+ /**
252
+ * Recent action outcomes — shows the executive what it already tried and whether it
253
+ * landed. Built from `decision.record` entities with an `actionStatus` set.
254
+ * Surfaces the Act→Confirm→Perceive feedback loop into the executive's reasoning.
255
+ */
256
+ recentActions: Array<{
257
+ /** Effector name that was invoked */
258
+ type: string
259
+ /** Lifecycle status set by ActionExecutor */
260
+ status: 'completed' | 'failed' | 'awaiting_host' | 'timed_out'
261
+ /** Tick the action was executed or dispatched */
262
+ tick: number
263
+ /** Short outcome description — truncated to 120 chars */
264
+ outcome: string
265
+ /** planId if this action came from a plan step */
266
+ planId?: string
267
+ }>
268
+ /** Behavioral disposition loaded from PMA at session start — stable per session. */
269
+ behavioralDisposition?: {
270
+ riskTolerance: number
271
+ explorationRate: number
272
+ impulsivity: number
273
+ }
274
+ /**
275
+ * How the Will has self-tuned its own faculties (the metacognition cycle's
276
+ * persona-prior), as first-person phrases — lets the deliberate self see its
277
+ * own accommodation. Omitted when no prior is active.
278
+ */
279
+ selfTuning?: string[]
280
+ /**
281
+ * What the Will knows about the others it has encountered — the known-entity dossier
282
+ * view, surfaced for reasoning. Joins theory-of-mind (what they seem to want/feel),
283
+ * reputation (trust/cooperativeness/reliability) and attachment (closeness) per `keid`,
284
+ * sorted by interaction recency, capped. `name` may be absent — the Will can know
285
+ * *someone* without yet knowing their name (provisional identity). `kind` is `sentient`
286
+ * for everything modelled here today; `thing` arrives with the generalised dossier.
287
+ * Omitted when the Will knows no one.
288
+ */
289
+ knownEntities?: Array<{
290
+ keid: string
291
+ kind: 'sentient' | 'thing'
292
+ name?: string
293
+ intention?: string
294
+ emotion?: string
295
+ trust?: number
296
+ cooperativeness?: number
297
+ closeness?: number
298
+ reliability?: number
299
+ confidence?: number
300
+ }>
301
+ /**
302
+ * What the Will is currently focused on (task.switcher) and how committed — surfaces
303
+ * task-persistence so the deliberate self weighs the cost of switching away. The
304
+ * `switchCost` (= effective `baseSwitchCost`, conscientiousness-developable, #28) scales
305
+ * how strong the pull to stay is. Omitted when nothing is in focus.
306
+ */
307
+ currentFocus?: {
308
+ goalId: string
309
+ goalDescription?: string
310
+ focusTicks: number
311
+ switchCost: number
312
+ }
313
+ }
314
+
315
+ // ── Pending message ──────────────────────────────────────────
316
+
317
+ export interface PendingMessage {
318
+ id: string
319
+ content: string
320
+ sender: string
321
+ senderId: string
322
+ tick: number
323
+ }
@@ -0,0 +1,338 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/faculties/exteroception.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * Exteroception — processes external world events into structured percepts.
7
+ *
8
+ * Scans the event bus and entity space for:
9
+ * - New entities entering the world
10
+ * - Changes to existing entities
11
+ * - Explicit events (messages, notifications, environmental changes)
12
+ * - Other agents' observable actions
13
+ *
14
+ * Each percept is an entity with salience tagging, enabling downstream
15
+ * engines (Attention, Affective, Memory) to prioritize processing.
16
+ *
17
+ * Part of Shard 0 (Perceptual Layer) — runs every tick, synchronous.
18
+ */
19
+
20
+ import type {
21
+ Duration,
22
+ Tick,
23
+ Timestamp,
24
+ SimulationContext,
25
+ ReadonlySimulationState,
26
+ StateCommands,
27
+ SimulationEvent,
28
+ SimulationEntity,
29
+ } from '#core/types'
30
+ import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
31
+ import type { CognitiveEventSchema } from '#cognition/schema.registry'
32
+ import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
33
+ import { GenerativeModel } from '#cognition/generative.model'
34
+
35
+ export interface ExteroceptionConfig {
36
+ /** Maximum percepts to produce per tick */
37
+ maxPerceptsPerTick?: number
38
+ /** Default salience for unmarked percepts */
39
+ defaultSalience?: number
40
+ /** Whether to emit percept events */
41
+ emitPerceptEvents?: boolean
42
+ /** Entity types to always treat as high-salience */
43
+ highPriorityTypes?: string[]
44
+ bus?: CognitiveBus
45
+ }
46
+
47
+ interface RawPercept {
48
+ entityId: string
49
+ changeType: 'appeared' | 'modified' | 'removed'
50
+ salience: number
51
+ category: string
52
+ summary: string
53
+ }
54
+
55
+ // Skip internal entities — they're not external percepts.
56
+ // Percepts about percepts create a feedback loop that floods
57
+ // working memory and episodic storage with noise.
58
+ const internalTypes = new Set([
59
+ 'percept', 'percept.social', 'working_memory.item',
60
+ 'interoception', 'attention.focus', 'decision.record',
61
+ 'task.focus', 'self_observation',
62
+ 'goal', 'belief', 'plan', 'narrative_chapter',
63
+ 'introspection', 'self_narrative', 'cognitive_bias',
64
+ 'effector.created', 'empathic_state', 'attachment.bond',
65
+ 'theory_of_mind', 'reputation', 'episodic_memory',
66
+ // Internal state entities written by our own engines — not external events
67
+ 'affect.blends', 'executive.summary',
68
+ ])
69
+
70
+ export class Exteroception implements SimulationEngine, CognitiveEngine {
71
+ readonly name = 'exteroception'
72
+
73
+ private _maxPerceptsPerTick: number
74
+ private _defaultSalience: number
75
+ private _emitPerceptEvents: boolean
76
+ private _highPriorityTypes: Set<string>
77
+ private _previousEntityVersions = new Map<string, number>() // entityId → updatedAt
78
+
79
+ private _bus: CognitiveBus | null = null
80
+
81
+ private readonly _model = new GenerativeModel()
82
+
83
+
84
+ constructor( config: ExteroceptionConfig = {} ){
85
+ this._bus = config.bus ?? null
86
+ this._maxPerceptsPerTick = config.maxPerceptsPerTick ?? 50
87
+ this._defaultSalience = config.defaultSalience ?? 0.3
88
+ this._emitPerceptEvents = config.emitPerceptEvents ?? true
89
+ this._highPriorityTypes = new Set( config.highPriorityTypes ?? [
90
+ 'message', 'notification', 'alert', 'threat', 'goal',
91
+ ])
92
+ }
93
+ attachBus( bus: CognitiveBus ): void { this._bus = bus }
94
+
95
+ // ── Engine interface ─────────────────────────────────────
96
+
97
+ subscribes(): string[] { return ['executive.prediction.formed'] }
98
+ publishes(): CognitiveEventSchema[] {
99
+ return [{ type: 'percept.category.updated', version: 1, validate: () => null }]
100
+ }
101
+
102
+ onCognitiveEvent( e: CognitiveEvent ): StateCommands | void {
103
+ this._model.observe( e.type, e.salience )
104
+ if( e.type === 'executive.prediction.formed' ){
105
+ const p = e.payload as { predictedDomains: string[]; confidence: number }
106
+ if( p.predictedDomains.includes('perception') )
107
+ this._model.setPrecision( 'percept.rate', 1.0 + p.confidence * 0.5 )
108
+ }
109
+ }
110
+
111
+ snapshot(): Record<string, unknown> { return {} }
112
+
113
+ async react(
114
+ _delta: Duration,
115
+ tick: Tick,
116
+ state: ReadonlySimulationState,
117
+ context: SimulationContext
118
+ ): Promise<EngineResult> {
119
+ const
120
+ events: Array<Omit<SimulationEvent, 'id' | 'timestamp' | 'tick'>> = [],
121
+ commands: StateCommands = { set: [], delete: [], metrics: [] }
122
+
123
+ const rawPercepts = this._scanWorld( state )
124
+
125
+ // Cap percepts per tick
126
+ const capped = rawPercepts.slice( 0, this._maxPerceptsPerTick )
127
+
128
+ // Convert raw percepts to entities
129
+ for( let i = 0; i < capped.length; i++ ){
130
+ const rp = capped[i]!
131
+
132
+ const perceptEntity = {
133
+ id: `percept-${tick}-${i}`,
134
+ type: 'percept',
135
+ metadata: {
136
+ entityId: rp.entityId,
137
+ changeType: rp.changeType,
138
+ salience: rp.salience,
139
+ category: rp.category,
140
+ summary: rp.summary,
141
+ tick,
142
+ },
143
+ }
144
+
145
+ commands.set!.push( perceptEntity )
146
+
147
+ // Emit percept event for downstream engines
148
+ if( this._emitPerceptEvents )
149
+ events.push({
150
+ type: `percept.${rp.changeType}.${rp.category}`,
151
+ source: this.name,
152
+ payload: {
153
+ entityId: rp.entityId,
154
+ salience: rp.salience,
155
+ category: rp.category,
156
+ summary: rp.summary,
157
+ },
158
+ })
159
+ }
160
+
161
+ // Clean up old percepts from previous ticks (keep last 2 ticks of percepts)
162
+ commands.delete = this._collectStalePerceptIds( state, tick )
163
+
164
+ // Aggregate metrics
165
+ commands.metrics!.push(
166
+ [ 'perception.percepts_this_tick', capped.length ],
167
+ [ 'perception.total_entities_observed', state.entities.size ],
168
+ )
169
+
170
+
171
+ // Phase C + F: publish cognitive event — gated by prediction error
172
+ const _bus = this._bus
173
+ if( _bus && capped.length > 0 ){
174
+ const predErr = this._model.observe( 'percept.rate', capped.length )
175
+ if( !predErr.gated )
176
+ _bus.publish({ type: 'percept.batch.ingested', version: 1, sourceEngine: this.name, salience: Math.max( 0.2, predErr.salience ), payload: { count: capped.length } })
177
+ }
178
+
179
+ // percept.category.updated — one event per distinct category so subscribers
180
+ // (aesthetic.evaluator etc.) get a structured signal without scanning percepts
181
+ if( _bus && capped.length > 0 ){
182
+ const countByCategory = new Map<string, number>()
183
+ for( const rp of capped )
184
+ countByCategory.set( rp.category, ( countByCategory.get( rp.category ) ?? 0 ) + 1 )
185
+ for( const [ category, count ] of countByCategory )
186
+ _bus.publish({ type: 'percept.category.updated', version: 1, sourceEngine: this.name, salience: Math.min( 1, count * 0.15 + 0.2 ), payload: { category, count } })
187
+ }
188
+
189
+ return { events: events.length > 0 ? events : undefined, commands }
190
+ }
191
+
192
+ // ── Internal ─────────────────────────────────────────────
193
+
194
+ /**
195
+ * Scan the world for perceptible changes.
196
+ * Compares current entity state against previous versions.
197
+ */
198
+ // In _scanWorld(), replace the entity loop with this:
199
+
200
+ private _scanWorld( state: ReadonlySimulationState ): RawPercept[] {
201
+ const percepts: RawPercept[] = []
202
+ const currentIds = new Set<string>()
203
+
204
+ for( const [ id, entity ] of state.entities ){
205
+ currentIds.add( id )
206
+
207
+ if( internalTypes.has( entity.type ) ){
208
+ // Still track the version so we don't get "appeared" events
209
+ // when these internal entities are first created
210
+ this._previousEntityVersions.set( id, entity.updatedAt )
211
+ continue
212
+ }
213
+
214
+ const previousVersion = this._previousEntityVersions.get( id )
215
+
216
+ if( previousVersion === undefined ){
217
+ percepts.push({
218
+ entityId: id,
219
+ changeType: 'appeared',
220
+ salience: this._computeSalience( entity, 'appeared', state.time ),
221
+ category: entity.type,
222
+ summary: this._summarizeEntity( entity, 'appeared'),
223
+ })
224
+ }
225
+ else if( entity.updatedAt > previousVersion ){
226
+ percepts.push({
227
+ entityId: id,
228
+ changeType: 'modified',
229
+ salience: this._computeSalience( entity, 'modified', state.time ),
230
+ category: entity.type,
231
+ summary: this._summarizeEntity( entity, 'modified'),
232
+ })
233
+ }
234
+
235
+ this._previousEntityVersions.set( id, entity.updatedAt )
236
+ }
237
+
238
+ // Detect removed entities (not internal types)
239
+ for( const [ id ] of this._previousEntityVersions ){
240
+ if( !currentIds.has( id ) ){
241
+ // Only report removal of meaningful entities
242
+ // We can't check the type here since the entity is gone,
243
+ // but we can infer from the ID prefix
244
+ if( !id.startsWith('percept-')
245
+ && !id.startsWith('wm-')
246
+ && !id.startsWith('attention-')
247
+ && !id.startsWith('decision-')
248
+ && !id.startsWith('interoception-')
249
+ && !id.startsWith('task-')
250
+ && !id.startsWith('self-obs-') ){
251
+ percepts.push({
252
+ entityId: id,
253
+ changeType: 'removed',
254
+ salience: 0.4,
255
+ category: 'removed',
256
+ summary: `Entity removed: ${id}`,
257
+ })
258
+ }
259
+ this._previousEntityVersions.delete( id )
260
+ }
261
+ }
262
+
263
+ percepts.sort( ( a, b ) => b.salience - a.salience )
264
+ return percepts
265
+ }
266
+
267
+ /**
268
+ * Generate a meaningful summary for an entity.
269
+ * Instead of "New percept: percept-54-0", produce something useful.
270
+ */
271
+ private _summarizeEntity(
272
+ entity: Readonly<SimulationEntity>,
273
+ changeType: string
274
+ ): string {
275
+ const name = entity.metadata?.name as string | undefined
276
+ const description = entity.metadata?.description as string | undefined
277
+
278
+ if( name ){
279
+ return changeType === 'appeared'
280
+ ? `${name} appears` : `${name} changed`
281
+ }
282
+ if( description ){
283
+ return description.slice( 0, 100 )
284
+ }
285
+
286
+ return `New ${entity.type}: ${entity.id.slice(0, 30)}`
287
+ }
288
+
289
+ /**
290
+ * Compute salience of a percept based on entity characteristics.
291
+ */
292
+ private _computeSalience(
293
+ entity: Readonly<SimulationEntity>,
294
+ _changeType: string,
295
+ now: Timestamp
296
+ ): number {
297
+ let salience = this._defaultSalience
298
+
299
+ // High-priority types get baseline boost
300
+ if( this._highPriorityTypes.has( entity.type ) )
301
+ salience += 0.4
302
+
303
+ // Entity metadata can specify explicit salience
304
+ if( typeof entity.metadata?.salience === 'number')
305
+ salience = entity.metadata.salience
306
+
307
+ // Urgency metadata boosts salience
308
+ if( typeof entity.metadata?.urgency === 'number')
309
+ salience += entity.metadata.urgency * 0.3
310
+
311
+ // Recency boost — entities with recent updatedAt are more salient.
312
+ // Both sides are sim-time ms (updatedAt is stamped by StateManager from the
313
+ // sim clock), so recency is measured against sim-time, never wall-clock (R2).
314
+ const ageSeconds = ( now - entity.updatedAt ) / 1000
315
+ if( ageSeconds < 10 )
316
+ salience += 0.2
317
+ else if( ageSeconds < 60 )
318
+ salience += 0.1
319
+
320
+ return Math.min( 1, Math.max( 0, salience ) )
321
+ }
322
+
323
+ /**
324
+ * Collect IDs of stale percept entities to clean up.
325
+ */
326
+ private _collectStalePerceptIds( state: ReadonlySimulationState, currentTick: Tick ): string[] {
327
+ const stale: string[] = []
328
+
329
+ for( const [ id, entity ] of state.entities ){
330
+ if( entity.type === 'percept' && typeof entity.metadata?.tick === 'number'){
331
+ if( currentTick - entity.metadata.tick > 2 )
332
+ stale.push( id )
333
+ }
334
+ }
335
+
336
+ return stale
337
+ }
338
+ }