@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,339 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/extensions/time.ext.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Time extension for logistics, mobility, IoT, and smart home scenarios.
|
|
7
|
+
* These are NOT part of core — they layer on top.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { SimulationClock } from '#core/clock'
|
|
11
|
+
import type { Timestamp } from '#core/types'
|
|
12
|
+
|
|
13
|
+
// ── Time-of-day utilities ─────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface TimeOfDay {
|
|
16
|
+
hour: number
|
|
17
|
+
minute: number
|
|
18
|
+
second: number
|
|
19
|
+
millisecond: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface DateComponents {
|
|
23
|
+
year: number
|
|
24
|
+
month: number
|
|
25
|
+
day: number
|
|
26
|
+
hour: number
|
|
27
|
+
minute: number
|
|
28
|
+
second: number
|
|
29
|
+
millisecond: number
|
|
30
|
+
dayOfWeek: number // 0 = Sunday, 6 = Saturday
|
|
31
|
+
timestamp: Timestamp
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TimeWindow {
|
|
35
|
+
startHour: number // 0-23
|
|
36
|
+
endHour: number // 0-23, can wrap (e.g., 22-6 for overnight)
|
|
37
|
+
startMinute?: number // 0-59
|
|
38
|
+
endMinute?: number // 0-59
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class TimeExtension {
|
|
42
|
+
private _clock: SimulationClock
|
|
43
|
+
private _timezoneOffset: number // minutes from UTC, e.g., -480 for PST
|
|
44
|
+
// private _referenceDate: Date
|
|
45
|
+
|
|
46
|
+
constructor( clock: SimulationClock, timezoneOffsetMinutes: number = 0 ){
|
|
47
|
+
this._clock = clock
|
|
48
|
+
this._timezoneOffset = timezoneOffsetMinutes
|
|
49
|
+
// this._referenceDate = new Date( this._clock.now )
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── Time-of-day (no date) ───────────────────────────────
|
|
53
|
+
|
|
54
|
+
getTimeOfDay(): TimeOfDay {
|
|
55
|
+
const date = this._getDateWithOffset()
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
hour: date.getHours(),
|
|
59
|
+
minute: date.getMinutes(),
|
|
60
|
+
second: date.getSeconds(),
|
|
61
|
+
millisecond: date.getMilliseconds()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getHour(): number {
|
|
66
|
+
return this._getDateWithOffset().getHours()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getMinute(): number {
|
|
70
|
+
return this._getDateWithOffset().getMinutes()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
isBetweenHours( startHour: number, endHour: number ): boolean {
|
|
74
|
+
const hour = this.getHour()
|
|
75
|
+
|
|
76
|
+
// Handle overnight windows (e.g., 22-6)
|
|
77
|
+
if( startHour > endHour )
|
|
78
|
+
return hour >= startHour || hour <= endHour
|
|
79
|
+
|
|
80
|
+
return hour >= startHour && hour <= endHour
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
isWithinTimeWindow( window: TimeWindow ): boolean {
|
|
84
|
+
const
|
|
85
|
+
hour = this.getHour(),
|
|
86
|
+
minute = this.getMinute(),
|
|
87
|
+
|
|
88
|
+
currentMinutes = hour * 60 + minute,
|
|
89
|
+
startMinutes = ( window.startHour * 60 ) + ( window.startMinute ?? 0 ),
|
|
90
|
+
endMinutes = ( window.endHour * 60 ) + ( window.endMinute ?? 0 )
|
|
91
|
+
|
|
92
|
+
// Handle overnight windows
|
|
93
|
+
if( startMinutes > endMinutes )
|
|
94
|
+
return currentMinutes >= startMinutes || currentMinutes <= endMinutes
|
|
95
|
+
|
|
96
|
+
return currentMinutes >= startMinutes && currentMinutes <= endMinutes
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── Full date/time (for scheduling) ─────────────────────
|
|
100
|
+
|
|
101
|
+
getCurrentDateComponents(): DateComponents {
|
|
102
|
+
const date = this._getDateWithOffset()
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
year: date.getFullYear(),
|
|
106
|
+
month: date.getMonth(),
|
|
107
|
+
day: date.getDate(),
|
|
108
|
+
hour: date.getHours(),
|
|
109
|
+
minute: date.getMinutes(),
|
|
110
|
+
second: date.getSeconds(),
|
|
111
|
+
millisecond: date.getMilliseconds(),
|
|
112
|
+
dayOfWeek: date.getDay(),
|
|
113
|
+
timestamp: this._clock.now
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── Business hours / shift detection ────────────────────
|
|
118
|
+
|
|
119
|
+
isBusinessHours(
|
|
120
|
+
businessStartHour: number = 9,
|
|
121
|
+
businessEndHour: number = 17,
|
|
122
|
+
includeWeekends: boolean = false
|
|
123
|
+
): boolean {
|
|
124
|
+
const
|
|
125
|
+
components = this.getCurrentDateComponents(),
|
|
126
|
+
isWeekend = components.dayOfWeek === 0 || components.dayOfWeek === 6
|
|
127
|
+
|
|
128
|
+
if( !includeWeekends && isWeekend ) return false
|
|
129
|
+
|
|
130
|
+
return this.isBetweenHours( businessStartHour, businessEndHour )
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
isRushHour( cityType: 'urban' | 'suburban' | 'rural' = 'urban'): boolean {
|
|
134
|
+
const hour = this.getHour()
|
|
135
|
+
|
|
136
|
+
switch( cityType ) {
|
|
137
|
+
case 'urban':
|
|
138
|
+
// Morning: 7-9, Evening: 16-19
|
|
139
|
+
return ( hour >= 7 && hour <= 9 ) || ( hour >= 16 && hour <= 19 )
|
|
140
|
+
|
|
141
|
+
case 'suburban':
|
|
142
|
+
// Morning: 6-8, Evening: 17-19
|
|
143
|
+
return ( hour >= 6 && hour <= 8 ) || ( hour >= 17 && hour <= 19 )
|
|
144
|
+
|
|
145
|
+
case 'rural':
|
|
146
|
+
// Minimal rush hour
|
|
147
|
+
return ( hour >= 7 && hour <= 8 ) || ( hour >= 17 && hour <= 18 )
|
|
148
|
+
|
|
149
|
+
default: return false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── Day part detection (for smart home) ─────────────────
|
|
154
|
+
|
|
155
|
+
getDayPart(): 'dawn' | 'morning' | 'afternoon' | 'evening' | 'night' | 'late-night' {
|
|
156
|
+
const hour = this.getHour()
|
|
157
|
+
|
|
158
|
+
if( hour >= 5 && hour < 7 ) return 'dawn'
|
|
159
|
+
if( hour >= 7 && hour < 12 ) return 'morning'
|
|
160
|
+
if( hour >= 12 && hour < 17 ) return 'afternoon'
|
|
161
|
+
if( hour >= 17 && hour < 21 ) return 'evening'
|
|
162
|
+
if( hour >= 21 && hour < 24 ) return 'night'
|
|
163
|
+
return 'late-night'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── ETA with time-of-day adjustment (logistics) ─────────
|
|
167
|
+
|
|
168
|
+
calculateAdjustedEta(
|
|
169
|
+
baseMinutes: number,
|
|
170
|
+
trafficMultiplier: number = 1.0
|
|
171
|
+
): number {
|
|
172
|
+
let multiplier = trafficMultiplier
|
|
173
|
+
|
|
174
|
+
// Apply rush hour penalty
|
|
175
|
+
if( this.isRushHour() ) multiplier *= 1.5
|
|
176
|
+
|
|
177
|
+
// Apply night time bonus (less traffic)
|
|
178
|
+
const hour = this.getHour()
|
|
179
|
+
if( hour >= 22 || hour <= 5 ) multiplier *= 0.8
|
|
180
|
+
|
|
181
|
+
return baseMinutes * multiplier
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Scheduling helpers ──────────────────────────────────
|
|
185
|
+
|
|
186
|
+
getMinutesUntil( targetHour: number, targetMinute: number = 0 ): number {
|
|
187
|
+
const
|
|
188
|
+
current = this.getTimeOfDay(),
|
|
189
|
+
currentMinutes = current.hour * 60 + current.minute,
|
|
190
|
+
targetMinutes = targetHour * 60 + targetMinute
|
|
191
|
+
|
|
192
|
+
if( targetMinutes > currentMinutes )
|
|
193
|
+
return targetMinutes - currentMinutes
|
|
194
|
+
|
|
195
|
+
// Target is tomorrow
|
|
196
|
+
return ( 24 * 60 - currentMinutes ) + targetMinutes
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getNextOccurrence(
|
|
200
|
+
targetHour: number,
|
|
201
|
+
targetMinute: number = 0,
|
|
202
|
+
onlyFuture: boolean = true
|
|
203
|
+
): { minutesFromNow: number; timestamp: Timestamp } {
|
|
204
|
+
const
|
|
205
|
+
current = this.getTimeOfDay(),
|
|
206
|
+
currentMinutes = current.hour * 60 + current.minute,
|
|
207
|
+
targetMinutes = targetHour * 60 + targetMinute,
|
|
208
|
+
msPerMinute = 60 * 1000
|
|
209
|
+
|
|
210
|
+
let minutesDelta: number
|
|
211
|
+
if( targetMinutes > currentMinutes )
|
|
212
|
+
minutesDelta = targetMinutes - currentMinutes
|
|
213
|
+
|
|
214
|
+
else if( onlyFuture )
|
|
215
|
+
minutesDelta = ( 24 * 60 - currentMinutes ) + targetMinutes
|
|
216
|
+
|
|
217
|
+
else minutesDelta = targetMinutes - currentMinutes
|
|
218
|
+
|
|
219
|
+
const wallDeltaMs = this._clock.toWallMs( minutesDelta * 60 * 1000 )
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
minutesFromNow: minutesDelta,
|
|
223
|
+
timestamp: Date.now() + wallDeltaMs
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ── Private helpers ─────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
private _getDateWithOffset(): Date {
|
|
230
|
+
const
|
|
231
|
+
simTime = this._clock.now,
|
|
232
|
+
offsetMs = this._timezoneOffset * 60 * 1000
|
|
233
|
+
|
|
234
|
+
return new Date( simTime + offsetMs )
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ── Convenience factory ─────────────────────────────────
|
|
238
|
+
|
|
239
|
+
static forTimezone( clock: SimulationClock, timezone: string ): TimeExtension {
|
|
240
|
+
// NOTE: Full IANA timezone support requires a library like 'luxon'
|
|
241
|
+
// For now, uses local system timezone
|
|
242
|
+
const offsetMinutes = -new Date().getTimezoneOffset()
|
|
243
|
+
// In production, use Intl.DateTimeFormat or a library like 'luxon' for IANA conversion
|
|
244
|
+
return new TimeExtension( clock, offsetMinutes )
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ── Scenario helpers for time-based tests ─────────────────
|
|
249
|
+
|
|
250
|
+
export interface TimeScenarioConfig {
|
|
251
|
+
startHour?: number
|
|
252
|
+
startMinute?: number
|
|
253
|
+
timeMultiplier?: number
|
|
254
|
+
timezone?: string
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export class TimeScenarioHelper {
|
|
258
|
+
private _clock: SimulationClock
|
|
259
|
+
private _extension: TimeExtension
|
|
260
|
+
|
|
261
|
+
constructor( clock: SimulationClock, config: TimeScenarioConfig = {} ){
|
|
262
|
+
this._clock = clock
|
|
263
|
+
|
|
264
|
+
// Set initial time if specified
|
|
265
|
+
if( config.startHour !== undefined ) {
|
|
266
|
+
const startDate = new Date( this._clock.now )
|
|
267
|
+
|
|
268
|
+
startDate.setHours( config.startHour, config.startMinute ?? 0, 0, 0 )
|
|
269
|
+
this._clock.setTime( startDate.getTime() )
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if( config.timeMultiplier )
|
|
273
|
+
this._clock.setMultiplier( config.timeMultiplier )
|
|
274
|
+
|
|
275
|
+
this._extension = config.timezone
|
|
276
|
+
? TimeExtension.forTimezone( this._clock, config.timezone )
|
|
277
|
+
: new TimeExtension( this._clock, -new Date().getTimezoneOffset() )
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
get clock(): SimulationClock { return this._clock }
|
|
281
|
+
get time(): TimeExtension { return this._extension }
|
|
282
|
+
|
|
283
|
+
// Fast-forward simulation to a specific time of day
|
|
284
|
+
async advanceToTimeOfDay(
|
|
285
|
+
targetHour: number,
|
|
286
|
+
targetMinute: number = 0,
|
|
287
|
+
maxWaitMs: number = 30000 // 30 seconds real-time max
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
const
|
|
290
|
+
{ minutesFromNow } = this._extension.getNextOccurrence( targetHour, targetMinute ),
|
|
291
|
+
waitWallMs = this._clock.toWallMs( minutesFromNow * 60 * 1000 )
|
|
292
|
+
|
|
293
|
+
if( waitWallMs > maxWaitMs )
|
|
294
|
+
throw new Error(`Target time would require waiting ${waitWallMs}ms, exceeds max ${maxWaitMs}ms`)
|
|
295
|
+
|
|
296
|
+
await this._clock.simSleep( minutesFromNow * 60 * 1000 )
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Usage Examples
|
|
301
|
+
// Logistics: Time-of-day for delivery windows
|
|
302
|
+
// typescript
|
|
303
|
+
// import { DefaultSimulationClock } from './core/clock'
|
|
304
|
+
// import { TimeExtension } from './extension/time.ext'
|
|
305
|
+
|
|
306
|
+
// const clock = new DefaultSimulationClock({ timeScale: 60 }) // 1 real sec = 1 sim minute
|
|
307
|
+
// clock.startTicks()
|
|
308
|
+
|
|
309
|
+
// const timeExt = new TimeExtension( clock, -480 ) // PST
|
|
310
|
+
|
|
311
|
+
// // Check if within delivery window
|
|
312
|
+
// const isWithinWindow = timeExt.isWithinTimeWindow({
|
|
313
|
+
// startHour: 9,
|
|
314
|
+
// endHour: 17,
|
|
315
|
+
// startMinute: 0,
|
|
316
|
+
// endMinute: 0
|
|
317
|
+
// })
|
|
318
|
+
|
|
319
|
+
// // Adjust ETA for rush hour
|
|
320
|
+
// const baseEta = 30 // minutes
|
|
321
|
+
// const adjustedEta = timeExt.calculateAdjustedEta( baseEta, 1.2 )
|
|
322
|
+
// Smart Home: Day part detection
|
|
323
|
+
// typescript
|
|
324
|
+
// const dayPart = timeExt.getDayPart()
|
|
325
|
+
|
|
326
|
+
// switch( dayPart ) {
|
|
327
|
+
// case 'dawn':
|
|
328
|
+
// // Gradually increase lights
|
|
329
|
+
// break
|
|
330
|
+
// case 'night':
|
|
331
|
+
// // Arm security system, turn off non-essential devices
|
|
332
|
+
// break
|
|
333
|
+
// }
|
|
334
|
+
// Mobility: Rush hour detection
|
|
335
|
+
// typescript
|
|
336
|
+
// if( timeExt.isRushHour('urban') ) {
|
|
337
|
+
// // Reroute to avoid congested areas
|
|
338
|
+
// // Increase surge pricing
|
|
339
|
+
// }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/index.ts — Will public API
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
// Core simulation primitives (includes StorageAdapter, BunStorageAdapter, etc.)
|
|
6
|
+
export * from '#core/index'
|
|
7
|
+
|
|
8
|
+
// All cognitive engines
|
|
9
|
+
export * from '#cognition/index'
|
|
10
|
+
|
|
11
|
+
// Public types (OutboxMessage, effectorInvocation, ActionRequest, ActionResult, etc.)
|
|
12
|
+
export * from '#types'
|
|
13
|
+
|
|
14
|
+
// Deployment layer — WillStem, WillConfig, WillSummary, CognitiveHealth, TickListener …
|
|
15
|
+
export {
|
|
16
|
+
WillStem,
|
|
17
|
+
type WillInstance,
|
|
18
|
+
type WillSummary,
|
|
19
|
+
type CognitiveHealth,
|
|
20
|
+
type WillStatus,
|
|
21
|
+
type TickListener,
|
|
22
|
+
type SimulationEventListener,
|
|
23
|
+
type StateSnapshot,
|
|
24
|
+
OUTBOX_TTL_TICKS,
|
|
25
|
+
} from '#stem/index'
|
|
26
|
+
|
|
27
|
+
export { assembleMind, type WillConfig } from '#stem/mind'
|
|
28
|
+
|
|
29
|
+
// External transport — the unified bidirectional envelope channel between a Will
|
|
30
|
+
// and its host peer (backend). Exposes the transport interface, every envelope
|
|
31
|
+
// type, and the three implementations:
|
|
32
|
+
// • SocketIoTransport — production (Will = client, backend hosts the server)
|
|
33
|
+
// • StreamTransport — live in-process consumer (per-channel demux via on())
|
|
34
|
+
// • LoopbackTransport — deterministic in-memory transport for tests/replay
|
|
35
|
+
export * from '#stem/tracts/transport/index'
|
|
36
|
+
|
|
37
|
+
// Vector memory — semantic episodic recall. Exposes the adapter, the VectorIndex
|
|
38
|
+
// interface, and the embedders so a host can inject a custom backend (e.g.
|
|
39
|
+
// pgvector) via WillConfig.vectorMemoryAdapter. (EmbeddingProvider / VectorRecord
|
|
40
|
+
// / VectorQueryResult / VectorQueryFilter are already exported via #cognition.)
|
|
41
|
+
export {
|
|
42
|
+
DefaultVectorMemoryAdapter,
|
|
43
|
+
type VectorMemoryAdapter,
|
|
44
|
+
type VectorIndex,
|
|
45
|
+
OpenAICompatibleEmbedder,
|
|
46
|
+
MockEmbedder,
|
|
47
|
+
type EmbeddingProvider,
|
|
48
|
+
type VectorRecord,
|
|
49
|
+
type VectorQueryResult,
|
|
50
|
+
type VectorQueryFilter,
|
|
51
|
+
type VectorMemoryConfig,
|
|
52
|
+
} from '#cognition/memory/index'
|
|
53
|
+
|
|
54
|
+
// Profile registry — built-in profiles are auto-registered when this package is imported
|
|
55
|
+
// (mind.ts imports '#profiles/built-in' as a side effect).
|
|
56
|
+
export { resolveProfile, listProfiles, type WorldProfile } from '#profiles/index'
|
|
57
|
+
|
|
58
|
+
// PMA (Personal Mind Archive) snapshot types
|
|
59
|
+
export type {
|
|
60
|
+
PMASnapshot,
|
|
61
|
+
PMAIdentity,
|
|
62
|
+
PMABelief,
|
|
63
|
+
PMAGoal,
|
|
64
|
+
PMAEmotionalBaseline,
|
|
65
|
+
PMABehavioral,
|
|
66
|
+
} from '#pma/index'
|
|
67
|
+
|
|
68
|
+
// PMA eval harness — behavioral fidelity scoring
|
|
69
|
+
export { PMAEvalHarness } from '#pma/eval'
|
|
70
|
+
export type {
|
|
71
|
+
ReconstructionFidelityReport,
|
|
72
|
+
ReconstructionFidelityScores,
|
|
73
|
+
BehavioralProbeResult,
|
|
74
|
+
PMAProbe,
|
|
75
|
+
} from '#pma/eval'
|
|
76
|
+
|
|
77
|
+
// Sensory input types — used by callers of WillManager.ingestText()
|
|
78
|
+
export type { TextMessage, VoiceChunk, SensoryInput } from '#senses/index'
|
|
79
|
+
|
|
80
|
+
// ── SDK facade — the ergonomic embedding API (recommended entry point) ──
|
|
81
|
+
// `Will.create()` wraps WillStem in the shape a developer expects: on('message'),
|
|
82
|
+
// effector(name, handler), say(), state(), hibernate()/wake(). Drop to `.stem`
|
|
83
|
+
// for the full contract. See src/sdk/will.ts + examples/effectors.ts.
|
|
84
|
+
export { Will } from '#sdk/will'
|
|
85
|
+
export type {
|
|
86
|
+
CreateWillOptions,
|
|
87
|
+
WillMessage,
|
|
88
|
+
WillStateSummary,
|
|
89
|
+
EffectorHandler,
|
|
90
|
+
EffectorResult,
|
|
91
|
+
} from '#sdk/will'
|
package/src/llm/gate.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/llm/gate.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Global concurrency gate for all LLM calls.
|
|
7
|
+
*
|
|
8
|
+
* Two protections:
|
|
9
|
+
* 1. Semaphore — caps simultaneous in-flight requests so independent
|
|
10
|
+
* engines (decision, semantic, planning…) can run in parallel up to
|
|
11
|
+
* the concurrency limit without flooding the API.
|
|
12
|
+
* 2. Retry with exponential backoff — on 429 the call waits and retries.
|
|
13
|
+
*
|
|
14
|
+
* No minimum interval is enforced. A fixed inter-call floor causes
|
|
15
|
+
* starvation: every engine serialises into a single queue and slower
|
|
16
|
+
* engines (semantic integrator) never get a timely slot because faster
|
|
17
|
+
* engines (decision) keep resetting the clock. The retry mechanism is
|
|
18
|
+
* the right tool for actual rate-limit overages.
|
|
19
|
+
*
|
|
20
|
+
* Configured via env vars:
|
|
21
|
+
* WILL_LLM_CONCURRENCY max simultaneous LLM calls (default 2)
|
|
22
|
+
* WILL_LLM_MAX_RETRIES retries before giving up on a 429 (default 4)
|
|
23
|
+
* WILL_LLM_RETRY_BASE_MS first retry wait, doubles each attempt (default 2000)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { logger } from '#core/logger'
|
|
27
|
+
|
|
28
|
+
const MAX_CONCURRENT = parseInt( process.env.WILL_LLM_CONCURRENCY ?? '2')
|
|
29
|
+
|
|
30
|
+
// Read lazily (per retry, not per module load) so tests and live tuning can
|
|
31
|
+
// adjust the backoff without re-importing the module. Jitter scales with the
|
|
32
|
+
// base delay (base/2 ⇒ up to 1000 ms at the 2000 ms default — unchanged).
|
|
33
|
+
const maxRetries = () => parseInt( process.env.WILL_LLM_MAX_RETRIES ?? '4')
|
|
34
|
+
const baseDelayMs = () => parseInt( process.env.WILL_LLM_RETRY_BASE_MS ?? '2000')
|
|
35
|
+
|
|
36
|
+
// ── Semaphore ─────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export class LLMSemaphore {
|
|
39
|
+
private _running = 0
|
|
40
|
+
private readonly _max: number
|
|
41
|
+
private _queue: Array<() => void> = []
|
|
42
|
+
|
|
43
|
+
constructor( max: number ){ this._max = max }
|
|
44
|
+
|
|
45
|
+
async acquire(): Promise<() => void> {
|
|
46
|
+
if( this._running < this._max ){
|
|
47
|
+
this._running++
|
|
48
|
+
return this._release()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// No free slot — wait until one is handed off directly to us. The releasing
|
|
52
|
+
// call transfers its slot without decrementing _running, so we must NOT
|
|
53
|
+
// increment here: we already own the slot the moment we resume.
|
|
54
|
+
await new Promise<void>( resolve => this._queue.push( resolve ) )
|
|
55
|
+
return this._release()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private _release(): () => void {
|
|
59
|
+
let done = false
|
|
60
|
+
return () => {
|
|
61
|
+
if( done ) return
|
|
62
|
+
done = true
|
|
63
|
+
|
|
64
|
+
// Hand the slot directly to the next waiter, keeping _running unchanged so
|
|
65
|
+
// a concurrent acquire() can't slip into a transient gap and over-admit
|
|
66
|
+
// past _max. Only decrement when nobody is waiting.
|
|
67
|
+
const next = this._queue.shift()
|
|
68
|
+
if( next ) next()
|
|
69
|
+
else this._running--
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get running(): number { return this._running }
|
|
74
|
+
get queued(): number { return this._queue.length }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const llmGate = new LLMSemaphore( MAX_CONCURRENT )
|
|
78
|
+
|
|
79
|
+
// ── Rate-limit detection ──────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export function isRateLimitError( err: unknown ): boolean {
|
|
82
|
+
if( !( err instanceof Error ) ) return false
|
|
83
|
+
const msg = err.message
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
msg.includes('rate_limit_error') ||
|
|
87
|
+
( err as { statusCode?: number } ).statusCode === 429 ||
|
|
88
|
+
msg.includes('rate limit') ||
|
|
89
|
+
msg.includes('429')
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ── Gate + retry wrapper ──────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Run `fn` through the global semaphore with automatic 429 retry.
|
|
97
|
+
*
|
|
98
|
+
* - Waits for a slot before calling `fn`
|
|
99
|
+
* - On 429: releases the slot, waits (exponential backoff + jitter), retries
|
|
100
|
+
* - Throws after MAX_RETRIES exhausted, or for non-retryable errors
|
|
101
|
+
*/
|
|
102
|
+
export async function withGate<T>(
|
|
103
|
+
fn: () => Promise<T>,
|
|
104
|
+
label: string,
|
|
105
|
+
gate: LLMSemaphore = llmGate
|
|
106
|
+
): Promise<T> {
|
|
107
|
+
let attempt = 0
|
|
108
|
+
|
|
109
|
+
// eslint-disable-next-line no-constant-condition
|
|
110
|
+
while( true ){
|
|
111
|
+
const release = await gate.acquire()
|
|
112
|
+
let retryDelay: number | null = null
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
const result = await fn()
|
|
116
|
+
return result
|
|
117
|
+
}
|
|
118
|
+
catch( err ){
|
|
119
|
+
if( isRateLimitError( err ) && attempt < maxRetries() ){
|
|
120
|
+
attempt++
|
|
121
|
+
const base = baseDelayMs()
|
|
122
|
+
retryDelay = Math.min(
|
|
123
|
+
60_000,
|
|
124
|
+
base * Math.pow( 2, attempt ) + Math.random() * ( base / 2 )
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
else throw err
|
|
128
|
+
}
|
|
129
|
+
finally { release() }
|
|
130
|
+
|
|
131
|
+
// Slot released in `finally` above — the backoff wait happens OUTSIDE the
|
|
132
|
+
// slot-holding region, so a rate-limited call no longer starves the other
|
|
133
|
+
// engines of concurrency while it sleeps.
|
|
134
|
+
logger.warn(
|
|
135
|
+
`[LLMGate] ${label} rate limited — retry ${attempt}/${maxRetries()} in ${Math.round( retryDelay! )}ms` +
|
|
136
|
+
` (running=${gate.running} queued=${gate.queued})`
|
|
137
|
+
)
|
|
138
|
+
await new Promise( r => setTimeout( r, retryDelay! ) )
|
|
139
|
+
}
|
|
140
|
+
}
|