@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,292 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/core/event.bus.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import type { SimulationEvent, SimulationEventBase, SimulationContext, Tick, Timestamp } from '#core/types'
|
|
6
|
+
import { wallClock } from '#core/wall.clock'
|
|
7
|
+
|
|
8
|
+
export type EventHandler<T = unknown> = ( event: SimulationEvent<T>, context: SimulationContext ) => void | Promise<void>
|
|
9
|
+
export type EventFilter = ( event: SimulationEventBase ) => boolean
|
|
10
|
+
|
|
11
|
+
export interface EventBus {
|
|
12
|
+
/**
|
|
13
|
+
* Enqueue an event for dispatch at end of the current tick.
|
|
14
|
+
* Prevents silent corruption from events stamped with tick 0.
|
|
15
|
+
* Callers must explicitly pass the current tick from the orchestrator.
|
|
16
|
+
*/
|
|
17
|
+
publish<T>(
|
|
18
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
19
|
+
context: SimulationContext,
|
|
20
|
+
tick: Tick
|
|
21
|
+
): void
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Enqueue and immediately flush if the queue is below the sync threshold.
|
|
25
|
+
* Useful outside of a managed tick loop.
|
|
26
|
+
*/
|
|
27
|
+
publishAsync<T>(
|
|
28
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
29
|
+
context: SimulationContext,
|
|
30
|
+
tick: Tick
|
|
31
|
+
): Promise<void>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Schedule an event to be injected into the pending queue at a future tick.
|
|
35
|
+
* The Orchestrator calls prepareTick() each tick to drain due events.
|
|
36
|
+
*/
|
|
37
|
+
scheduleAt<T>(
|
|
38
|
+
scheduledTick: Tick,
|
|
39
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
40
|
+
context: SimulationContext
|
|
41
|
+
): void
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Move all scheduled events whose tick ≤ currentTick into the pending queue.
|
|
45
|
+
* Called by the Orchestrator at the start of each tick, before engines run.
|
|
46
|
+
*/
|
|
47
|
+
prepareTick( currentTick: Tick ): void
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Drain and dispatch all pending events synchronously (within the current
|
|
51
|
+
* async frame). Called by the Orchestrator at end of each tick, after all
|
|
52
|
+
* commands have been applied.
|
|
53
|
+
*/
|
|
54
|
+
flush(): Promise<void>
|
|
55
|
+
|
|
56
|
+
subscribe<T>( eventType: string, handler: EventHandler<T> ): () => void
|
|
57
|
+
subscribeFiltered( filter: EventFilter, handler: EventHandler ): () => void
|
|
58
|
+
subscribeAll( handler: EventHandler ): () => void
|
|
59
|
+
|
|
60
|
+
clear(): void
|
|
61
|
+
getPendingCount(): number
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface EventBusConfig {
|
|
65
|
+
maxQueueSize?: number
|
|
66
|
+
syncThreshold?: number
|
|
67
|
+
/**
|
|
68
|
+
* Sim-time source for event timestamps. Events land in the event log, so the
|
|
69
|
+
* timestamp must come from the sim clock (not wall time) for replay fidelity
|
|
70
|
+
* (R2). Defaults to `wallClock` for standalone/non-orchestrated use; the
|
|
71
|
+
* Simulation wires this to `clock.now`.
|
|
72
|
+
*/
|
|
73
|
+
now?: () => Timestamp
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── Internal types ───────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
interface PendingEntry {
|
|
79
|
+
event: SimulationEvent
|
|
80
|
+
context: SimulationContext
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface ScheduledEntry {
|
|
84
|
+
tick: Tick
|
|
85
|
+
event: SimulationEvent
|
|
86
|
+
context: SimulationContext
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Implementation ───────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
export class DefaultEventBus implements EventBus {
|
|
92
|
+
private _handlers = new Map<string, Set<EventHandler>>()
|
|
93
|
+
private _filteredHandlers = new Map<EventFilter, Set<EventHandler>>()
|
|
94
|
+
private _allHandlers = new Set<EventHandler>()
|
|
95
|
+
private _pendingEvents: PendingEntry[] = []
|
|
96
|
+
private _scheduledEvents: ScheduledEntry[] = [] // sorted ascending by tick
|
|
97
|
+
private _isProcessing = false
|
|
98
|
+
private _maxQueueSize: number
|
|
99
|
+
private _syncThreshold: number
|
|
100
|
+
private _now: () => Timestamp
|
|
101
|
+
// Monotonic event-id counter. Event ids land in the replay stream, so they
|
|
102
|
+
// must be deterministic across identical runs for byte-identical replay (R2);
|
|
103
|
+
// a per-bus counter incremented in publish/schedule order replaces randomUUID().
|
|
104
|
+
private _eventSeq = 0
|
|
105
|
+
|
|
106
|
+
constructor( config: EventBusConfig = {} ){
|
|
107
|
+
this._maxQueueSize = config.maxQueueSize ?? 10000
|
|
108
|
+
this._syncThreshold = config.syncThreshold ?? 100
|
|
109
|
+
this._now = config.now ?? wallClock
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Publish ─────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Events are always stamped with the caller-provided tick
|
|
116
|
+
* to prevent default-to-zero bugs.
|
|
117
|
+
*/
|
|
118
|
+
publish<T>(
|
|
119
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
120
|
+
context: SimulationContext,
|
|
121
|
+
tick: Tick
|
|
122
|
+
): void {
|
|
123
|
+
const fullEvent = this._stamp<T>( event, tick )
|
|
124
|
+
this._enqueue( fullEvent, context )
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async publishAsync<T>(
|
|
128
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
129
|
+
context: SimulationContext,
|
|
130
|
+
tick: Tick
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const fullEvent = this._stamp<T>( event, tick )
|
|
133
|
+
this._enqueue( fullEvent, context )
|
|
134
|
+
|
|
135
|
+
this._pendingEvents.length <= this._syncThreshold && await this.flush()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private _stamp<T>(
|
|
139
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
140
|
+
tick: Tick
|
|
141
|
+
): SimulationEvent<T> {
|
|
142
|
+
return {
|
|
143
|
+
...event,
|
|
144
|
+
id: `evt-${this._eventSeq++}`,
|
|
145
|
+
timestamp: this._now(),
|
|
146
|
+
tick,
|
|
147
|
+
} as SimulationEvent<T>
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private _enqueue( event: SimulationEvent, context: SimulationContext ): void {
|
|
151
|
+
if( this._pendingEvents.length >= this._maxQueueSize )
|
|
152
|
+
throw new Error(`Event bus queue full (${this._maxQueueSize}). Dropping event: ${event.type}`)
|
|
153
|
+
|
|
154
|
+
this._pendingEvents.push({ event, context })
|
|
155
|
+
|
|
156
|
+
// Trigger async processing for standalone (non-orchestrated) use.
|
|
157
|
+
// When the Orchestrator is driving ticks it calls flush() explicitly;
|
|
158
|
+
// the setImmediate fires after that and finds an empty queue — no-op.
|
|
159
|
+
!this._isProcessing && setImmediate( () => { this._processBackground() } )
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Future-event priority queue ─────────────────────────
|
|
163
|
+
|
|
164
|
+
scheduleAt<T>(
|
|
165
|
+
scheduledTick: Tick,
|
|
166
|
+
event: Omit<SimulationEvent<T>, 'id' | 'timestamp' | 'tick'>,
|
|
167
|
+
context: SimulationContext
|
|
168
|
+
): void {
|
|
169
|
+
const fullEvent: SimulationEvent<T> = {
|
|
170
|
+
...event,
|
|
171
|
+
id: `evt-${this._eventSeq++}`,
|
|
172
|
+
timestamp: this._now(),
|
|
173
|
+
tick: scheduledTick,
|
|
174
|
+
} as SimulationEvent<T>
|
|
175
|
+
|
|
176
|
+
// Binary-search insertion to maintain ascending tick order — O(log n)
|
|
177
|
+
const entry: ScheduledEntry = { tick: scheduledTick, event: fullEvent, context }
|
|
178
|
+
let lo = 0, hi = this._scheduledEvents.length
|
|
179
|
+
|
|
180
|
+
while( lo < hi ){
|
|
181
|
+
const mid = ( lo + hi ) >>> 1
|
|
182
|
+
this._scheduledEvents[mid]!.tick <= scheduledTick ? lo = mid + 1 : hi = mid
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this._scheduledEvents.splice( lo, 0, entry )
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
prepareTick( currentTick: Tick ): void {
|
|
189
|
+
// Find the first entry beyond the current tick — O(log n) scan
|
|
190
|
+
let i = 0
|
|
191
|
+
while( i < this._scheduledEvents.length && this._scheduledEvents[i]!.tick <= currentTick )
|
|
192
|
+
i++
|
|
193
|
+
|
|
194
|
+
if( i === 0 ) return
|
|
195
|
+
|
|
196
|
+
// Move due events into the pending queue (preserving scheduled tick)
|
|
197
|
+
const due = this._scheduledEvents.splice( 0, i )
|
|
198
|
+
for( const entry of due )
|
|
199
|
+
this._pendingEvents.push({ event: entry.event, context: entry.context })
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ── Flush ───────────────────────────────────────────────
|
|
203
|
+
|
|
204
|
+
async flush(): Promise<void> {
|
|
205
|
+
await this._process()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Background processing — triggered via setImmediate for standalone use.
|
|
210
|
+
* Silently swallows errors to avoid unhandled-rejection noise in that path.
|
|
211
|
+
*/
|
|
212
|
+
private _processBackground(): void {
|
|
213
|
+
this._process().catch( () => {} )
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private async _process(): Promise<void> {
|
|
217
|
+
if( this._isProcessing ) return
|
|
218
|
+
if( this._pendingEvents.length === 0 ) return
|
|
219
|
+
|
|
220
|
+
this._isProcessing = true
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
// Loop so that events published by handlers in the same flush are
|
|
224
|
+
// dispatched before we yield back to the caller.
|
|
225
|
+
while( this._pendingEvents.length > 0 ){
|
|
226
|
+
const batch = this._pendingEvents.splice( 0 )
|
|
227
|
+
for( const { event, context } of batch )
|
|
228
|
+
await this._dispatch( event, context )
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
this._isProcessing = false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private async _dispatch( event: SimulationEvent, context: SimulationContext ): Promise<void> {
|
|
237
|
+
// Type-specific handlers
|
|
238
|
+
const handlers = this._handlers.get( event.type )
|
|
239
|
+
if( handlers )
|
|
240
|
+
for( const handler of handlers )
|
|
241
|
+
await handler( event, context )
|
|
242
|
+
|
|
243
|
+
// Filtered handlers
|
|
244
|
+
for( const [ filter, handlersSet ] of this._filteredHandlers )
|
|
245
|
+
filter( event ) && await Promise.all( [ ...handlersSet ].map( h => h( event, context ) ) )
|
|
246
|
+
|
|
247
|
+
// All-catch handlers
|
|
248
|
+
for( const handler of this._allHandlers )
|
|
249
|
+
await handler( event, context )
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── Subscriptions ────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
subscribe<T>( eventType: string, handler: EventHandler<T> ): () => void {
|
|
255
|
+
!this._handlers.has( eventType ) && this._handlers.set( eventType, new Set() )
|
|
256
|
+
this._handlers.get( eventType )!.add( handler as EventHandler )
|
|
257
|
+
|
|
258
|
+
return () => {
|
|
259
|
+
this._handlers.get( eventType )?.delete( handler as EventHandler )
|
|
260
|
+
this._handlers.get( eventType )?.size === 0 && this._handlers.delete( eventType )
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
subscribeFiltered( filter: EventFilter, handler: EventHandler ): () => void {
|
|
265
|
+
!this._filteredHandlers.has( filter ) && this._filteredHandlers.set( filter, new Set() )
|
|
266
|
+
this._filteredHandlers.get( filter )!.add( handler )
|
|
267
|
+
|
|
268
|
+
return () => {
|
|
269
|
+
this._filteredHandlers.get( filter )?.delete( handler )
|
|
270
|
+
this._filteredHandlers.get( filter )?.size === 0 && this._filteredHandlers.delete( filter )
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
subscribeAll( handler: EventHandler ): () => void {
|
|
275
|
+
this._allHandlers.add( handler )
|
|
276
|
+
return () => { this._allHandlers.delete( handler ) }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ── Housekeeping ─────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
clear(): void {
|
|
282
|
+
this._handlers.clear()
|
|
283
|
+
this._filteredHandlers.clear()
|
|
284
|
+
this._allHandlers.clear()
|
|
285
|
+
this._pendingEvents = []
|
|
286
|
+
this._scheduledEvents = []
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
getPendingCount(): number {
|
|
290
|
+
return this._pendingEvents.length
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/core/inbound.recorder.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// External-inbound record/replay seam (REORIENT R2, transport edition).
|
|
6
|
+
//
|
|
7
|
+
// The ExternalTransport is a non-deterministic input, exactly like the LLM:
|
|
8
|
+
// to re-run a session deterministically the recorder must capture every inbound
|
|
9
|
+
// envelope (messages, percepts, acks) at the tick it was applied. On replay a
|
|
10
|
+
// *source* re-feeds those recorded envelopes instead of waiting on a live socket.
|
|
11
|
+
//
|
|
12
|
+
// This mirrors completion.recorder.ts: a capture sink (recordInbound) and a
|
|
13
|
+
// re-feed source, both keyed by willId. Deterministic re-execution itself is
|
|
14
|
+
// still deferred — this completes the capture half so it lands ready.
|
|
15
|
+
//
|
|
16
|
+
// `envelope` is `unknown` on purpose: this is `#core` and must not depend on the
|
|
17
|
+
// `#stem` transport types. It round-trips as JSON.
|
|
18
|
+
// ─────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
import type { Tick, Timestamp } from '#core/types'
|
|
21
|
+
|
|
22
|
+
export interface InboundRecord {
|
|
23
|
+
tick: Tick
|
|
24
|
+
willId: string
|
|
25
|
+
/** The InboundEnvelope as applied (opaque here to avoid a #stem dependency). */
|
|
26
|
+
envelope: unknown
|
|
27
|
+
timestamp: Timestamp
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface InboundSink {
|
|
31
|
+
recordInbound( record: InboundRecord ): void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── Capture sink ─────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const _sinks = new Map<string, InboundSink>()
|
|
37
|
+
|
|
38
|
+
export function setInboundRecorder( willId: string, sink: InboundSink ): void {
|
|
39
|
+
_sinks.set( willId, sink )
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function clearInboundRecorder( willId: string ): void {
|
|
43
|
+
_sinks.delete( willId )
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getInboundRecorder( willId: string ): InboundSink | undefined {
|
|
47
|
+
return _sinks.get( willId )
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Replay re-feed source (deferred re-execution) ────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Supplies recorded inbound envelopes back during a deterministic re-execution
|
|
54
|
+
* replay, keyed by willId. Returns the envelopes recorded at `tick` (empty if
|
|
55
|
+
* none). Registered only while replaying; absent during live runs.
|
|
56
|
+
*/
|
|
57
|
+
export interface InboundSource {
|
|
58
|
+
envelopesAt( tick: Tick ): unknown[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Re-feed source backed by recorded inbound. Groups envelopes by their applied
|
|
63
|
+
* tick (FIFO within a tick, preserving record order) and hands them back once.
|
|
64
|
+
* Unlike the LLM source, a tick with no recorded inbound is normal (most ticks),
|
|
65
|
+
* so a miss returns `[]` rather than throwing — only an upstream LLM/clock/PRNG
|
|
66
|
+
* divergence (caught by their own sources) can invalidate the replay.
|
|
67
|
+
*/
|
|
68
|
+
export class RecordedInboundSource implements InboundSource {
|
|
69
|
+
private readonly _byTick = new Map<Tick, unknown[]>()
|
|
70
|
+
|
|
71
|
+
constructor( records: readonly InboundRecord[] ){
|
|
72
|
+
for( const r of records ){
|
|
73
|
+
const queue = this._byTick.get( r.tick )
|
|
74
|
+
if( queue ) queue.push( r.envelope )
|
|
75
|
+
else this._byTick.set( r.tick, [ r.envelope ] )
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
envelopesAt( tick: Tick ): unknown[] {
|
|
80
|
+
const queue = this._byTick.get( tick )
|
|
81
|
+
if( !queue || queue.length === 0 ) return []
|
|
82
|
+
// Consume so a tick is never re-fed twice within one replay.
|
|
83
|
+
this._byTick.set( tick, [] )
|
|
84
|
+
return queue
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const _sources = new Map<string, InboundSource>()
|
|
89
|
+
|
|
90
|
+
export function setInboundSource( willId: string, source: InboundSource ): void {
|
|
91
|
+
_sources.set( willId, source )
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function clearInboundSource( willId: string ): void {
|
|
95
|
+
_sources.delete( willId )
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getInboundSource( willId: string ): InboundSource | undefined {
|
|
99
|
+
return _sources.get( willId )
|
|
100
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/index.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import { type ClockConfig, DefaultSimulationClock, type SimulationClock } from '#core/clock'
|
|
6
|
+
import type { Coordinates, Duration, SimulationContext, SimulationEntity, SimulationEvent, SimulationEventBase, EventPayload, SimulationState, Tick, Timestamp, MinimalContext, RestoreOptions } from '#core/types'
|
|
7
|
+
import { DefaultOrchestrator, type Orchestrator, type OrchestratorConfig, type SimulationEngine, type EngineResult } from '#core/orchestrator'
|
|
8
|
+
import { DefaultEventBus, type EventBus, type EventBusConfig, type EventFilter, type EventHandler } from '#core/event.bus'
|
|
9
|
+
import { DefaultScenario, type Scenario, type ScenarioConfig, type ScenarioValidationResult } from '#core/scenario'
|
|
10
|
+
import { DefaultMetricCollector, type MetricCollector, type MetricPoint } from '#core/metrics'
|
|
11
|
+
import { DefaultSimulation, type Simulation, type SimulationConfig } from '#core/simulation'
|
|
12
|
+
import { DefaultStateManager, type StateManager } from '#core/state.manager'
|
|
13
|
+
import { createContext, createPRNG } from '#core/utils'
|
|
14
|
+
|
|
15
|
+
// New exports
|
|
16
|
+
export {
|
|
17
|
+
DefaultSerializer,
|
|
18
|
+
DeltaEncoder,
|
|
19
|
+
type Serializer,
|
|
20
|
+
type SerializationConfig,
|
|
21
|
+
type SerializationFormat,
|
|
22
|
+
type SerializedState,
|
|
23
|
+
type SerializedEntity,
|
|
24
|
+
type DeltaSnapshot
|
|
25
|
+
} from '#core/serialization'
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
DefaultPartitionRouter,
|
|
29
|
+
ConsistentHashRouter,
|
|
30
|
+
DistributedStateManager,
|
|
31
|
+
DistributedOrchestrator,
|
|
32
|
+
LocalTransport,
|
|
33
|
+
type DistributedNode,
|
|
34
|
+
type DistributedNodeConfig,
|
|
35
|
+
type ShardConfig,
|
|
36
|
+
type ShardStrategy,
|
|
37
|
+
type PartitionRouter,
|
|
38
|
+
type CrossShardTransport,
|
|
39
|
+
type CrossShardQuery,
|
|
40
|
+
type CrossShardResult,
|
|
41
|
+
type DistributedEvent,
|
|
42
|
+
type DeliveryGuarantee
|
|
43
|
+
} from '#core/distributed'
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
DefaultReplayRecorder,
|
|
47
|
+
DefaultReplaySession,
|
|
48
|
+
ReplayManager,
|
|
49
|
+
type ReplayRecord,
|
|
50
|
+
type ReplayMetadata,
|
|
51
|
+
type ReplaySession,
|
|
52
|
+
type ReplayRecorder,
|
|
53
|
+
type ReplayConfig,
|
|
54
|
+
type ReplayComparison,
|
|
55
|
+
type ReplayDifference
|
|
56
|
+
} from '#core/replay'
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
setCompletionRecorder,
|
|
60
|
+
clearCompletionRecorder,
|
|
61
|
+
getCompletionRecorder,
|
|
62
|
+
type LLMCompletionRecord,
|
|
63
|
+
type LLMCompletionSink
|
|
64
|
+
} from '#core/completion.recorder'
|
|
65
|
+
|
|
66
|
+
// Injectable diagnostic logger
|
|
67
|
+
export {
|
|
68
|
+
logger,
|
|
69
|
+
setLogger,
|
|
70
|
+
resetLogger,
|
|
71
|
+
getLogger,
|
|
72
|
+
ConsoleLogger,
|
|
73
|
+
SilentLogger,
|
|
74
|
+
type Logger
|
|
75
|
+
} from '#core/logger'
|
|
76
|
+
|
|
77
|
+
// Core exports
|
|
78
|
+
export {
|
|
79
|
+
DefaultSimulation,
|
|
80
|
+
type Simulation,
|
|
81
|
+
type SimulationConfig,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
DefaultSimulationClock,
|
|
86
|
+
type SimulationClock,
|
|
87
|
+
type ClockConfig
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export {
|
|
91
|
+
DefaultEventBus,
|
|
92
|
+
type EventBus,
|
|
93
|
+
type EventBusConfig,
|
|
94
|
+
type EventHandler,
|
|
95
|
+
type EventFilter
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
DefaultStateManager,
|
|
100
|
+
type StateManager
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export {
|
|
104
|
+
DefaultOrchestrator,
|
|
105
|
+
type Orchestrator,
|
|
106
|
+
type OrchestratorConfig,
|
|
107
|
+
type SimulationEngine,
|
|
108
|
+
type EngineResult,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
DefaultScenario,
|
|
113
|
+
type Scenario,
|
|
114
|
+
type ScenarioConfig,
|
|
115
|
+
type ScenarioValidationResult
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
DefaultMetricCollector,
|
|
120
|
+
type MetricCollector,
|
|
121
|
+
type MetricPoint
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Storage abstractions
|
|
125
|
+
export {
|
|
126
|
+
BunStorageAdapter,
|
|
127
|
+
type StorageAdapter
|
|
128
|
+
} from '#core/abstracts'
|
|
129
|
+
|
|
130
|
+
// Utility functions
|
|
131
|
+
export {
|
|
132
|
+
createContext,
|
|
133
|
+
createPRNG
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Types
|
|
137
|
+
export type {
|
|
138
|
+
Timestamp,
|
|
139
|
+
Duration,
|
|
140
|
+
Tick,
|
|
141
|
+
Coordinates,
|
|
142
|
+
SimulationContext,
|
|
143
|
+
SimulationEntity,
|
|
144
|
+
SimulationEvent,
|
|
145
|
+
SimulationEventBase,
|
|
146
|
+
EventPayload,
|
|
147
|
+
SimulationState,
|
|
148
|
+
MinimalContext,
|
|
149
|
+
RestoreOptions
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export {
|
|
153
|
+
AsyncEngine,
|
|
154
|
+
} from '#core/async.engine'
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
ConflictDetector,
|
|
158
|
+
} from '#core/conflict.detector'
|
|
159
|
+
|
|
160
|
+
// In the types section, add:
|
|
161
|
+
export type {
|
|
162
|
+
ReasoningFootprint,
|
|
163
|
+
ConflictReport,
|
|
164
|
+
ConflictResolution,
|
|
165
|
+
ConflictStrategy,
|
|
166
|
+
AsyncEngineConfig,
|
|
167
|
+
} from '#core/types'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/core/logger.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Injectable diagnostic logger for the Will library.
|
|
7
|
+
*
|
|
8
|
+
* Will is embedded inside a host process (the API server, the dev runner,
|
|
9
|
+
* tests). A library has no business deciding where diagnostic output goes,
|
|
10
|
+
* so every internal `console.*` call routes through this seam instead. The
|
|
11
|
+
* host installs its own sink once at startup via `setLogger()`; everything
|
|
12
|
+
* downstream calls `logger.info()/warn()/error()/debug()` and never touches
|
|
13
|
+
* `console` directly.
|
|
14
|
+
*
|
|
15
|
+
* This is deliberately NOT the same thing as `SessionLogger` (stem/tracts),
|
|
16
|
+
* which writes the structured NDJSON cognitive event stream. This logger is
|
|
17
|
+
* for unstructured operational diagnostics — "rate limited, retrying",
|
|
18
|
+
* "S3 upload failed", "engine X skipped" — the lines that used to be
|
|
19
|
+
* `console.warn`.
|
|
20
|
+
*
|
|
21
|
+
* Default behaviour is identical to the previous direct `console.*` usage:
|
|
22
|
+
* `ConsoleLogger` forwards each level to the matching console method, so
|
|
23
|
+
* installing nothing changes nothing.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* import { logger } from '#core/logger'
|
|
27
|
+
* logger.warn('[LLMGate] rate limited — retrying')
|
|
28
|
+
*
|
|
29
|
+
* // host, once at startup:
|
|
30
|
+
* import { setLogger } from 'will'
|
|
31
|
+
* setLogger(myStructuredLogger)
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export interface Logger {
|
|
35
|
+
debug( message?: unknown, ...args: unknown[] ): void
|
|
36
|
+
info ( message?: unknown, ...args: unknown[] ): void
|
|
37
|
+
warn ( message?: unknown, ...args: unknown[] ): void
|
|
38
|
+
error( message?: unknown, ...args: unknown[] ): void
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Default sink: forwards to the global console. `debug` → console.debug,
|
|
43
|
+
* `info` → console.info (stdout, same channel as the old console.log calls),
|
|
44
|
+
* `warn`/`error` → their console equivalents (stderr). Behaviour matches the
|
|
45
|
+
* pre-refactor direct console usage exactly.
|
|
46
|
+
*/
|
|
47
|
+
export class ConsoleLogger implements Logger {
|
|
48
|
+
debug( message?: unknown, ...args: unknown[] ): void { console.debug( message, ...args ) }
|
|
49
|
+
info ( message?: unknown, ...args: unknown[] ): void { console.info ( message, ...args ) }
|
|
50
|
+
warn ( message?: unknown, ...args: unknown[] ): void { console.warn ( message, ...args ) }
|
|
51
|
+
error( message?: unknown, ...args: unknown[] ): void { console.error( message, ...args ) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* No-op sink — useful for tests or hosts that want Will to stay silent.
|
|
56
|
+
*/
|
|
57
|
+
export class SilentLogger implements Logger {
|
|
58
|
+
debug(): void {}
|
|
59
|
+
info (): void {}
|
|
60
|
+
warn (): void {}
|
|
61
|
+
error(): void {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Module-level current logger. Mutable so a host can swap it once at startup.
|
|
65
|
+
let _logger: Logger = new ConsoleLogger()
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Stable indirection object. Callers import this and call `logger.info(...)`;
|
|
69
|
+
* `setLogger()` re-points the underlying sink without callers re-importing.
|
|
70
|
+
*/
|
|
71
|
+
export const logger: Logger = {
|
|
72
|
+
debug: ( message?: unknown, ...args: unknown[] ) => _logger.debug( message, ...args ),
|
|
73
|
+
info : ( message?: unknown, ...args: unknown[] ) => _logger.info ( message, ...args ),
|
|
74
|
+
warn : ( message?: unknown, ...args: unknown[] ) => _logger.warn ( message, ...args ),
|
|
75
|
+
error: ( message?: unknown, ...args: unknown[] ) => _logger.error( message, ...args ),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Install a custom sink for all subsequent Will diagnostic logging. */
|
|
79
|
+
export function setLogger( next: Logger ): void { _logger = next }
|
|
80
|
+
|
|
81
|
+
/** Reset to the default console sink. Primarily for tests. */
|
|
82
|
+
export function resetLogger(): void { _logger = new ConsoleLogger() }
|
|
83
|
+
|
|
84
|
+
/** The currently-installed sink (the real object, not the indirection). */
|
|
85
|
+
export function getLogger(): Logger { return _logger }
|