@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,391 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/bus.ts
3
+ // ─────────────────────────────────────────────────────────────
4
+
5
+ /**
6
+ * CognitiveBus — inter-engine communication layer.
7
+ *
8
+ * Distinct from the existing DefaultEventBus (simulation event notifications,
9
+ * tick-scoped, for SSE/session logging). This bus handles:
10
+ * - Typed CognitiveEvent delivery between engines
11
+ * - Per-engine FIFO ordering (KPN-derived guarantee)
12
+ * - Lamport logical clock for causal ordering
13
+ * - Schema validation at publish time via SchemaRegistry
14
+ * - Persistent event log before delivery
15
+ * - Salience scoring so subscribers can filter by significance
16
+ *
17
+ * Topic scheme: {engine}.{category}.{signal}
18
+ * e.g. 'stress.zone.critical', 'executive.interpretation.formed', 'clock.tick'
19
+ *
20
+ * Wildcard matching: 'stress.*' matches any topic starting with 'stress.'
21
+ *
22
+ * Two implementations:
23
+ * - DefaultCognitiveBus — queued delivery, flushed on demand (production)
24
+ * - SyncCognitiveBus — immediate synchronous delivery (test bus)
25
+ */
26
+
27
+ import { logger } from '#core/logger'
28
+ import { globalSchemaRegistry, type SchemaRegistry } from '#cognition/schema.registry'
29
+ import type { EventLog } from '#cognition/event.log'
30
+ import type { StateCommands } from '#core/types'
31
+ import { wallClock } from '#core/wall.clock'
32
+
33
+ // ── SimulationContext augmentation ───────────────────────────
34
+ // Adds cognitiveBus as an optional field on SimulationContext so
35
+ // engines can access it via context.cognitiveBus without touching core types.
36
+ declare module '#core/types' {
37
+ interface SimulationContext {
38
+ cognitiveBus?: CognitiveBus
39
+ }
40
+ }
41
+
42
+ // ── CognitiveEvent type ──────────────────────────────────────
43
+
44
+ export interface CognitiveEvent<T = unknown> {
45
+ readonly id: string
46
+ readonly type: string
47
+ readonly version: number
48
+ readonly sourceEngine: string
49
+ readonly sequenceNumber: number
50
+ readonly logicalTime: number
51
+ readonly wallTime: number
52
+ readonly salience: number
53
+ readonly payload: T
54
+ }
55
+
56
+ // ── Transport abstraction ────────────────────────────────────
57
+
58
+ export type CognitiveEventHandler = ( event: CognitiveEvent ) => StateCommands | void
59
+
60
+ export interface CognitiveBusTransport {
61
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler ): void
62
+ unsubscribe( engineId: string ): void
63
+ deliver( event: CognitiveEvent, matchedEngines: string[] ): void
64
+ flush(): void
65
+ }
66
+
67
+ // ── Subscription store ───────────────────────────────────────
68
+
69
+ interface Subscription {
70
+ engineId: string
71
+ topics: string[]
72
+ handler: CognitiveEventHandler
73
+ }
74
+
75
+ function topicMatches( pattern: string, topic: string ): boolean {
76
+ if( pattern === '*' ) return true // global workspace subscription
77
+ if( pattern === topic ) return true
78
+ if( pattern.endsWith('.*') ){
79
+ const prefix = pattern.slice( 0, -2 )
80
+ return topic === prefix || topic.startsWith( prefix + '.' )
81
+ }
82
+ return false
83
+ }
84
+
85
+ // ── Per-category inbox policy (Phase A) ─────────────────────
86
+ // critical — goal changes, executive broadcasts, effector events: never dropped
87
+ // metric — *.state.changed, clock.tick, high-frequency signals: drop-oldest on overflow
88
+
89
+ const METRIC_QUEUE_MAX = 500
90
+
91
+ function isCriticalEvent( type: string ): boolean {
92
+ return type.startsWith('executive.')
93
+ || type.startsWith('goal.')
94
+ || type.startsWith('effector.')
95
+ || type.startsWith('action.')
96
+ || type === 'engine.snapshot'
97
+ }
98
+
99
+ // ── In-process transport (production) ───────────────────────
100
+
101
+ export class InProcessCognitiveTransport implements CognitiveBusTransport {
102
+ private _subscriptions = new Map<string, Subscription>()
103
+ // Critical queue — unbounded, never dropped
104
+ private _criticalQueue: Array<{ event: CognitiveEvent; targets: string[] }> = []
105
+ // Metric queue — bounded with drop-oldest on overflow
106
+ private _metricQueue: Array<{ event: CognitiveEvent; targets: string[] }> = []
107
+ private _droppedCount = 0
108
+
109
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler ): void {
110
+ this._subscriptions.set( engineId, { engineId, topics, handler })
111
+ }
112
+
113
+ unsubscribe( engineId: string ): void {
114
+ this._subscriptions.delete( engineId )
115
+ }
116
+
117
+ deliver( event: CognitiveEvent, matchedEngines: string[] ): void {
118
+ if( isCriticalEvent( event.type ) ){
119
+ this._criticalQueue.push({ event, targets: matchedEngines })
120
+ } else {
121
+ if( this._metricQueue.length >= METRIC_QUEUE_MAX ){
122
+ this._metricQueue.shift() // drop oldest metric event
123
+ this._droppedCount++
124
+ if( this._droppedCount % 100 === 1 )
125
+ logger.warn(`[CognitiveBus] metric queue overflow — dropped ${this._droppedCount} events`)
126
+ }
127
+ this._metricQueue.push({ event, targets: matchedEngines })
128
+ }
129
+ }
130
+
131
+ flush(): void {
132
+ // Critical events delivered first, then metrics (preserves priority ordering)
133
+ const batch = [ ...this._criticalQueue.splice(0), ...this._metricQueue.splice(0) ]
134
+ for( const { event, targets } of batch ){
135
+ for( const engineId of targets ){
136
+ const sub = this._subscriptions.get( engineId )
137
+ if( sub ) sub.handler( event )
138
+ }
139
+ }
140
+ }
141
+
142
+ get droppedCount(): number { return this._droppedCount }
143
+
144
+ matchSubscribers( topic: string ): string[] {
145
+ const matched: string[] = []
146
+ for( const sub of this._subscriptions.values() ){
147
+ if( sub.topics.some( p => topicMatches( p, topic ) ) )
148
+ matched.push( sub.engineId )
149
+ }
150
+ return matched
151
+ }
152
+ }
153
+
154
+ // ── Synchronous transport (test bus) ────────────────────────
155
+
156
+ /**
157
+ * Delivers events immediately and synchronously in publish order.
158
+ * Required for deterministic unit and integration tests.
159
+ */
160
+ export class SyncCognitiveBusTransport implements CognitiveBusTransport {
161
+ private _subscriptions = new Map<string, Subscription>()
162
+
163
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler ): void {
164
+ this._subscriptions.set( engineId, { engineId, topics, handler })
165
+ }
166
+
167
+ unsubscribe( engineId: string ): void {
168
+ this._subscriptions.delete( engineId )
169
+ }
170
+
171
+ deliver( event: CognitiveEvent, matchedEngines: string[] ): void {
172
+ for( const engineId of matchedEngines ){
173
+ const sub = this._subscriptions.get( engineId )
174
+ if( sub ) sub.handler( event )
175
+ }
176
+ }
177
+
178
+ flush(): void { /* no-op — already delivered synchronously */ }
179
+
180
+ matchSubscribers( topic: string ): string[] {
181
+ const matched: string[] = []
182
+ for( const sub of this._subscriptions.values() ){
183
+ if( sub.topics.some( p => topicMatches( p, topic ) ) )
184
+ matched.push( sub.engineId )
185
+ }
186
+ return matched
187
+ }
188
+ }
189
+
190
+ // ── CognitiveBus interface ───────────────────────────────────
191
+
192
+ export type AcceptsVersionsFn = ( eventType: string ) => number[]
193
+
194
+ export interface CognitiveBus {
195
+ publish( event: Omit<CognitiveEvent, 'id' | 'sequenceNumber' | 'logicalTime' | 'wallTime'> ): void
196
+ /** acceptsVersions — optional per-subscriber version filter (Phase A). */
197
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler, acceptsVersions?: AcceptsVersionsFn ): void
198
+ unsubscribe( engineId: string ): void
199
+ flush(): void
200
+ /** Drain and return all StateCommands returned by onCognitiveEvent() handlers since last drain. */
201
+ drainCommands(): StateCommands[]
202
+ logicalTime: number
203
+ }
204
+
205
+ // ── DefaultCognitiveBus ──────────────────────────────────────
206
+
207
+ export class DefaultCognitiveBus implements CognitiveBus {
208
+ private _transport: InProcessCognitiveTransport
209
+ private _registry: SchemaRegistry
210
+ private _log: EventLog | null
211
+ private _lamportClock = 0
212
+ private _sequenceNumbers = new Map<string, number>()
213
+ private _counter = 0
214
+ private _pendingCommands: StateCommands[] = []
215
+
216
+ constructor(
217
+ transport: InProcessCognitiveTransport,
218
+ registry: SchemaRegistry = globalSchemaRegistry,
219
+ log: EventLog | null = null
220
+ ){
221
+ this._transport = transport
222
+ this._registry = registry
223
+ this._log = log
224
+ }
225
+
226
+ get logicalTime(): number { return this._lamportClock }
227
+
228
+ publish( partial: Omit<CognitiveEvent, 'id' | 'sequenceNumber' | 'logicalTime' | 'wallTime'> ): void {
229
+ if( this._registry.hasType( partial.type ) ){
230
+ const error = this._registry.validate( partial.type, partial.version, partial.payload )
231
+ if( error ) throw new Error(`CognitiveBus schema violation [${partial.type} v${partial.version}]: ${error}`)
232
+ }
233
+
234
+ const seq = ( this._sequenceNumbers.get( partial.sourceEngine ) ?? 0 ) + 1
235
+ this._sequenceNumbers.set( partial.sourceEngine, seq )
236
+ this._lamportClock++
237
+
238
+ const event: CognitiveEvent = {
239
+ ...partial,
240
+ id: `cog-${this._lamportClock}-${++this._counter}`,
241
+ sequenceNumber: seq,
242
+ logicalTime: this._lamportClock,
243
+ wallTime: wallClock(), // telemetry only; logicalTime is the deterministic clock
244
+ }
245
+
246
+ this._log?.append( event )
247
+
248
+ const targets = this._transport.matchSubscribers( event.type )
249
+ if( targets.length > 0 )
250
+ this._transport.deliver( event, targets )
251
+ }
252
+
253
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler, acceptsVersions?: AcceptsVersionsFn ): void {
254
+ this._transport.subscribe( engineId, topics, this._wrapHandler( handler, acceptsVersions ) )
255
+ }
256
+
257
+ unsubscribe( engineId: string ): void {
258
+ this._transport.unsubscribe( engineId )
259
+ }
260
+
261
+ flush(): void {
262
+ this._transport.flush()
263
+ }
264
+
265
+ drainCommands(): StateCommands[] {
266
+ const cmds = this._pendingCommands
267
+ this._pendingCommands = []
268
+ return cmds
269
+ }
270
+
271
+ private _wrapHandler( handler: CognitiveEventHandler, acceptsVersions?: AcceptsVersionsFn ): CognitiveEventHandler {
272
+ const registry = this._registry
273
+ return ( event: CognitiveEvent ) => {
274
+ let result: StateCommands | void = undefined
275
+ if( !acceptsVersions ){
276
+ result = handler( event )
277
+ } else {
278
+ const accepted = acceptsVersions( event.type )
279
+ if( accepted.length === 0 || accepted.includes( event.version ) ){
280
+ result = handler( event )
281
+ } else {
282
+ const migrated = registry.tryMigrate( event.type, event.version, event.payload )
283
+ if( migrated.ok && ( accepted.length === 0 || accepted.includes( migrated.targetVersion ) ) )
284
+ result = handler({ ...event, version: migrated.targetVersion, payload: migrated.payload })
285
+ }
286
+ }
287
+ if( result ) this._pendingCommands.push( result )
288
+ }
289
+ }
290
+ }
291
+
292
+ // ── SyncCognitiveBus ─────────────────────────────────────────
293
+
294
+ export class SyncCognitiveBus implements CognitiveBus {
295
+ private _transport: SyncCognitiveBusTransport
296
+ private _registry: SchemaRegistry
297
+ private _log: EventLog | null
298
+ private _lamportClock = 0
299
+ private _sequenceNumbers = new Map<string, number>()
300
+ private _counter = 0
301
+ private _pendingCommands: StateCommands[] = []
302
+
303
+ constructor(
304
+ transport: SyncCognitiveBusTransport,
305
+ registry: SchemaRegistry = globalSchemaRegistry,
306
+ log: EventLog | null = null
307
+ ){
308
+ this._transport = transport
309
+ this._registry = registry
310
+ this._log = log
311
+ }
312
+
313
+ get logicalTime(): number { return this._lamportClock }
314
+
315
+ publish( partial: Omit<CognitiveEvent, 'id' | 'sequenceNumber' | 'logicalTime' | 'wallTime'> ): void {
316
+ if( this._registry.hasType( partial.type ) ){
317
+ const error = this._registry.validate( partial.type, partial.version, partial.payload )
318
+ if( error ) throw new Error(`SyncCognitiveBus schema violation [${partial.type} v${partial.version}]: ${error}`)
319
+ }
320
+
321
+ const seq = ( this._sequenceNumbers.get( partial.sourceEngine ) ?? 0 ) + 1
322
+ this._sequenceNumbers.set( partial.sourceEngine, seq )
323
+ this._lamportClock++
324
+
325
+ const event: CognitiveEvent = {
326
+ ...partial,
327
+ id: `cog-${this._lamportClock}-${++this._counter}`,
328
+ sequenceNumber: seq,
329
+ logicalTime: this._lamportClock,
330
+ wallTime: wallClock(), // telemetry only; logicalTime is the deterministic clock
331
+ }
332
+
333
+ this._log?.append( event )
334
+
335
+ const targets = this._transport.matchSubscribers( event.type )
336
+ if( targets.length > 0 )
337
+ this._transport.deliver( event, targets )
338
+ }
339
+
340
+ subscribe( engineId: string, topics: string[], handler: CognitiveEventHandler, acceptsVersions?: AcceptsVersionsFn ): void {
341
+ this._transport.subscribe( engineId, topics, this._wrapHandler( handler, acceptsVersions ) )
342
+ }
343
+
344
+ unsubscribe( engineId: string ): void {
345
+ this._transport.unsubscribe( engineId )
346
+ }
347
+
348
+ flush(): void { /* no-op for sync transport */ }
349
+
350
+ drainCommands(): StateCommands[] {
351
+ const cmds = this._pendingCommands
352
+ this._pendingCommands = []
353
+ return cmds
354
+ }
355
+
356
+ private _wrapHandler( handler: CognitiveEventHandler, acceptsVersions?: AcceptsVersionsFn ): CognitiveEventHandler {
357
+ const registry = this._registry
358
+ return ( event: CognitiveEvent ) => {
359
+ let result: StateCommands | void = undefined
360
+ if( !acceptsVersions ){
361
+ result = handler( event )
362
+ } else {
363
+ const accepted = acceptsVersions( event.type )
364
+ if( accepted.length === 0 || accepted.includes( event.version ) ){
365
+ result = handler( event )
366
+ } else {
367
+ const migrated = registry.tryMigrate( event.type, event.version, event.payload )
368
+ if( migrated.ok && ( accepted.length === 0 || accepted.includes( migrated.targetVersion ) ) )
369
+ result = handler({ ...event, version: migrated.targetVersion, payload: migrated.payload })
370
+ }
371
+ }
372
+ if( result ) this._pendingCommands.push( result )
373
+ }
374
+ }
375
+ }
376
+
377
+ // ── Factory helpers ──────────────────────────────────────────
378
+
379
+ export function createProductionBus(
380
+ log: EventLog | null = null,
381
+ registry = globalSchemaRegistry
382
+ ): DefaultCognitiveBus {
383
+ return new DefaultCognitiveBus( new InProcessCognitiveTransport(), registry, log )
384
+ }
385
+
386
+ export function createTestBus(
387
+ log: EventLog | null = null,
388
+ registry = globalSchemaRegistry
389
+ ): SyncCognitiveBus {
390
+ return new SyncCognitiveBus( new SyncCognitiveBusTransport(), registry, log )
391
+ }
@@ -0,0 +1,86 @@
1
+ // ─────────────────────────────────────────────────────────────
2
+ // src/cognition/completion.inbox.ts — tick-boundary landing for async results
3
+ // ─────────────────────────────────────────────────────────────
4
+ //
5
+ // The architecture's rule is that state changes land at tick boundaries: engine
6
+ // commands go through the collect→commit pipeline, bus events queue and flush in
7
+ // Phase 2, and AsyncEngine results settle off-tick but convert to commands inside
8
+ // the next react(). Facet decisions were the one path that broke the rule — the
9
+ // direct subscriber callbacks (PlanningEngine plan mutations, AuditionEngine
10
+ // outbox writes) fired at raw LLM-promise resolution, i.e. at an arbitrary
11
+ // wall-clock moment that could interleave with a tick in flight.
12
+ //
13
+ // The CompletionInbox closes that seam. Async completion effects are enqueued
14
+ // as thunks at resolution time and applied by the CognitiveOrchestrator at the
15
+ // top of Phase 2 — the same tick-aligned point where the bus flushes. Result:
16
+ //
17
+ // • the frozen-snapshot premise holds — nothing mutates shared state while a
18
+ // tick's engines are reading;
19
+ // • a completion's landing tick is quantized: effects from work that resolved
20
+ // during tick N are visible to every engine at tick N+1, in FIFO order;
21
+ // • under replay, re-fed completions resolve deterministically, so enqueue
22
+ // order — and therefore landing order — reproduces exactly.
23
+ //
24
+ // This does NOT constrain facet spawning or reasoning: facets stay dynamically
25
+ // spawned, entity-scoped, and fully async. Only the *return path* is disciplined
26
+ // — the same contract every synchronous engine already obeys. Reply chunk
27
+ // streaming intentionally bypasses the inbox: chunks are client-facing flow,
28
+ // not simulation state, and stay real-time.
29
+ // ─────────────────────────────────────────────────────────────
30
+
31
+ import { logger } from '#core/logger'
32
+
33
+ interface PendingCompletion {
34
+ /** Provenance label for diagnostics — e.g. `facet-3:decision`. */
35
+ label: string
36
+ /** The deferred effect — runs at the next Phase-2 drain. */
37
+ apply: () => void
38
+ }
39
+
40
+ export class CompletionInbox {
41
+ private _queue: PendingCompletion[] = []
42
+
43
+ /** Number of completions waiting to land. */
44
+ get size(): number { return this._queue.length }
45
+
46
+ /**
47
+ * Stage a completion effect for the next tick boundary. Called from async
48
+ * resolution contexts (facet decision emission); never applies inline.
49
+ */
50
+ enqueue( label: string, apply: () => void ): void {
51
+ this._queue.push( { label, apply } )
52
+ }
53
+
54
+ /**
55
+ * Apply every staged completion in FIFO order. Called by the
56
+ * CognitiveOrchestrator at the top of Phase 2, before the bus flush — so any
57
+ * bus events a thunk publishes deliver in the same phase. A throwing thunk is
58
+ * isolated: it never blocks the rest of the queue or the tick.
59
+ *
60
+ * Thunks enqueued DURING the drain (e.g. a listener triggering another facet
61
+ * whose mock resolves synchronously) land next tick — the snapshot taken this
62
+ * drain cycle stays coherent.
63
+ */
64
+ drain( tick: number ): number {
65
+ if( this._queue.length === 0 ) return 0
66
+
67
+ const batch = this._queue
68
+ this._queue = []
69
+
70
+ for( const { label, apply } of batch ){
71
+ try { apply() }
72
+ catch( err ){
73
+ logger.error( `[completion-inbox] "${label}" failed while landing at tick ${tick}:`, err )
74
+ }
75
+ }
76
+
77
+ return batch.length
78
+ }
79
+
80
+ /** Drop staged completions (mind teardown). Returns how many were discarded. */
81
+ clear(): number {
82
+ const n = this._queue.length
83
+ this._queue = []
84
+ return n
85
+ }
86
+ }