@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.
- package/LICENSE +202 -0
- package/README.md +777 -0
- package/dist/index.d.ts +8144 -0
- package/dist/index.js +26231 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
- package/src/cognition/agency/access.grants.ts +59 -0
- package/src/cognition/agency/competence.codec.ts +89 -0
- package/src/cognition/agency/engines/action.selector.ts +462 -0
- package/src/cognition/agency/engines/affordance.synthesizer.ts +384 -0
- package/src/cognition/agency/engines/deliberation.engine.ts +241 -0
- package/src/cognition/agency/engines/instruction.intake.ts +54 -0
- package/src/cognition/agency/engines/motor.schema.executor.ts +532 -0
- package/src/cognition/agency/engines/reafference.engine.ts +241 -0
- package/src/cognition/agency/execution.primitives.ts +118 -0
- package/src/cognition/agency/index.ts +58 -0
- package/src/cognition/agency/proactive.communicator.ts +274 -0
- package/src/cognition/agency/reconcile.learning.ts +82 -0
- package/src/cognition/agency/schemas/external.ts +59 -0
- package/src/cognition/agency/schemas/innate.ts +111 -0
- package/src/cognition/agency/schemas/repertoire.ts +246 -0
- package/src/cognition/agency/selection.scoring.ts +191 -0
- package/src/cognition/agency/types.ts +165 -0
- package/src/cognition/bus.ts +391 -0
- package/src/cognition/completion.inbox.ts +86 -0
- package/src/cognition/config.mirror.entities.ts +475 -0
- package/src/cognition/conversation.memory.ts +81 -0
- package/src/cognition/event.log.ts +116 -0
- package/src/cognition/event.schemas.ts +568 -0
- package/src/cognition/faculties/aesthetic.evaluator.ts +313 -0
- package/src/cognition/faculties/affective.blender.ts +562 -0
- package/src/cognition/faculties/attachment.evaluator.ts +432 -0
- package/src/cognition/faculties/attention.allocator.ts +422 -0
- package/src/cognition/faculties/autobiographical.narrator.ts +300 -0
- package/src/cognition/faculties/bias.detector.ts +363 -0
- package/src/cognition/faculties/circadian.oscillator.ts +294 -0
- package/src/cognition/faculties/confidence.calibrator.ts +324 -0
- package/src/cognition/faculties/dream.simulator.ts +251 -0
- package/src/cognition/faculties/empathy.simulator.ts +253 -0
- package/src/cognition/faculties/energy.regulator.ts +320 -0
- package/src/cognition/faculties/episodic.consolidator.ts +728 -0
- package/src/cognition/faculties/executive.engine/commands.ts +459 -0
- package/src/cognition/faculties/executive.engine/config.ts +46 -0
- package/src/cognition/faculties/executive.engine/context.ts +605 -0
- package/src/cognition/faculties/executive.engine/deferred.effects.ts +104 -0
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +60 -0
- package/src/cognition/faculties/executive.engine/effort.gate.ts +114 -0
- package/src/cognition/faculties/executive.engine/engine.ts +1028 -0
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +90 -0
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +271 -0
- package/src/cognition/faculties/executive.engine/facet.ts +628 -0
- package/src/cognition/faculties/executive.engine/gating.ts +222 -0
- package/src/cognition/faculties/executive.engine/index.ts +6 -0
- package/src/cognition/faculties/executive.engine/messages.ts +102 -0
- package/src/cognition/faculties/executive.engine/parser.ts +380 -0
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +1053 -0
- package/src/cognition/faculties/executive.engine/types.ts +323 -0
- package/src/cognition/faculties/exteroception.ts +338 -0
- package/src/cognition/faculties/forgetting.curve.ts +202 -0
- package/src/cognition/faculties/frustration.evaluator.ts +280 -0
- package/src/cognition/faculties/goal.manager.ts +1008 -0
- package/src/cognition/faculties/inhibition.controller.ts +351 -0
- package/src/cognition/faculties/interoception.ts +417 -0
- package/src/cognition/faculties/introspection.engine.ts +246 -0
- package/src/cognition/faculties/known.entity.tracker.ts +439 -0
- package/src/cognition/faculties/loss.evaluator.ts +224 -0
- package/src/cognition/faculties/moral.evaluator.ts +437 -0
- package/src/cognition/faculties/novelty.detector.ts +254 -0
- package/src/cognition/faculties/persona.consolidator.ts +845 -0
- package/src/cognition/faculties/planning.engine/engine.ts +859 -0
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +83 -0
- package/src/cognition/faculties/planning.engine/plan.store.ts +174 -0
- package/src/cognition/faculties/planning.engine/plan.supervision.ts +494 -0
- package/src/cognition/faculties/planning.engine/types.ts +143 -0
- package/src/cognition/faculties/reputation.tracker.ts +294 -0
- package/src/cognition/faculties/reward.evaluator.ts +304 -0
- package/src/cognition/faculties/self.model.updater.ts +677 -0
- package/src/cognition/faculties/semantic.engine/clustering.ts +673 -0
- package/src/cognition/faculties/semantic.engine/index.ts +6 -0
- package/src/cognition/faculties/semantic.engine/integrator.ts +750 -0
- package/src/cognition/faculties/semantic.engine/types.ts +53 -0
- package/src/cognition/faculties/sleep.pressure.regulator.ts +262 -0
- package/src/cognition/faculties/social.perception.ts +333 -0
- package/src/cognition/faculties/spaced.repetition.ts +605 -0
- package/src/cognition/faculties/stress.regulator.ts +375 -0
- package/src/cognition/faculties/task.switcher.ts +239 -0
- package/src/cognition/faculties/theory.of.mind.ts +333 -0
- package/src/cognition/faculties/threat.evaluator.ts +312 -0
- package/src/cognition/faculties/working.memory.ts +406 -0
- package/src/cognition/generative.model.ts +344 -0
- package/src/cognition/heartbeat.ts +70 -0
- package/src/cognition/index.ts +291 -0
- package/src/cognition/instruction.handler.ts +146 -0
- package/src/cognition/memory/index.ts +35 -0
- package/src/cognition/memory/vector.adapter.ts +316 -0
- package/src/cognition/memory/vector.content.ts +47 -0
- package/src/cognition/memory/vector.embedder.ts +212 -0
- package/src/cognition/memory/vector.index.ts +459 -0
- package/src/cognition/memory/vector.types.ts +65 -0
- package/src/cognition/orchestrator.ts +142 -0
- package/src/cognition/persona.prior.ts +265 -0
- package/src/cognition/schema.registry.ts +145 -0
- package/src/cognition/senses/audition.engine/engine.ts +976 -0
- package/src/cognition/senses/audition.engine/salience.ts +59 -0
- package/src/cognition/senses/base.sense.engine.ts +117 -0
- package/src/cognition/senses/gustation.engine.ts +24 -0
- package/src/cognition/senses/index.ts +172 -0
- package/src/cognition/senses/olfaction.engine.ts +24 -0
- package/src/cognition/senses/somatosensation.engine.ts +23 -0
- package/src/cognition/senses/vision.engine.ts +23 -0
- package/src/cognition/types.ts +63 -0
- package/src/cognition/utilities/token.tracker.ts +492 -0
- package/src/core/abstracts.ts +60 -0
- package/src/core/async.engine.ts +539 -0
- package/src/core/clock.ts +196 -0
- package/src/core/completion.recorder.ts +122 -0
- package/src/core/conflict.detector.ts +140 -0
- package/src/core/distributed.ts +626 -0
- package/src/core/event.bus.ts +292 -0
- package/src/core/inbound.recorder.ts +100 -0
- package/src/core/index.ts +167 -0
- package/src/core/logger.ts +85 -0
- package/src/core/metrics.ts +107 -0
- package/src/core/orchestrator.ts +625 -0
- package/src/core/replay.ts +589 -0
- package/src/core/scenario.ts +120 -0
- package/src/core/serialization.ts +448 -0
- package/src/core/simulation.ts +197 -0
- package/src/core/snapshot.manager.ts +293 -0
- package/src/core/state.manager.ts +283 -0
- package/src/core/types.ts +274 -0
- package/src/core/utils.ts +82 -0
- package/src/core/wall.clock.ts +23 -0
- package/src/extensions/livestream.ext.ts +570 -0
- package/src/extensions/time.ext.ts +339 -0
- package/src/index.ts +91 -0
- package/src/llm/gate.ts +140 -0
- package/src/llm/index.ts +716 -0
- package/src/llm/summarizer.ts +170 -0
- package/src/llm/wire.contracts.ts +67 -0
- package/src/pma/eval.ts +651 -0
- package/src/pma/index.ts +1082 -0
- package/src/profiles/built-in.ts +7 -0
- package/src/profiles/companion.ts +28 -0
- package/src/profiles/company-brain.ts +54 -0
- package/src/profiles/customer-service.ts +30 -0
- package/src/profiles/game-npc.ts +20 -0
- package/src/profiles/index.ts +42 -0
- package/src/profiles/smart-home.ts +34 -0
- package/src/runners/coherence.runner.ts +49 -0
- package/src/runners/outreach.runner.ts +158 -0
- package/src/runners/social.runner.ts +182 -0
- package/src/runners/thin-shim.runner.ts +195 -0
- package/src/sdk/will.ts +334 -0
- package/src/stem/assembly.audit.ts +88 -0
- package/src/stem/distribution.ts +203 -0
- package/src/stem/guards/identity.coherence.ts +152 -0
- package/src/stem/guards/identity.guard.ts +227 -0
- package/src/stem/index.ts +1145 -0
- package/src/stem/mind.ts +1090 -0
- package/src/stem/tracts/ack.reconciler.ts +55 -0
- package/src/stem/tracts/biography.writer.ts +190 -0
- package/src/stem/tracts/effector.controller.ts +143 -0
- package/src/stem/tracts/health.reporter.ts +113 -0
- package/src/stem/tracts/inbound.queue.ts +54 -0
- package/src/stem/tracts/outbox.controller.ts +167 -0
- package/src/stem/tracts/outbox.writer.ts +133 -0
- package/src/stem/tracts/pma.controller.ts +109 -0
- package/src/stem/tracts/replay.controller.ts +177 -0
- package/src/stem/tracts/sensory.controller.ts +168 -0
- package/src/stem/tracts/session.logger.ts +157 -0
- package/src/stem/tracts/transport/index.ts +38 -0
- package/src/stem/tracts/transport/loopback.transport.ts +86 -0
- package/src/stem/tracts/transport/socketio.transport.ts +200 -0
- package/src/stem/tracts/transport/stream.transport.ts +152 -0
- package/src/stem/tracts/transport/types.ts +193 -0
- package/src/stem/tracts/transport.controller.ts +380 -0
- 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
|
+
}
|