@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,628 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/executive.engine/facet.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* ExecutiveFacet — focused attentional instance of the executive consciousness.
|
|
7
|
+
*
|
|
8
|
+
* Facets are true projections of the master executive identity:
|
|
9
|
+
* - Share master's identity, values, beliefs, and memories
|
|
10
|
+
* - Access LIVE state each reasoning cycle (not frozen snapshot)
|
|
11
|
+
* - Use the same PromptFactory as master for consistent prompting
|
|
12
|
+
* - Only the FOCUS section differentiates them from master
|
|
13
|
+
*
|
|
14
|
+
* The facet syncs bidirectionally with the master via cognitive bus events.
|
|
15
|
+
* The focus section is provided by the creator engine (PlanningEngine, etc.)
|
|
16
|
+
* via setFocus() — the facet has no hardcoded assumptions about what it's
|
|
17
|
+
* reasoning about.
|
|
18
|
+
*
|
|
19
|
+
* Part of Shard 3 (Executive Layer) — runs asynchronously outside tick cycle.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { logger } from '#core/logger'
|
|
23
|
+
import type { ReadonlySimulationState } from '#core/types'
|
|
24
|
+
import { wallClock } from '#core/wall.clock'
|
|
25
|
+
import type { CognitiveBus } from '#cognition/bus'
|
|
26
|
+
import type { CompletionInbox } from '#cognition/completion.inbox'
|
|
27
|
+
import type { LLMDirector, LLMCallMeta } from '#llm/index'
|
|
28
|
+
import type { ExecutiveOutputFull, IdeationCandidate } from '#faculties/executive.engine/types'
|
|
29
|
+
import type { ContextDependencies } from '#faculties/executive.engine/context'
|
|
30
|
+
import type { SessionLogger } from '#stem/tracts/session.logger'
|
|
31
|
+
import {
|
|
32
|
+
PromptFactory,
|
|
33
|
+
type PromptDependencies,
|
|
34
|
+
type FocusSection
|
|
35
|
+
} from '#faculties/executive.engine/prompt.factory'
|
|
36
|
+
import { parseResponse, buildFallbackOutput } from '#faculties/executive.engine/parser'
|
|
37
|
+
import { selectProcess, ideationTemperature, DELIBERATE_THRESHOLD } from '#faculties/executive.engine/effort.gate'
|
|
38
|
+
import { proposeCandidates } from '#faculties/executive.engine/deliberate.reasoning'
|
|
39
|
+
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
40
|
+
|
|
41
|
+
// ── Facet event types ─────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generic report interface — the facet doesn't interpret this.
|
|
45
|
+
* The creator engine defines the structure and interprets responses.
|
|
46
|
+
*/
|
|
47
|
+
export interface FacetReport {
|
|
48
|
+
/** The reason for this report (creator-defined) */
|
|
49
|
+
type: string
|
|
50
|
+
/** The payload content (creator-defined structure) */
|
|
51
|
+
payload: unknown
|
|
52
|
+
/** Optional context identifier for routing */
|
|
53
|
+
contextId?: string
|
|
54
|
+
/** Optional dynamic instructions to append to the user message */
|
|
55
|
+
instructions?: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface FacetDecision {
|
|
59
|
+
facetId: string
|
|
60
|
+
/** The reason/report type this decision responds to */
|
|
61
|
+
respondingToType: string
|
|
62
|
+
/** The decision payload (creator-defined structure) */
|
|
63
|
+
decision: unknown
|
|
64
|
+
reasoning: string
|
|
65
|
+
confidence: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type FacetEventListener = ( decision: FacetDecision ) => void
|
|
69
|
+
|
|
70
|
+
export interface ExecutiveFacetHandle {
|
|
71
|
+
/** Push a report into the facet — triggers a reasoning cycle */
|
|
72
|
+
report: ( report: FacetReport ) => void
|
|
73
|
+
/** Subscribe to facet decisions — returns unsubscribe function */
|
|
74
|
+
subscribe: ( listener: FacetEventListener ) => () => void
|
|
75
|
+
/** The facet's unique identifier */
|
|
76
|
+
facetId: string
|
|
77
|
+
/** Destroy the facet — unsubscribes from bus, cleans up */
|
|
78
|
+
destroy: () => void
|
|
79
|
+
/** Set the focus for this facet (called by creator before first report) */
|
|
80
|
+
setFocus: ( focus: FocusSection ) => void
|
|
81
|
+
/** Set the current state reference (called by orchestrator) */
|
|
82
|
+
setStateRef: ( state: ReadonlySimulationState ) => void
|
|
83
|
+
/**
|
|
84
|
+
* Register a per-facet chunk handler.
|
|
85
|
+
* Called when the creating engine wants per-entity token streaming
|
|
86
|
+
* (e.g. AuditionEngine, one handler per conversation session).
|
|
87
|
+
* Fires for every LLM token during `_reason()` — in addition to any
|
|
88
|
+
* global `_chunkBroadcaster` wired via `setChunkBroadcaster()` on the
|
|
89
|
+
* master ExecutiveEngine.
|
|
90
|
+
*/
|
|
91
|
+
onChunk: ( handler: ( chunk: string ) => void ) => void
|
|
92
|
+
/**
|
|
93
|
+
* Register a callback fired when the supervisor REAPS this facet (idle TTL or
|
|
94
|
+
* LRU eviction), as opposed to an explicit `destroy()`. Lets the owner (e.g.
|
|
95
|
+
* AuditionEngine) drop its handle reference + session state when the facet is
|
|
96
|
+
* reclaimed out from under it. Not fired by `destroy()`.
|
|
97
|
+
*/
|
|
98
|
+
onReaped: ( handler: () => void ) => void
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── ExecutiveFacet ────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export class ExecutiveFacet {
|
|
104
|
+
readonly facetId: string
|
|
105
|
+
|
|
106
|
+
private _bus: CognitiveBus
|
|
107
|
+
private _llmDirector: LLMDirector
|
|
108
|
+
private _contextDeps: ContextDependencies
|
|
109
|
+
private _promptDeps: PromptDependencies
|
|
110
|
+
private _willId: string
|
|
111
|
+
private _listeners = new Set<FacetEventListener>()
|
|
112
|
+
private _destroyed = false
|
|
113
|
+
private _sessionLogger: SessionLogger | null = null
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Tick-boundary landing (see cognition/completion.inbox.ts). When present,
|
|
117
|
+
* subscriber notification is STAGED at LLM-promise resolution and applied by
|
|
118
|
+
* the CognitiveOrchestrator in Phase 2 — so PlanningEngine / AuditionEngine
|
|
119
|
+
* effects (plan mutations, outbox writes) never interleave with a tick in
|
|
120
|
+
* flight. Null = legacy direct notification (bare facets in unit tests).
|
|
121
|
+
*/
|
|
122
|
+
private _inbox: CompletionInbox | null = null
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Reports waiting for the per-tick pump (tick-discipline mode only). Reasoning
|
|
126
|
+
* launches from pump(), never from report() — see report() for why.
|
|
127
|
+
*/
|
|
128
|
+
private _pendingReports: FacetReport[] = []
|
|
129
|
+
|
|
130
|
+
/** Current focus for this facet — set by creator (PlanningEngine, etc.) */
|
|
131
|
+
private _currentFocus: FocusSection | null = null
|
|
132
|
+
|
|
133
|
+
/** Accumulated reasoning history for continuity across report() calls */
|
|
134
|
+
private _facetReasoningHistory: string[] = []
|
|
135
|
+
private _masterSyncHistory: string[] = []
|
|
136
|
+
|
|
137
|
+
/** Confidence of this facet's *previous* decision — a dual-process gate signal. */
|
|
138
|
+
private _lastConfidence = 0.5
|
|
139
|
+
|
|
140
|
+
/** Current state reference (updated by orchestrator each tick) */
|
|
141
|
+
private _currentStateRef: ReadonlySimulationState | null = null
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Sim tick of the last activity (report). Used by FacetSupervisor's idle
|
|
145
|
+
* reaper + LRU eviction. Sim tick (deterministic), never wallClock.
|
|
146
|
+
*/
|
|
147
|
+
private _lastActiveTick = 0
|
|
148
|
+
get lastActiveTick(): number { return this._lastActiveTick }
|
|
149
|
+
/** Stamp activity at `tick` (called at spawn and on each report). */
|
|
150
|
+
markActive( tick: number ): void { this._lastActiveTick = tick }
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Per-facet chunk handler — fires for every LLM token during _reason().
|
|
154
|
+
* Set by the creating engine (e.g. AuditionEngine) for entity-scoped streaming.
|
|
155
|
+
* Independent from the master's global _chunkBroadcaster.
|
|
156
|
+
*/
|
|
157
|
+
private _chunkHandler: (( chunk: string ) => void) | null = null
|
|
158
|
+
|
|
159
|
+
constructor(
|
|
160
|
+
facetId: string,
|
|
161
|
+
bus: CognitiveBus,
|
|
162
|
+
llmDirector: LLMDirector,
|
|
163
|
+
contextDeps: ContextDependencies,
|
|
164
|
+
promptDeps: PromptDependencies,
|
|
165
|
+
willId: string,
|
|
166
|
+
inbox: CompletionInbox | null = null
|
|
167
|
+
){
|
|
168
|
+
this.facetId = facetId
|
|
169
|
+
this._bus = bus
|
|
170
|
+
this._llmDirector = llmDirector
|
|
171
|
+
this._contextDeps = contextDeps
|
|
172
|
+
this._promptDeps = promptDeps
|
|
173
|
+
this._willId = willId
|
|
174
|
+
this._inbox = inbox
|
|
175
|
+
|
|
176
|
+
// Subscribe to master sync events
|
|
177
|
+
this._bus.subscribe(
|
|
178
|
+
`executive-facet-${facetId}`,
|
|
179
|
+
[ 'executive.master.sync' ],
|
|
180
|
+
this._onMasterSync
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
logger.info( `[executive.facet] ${facetId} created` )
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Public API ─────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Set the focus for this facet.
|
|
190
|
+
* Called by the creator (PlanningEngine, etc.) before first report.
|
|
191
|
+
*/
|
|
192
|
+
setFocus( focus: FocusSection ): void {
|
|
193
|
+
this._currentFocus = focus
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Set the current state reference.
|
|
198
|
+
* Called by the orchestrator each tick to keep facet in sync.
|
|
199
|
+
*/
|
|
200
|
+
setStateRef( state: ReadonlySimulationState ): void {
|
|
201
|
+
this._currentStateRef = state
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
attachSessionLogger( logger: SessionLogger | null ): void {
|
|
205
|
+
this._sessionLogger = logger
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Register a per-facet chunk handler for entity-scoped token streaming. */
|
|
209
|
+
setChunkHandler( handler: ( chunk: string ) => void ): void {
|
|
210
|
+
this._chunkHandler = handler
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async report( report: FacetReport ): Promise<void> {
|
|
214
|
+
if( this._destroyed ) return
|
|
215
|
+
|
|
216
|
+
// Stamp activity so the supervisor's idle reaper keeps an active facet alive.
|
|
217
|
+
this.markActive( ( this._currentStateRef?.tick as number ) ?? this._lastActiveTick )
|
|
218
|
+
|
|
219
|
+
logger.info(
|
|
220
|
+
`[executive.facet] ${this.facetId} received report: type=${report.type}`
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
// Tick-discipline mode (inbox attached — the same signal that quantizes
|
|
224
|
+
// decision LANDING): reasoning must not START at raw report time either.
|
|
225
|
+
// A report can arrive from any wall-clock context (an ingest chain, another
|
|
226
|
+
// chain's resolution), and launching _reason() there makes both the issue
|
|
227
|
+
// tick and the prompt bytes race-dependent — the prompt reads live manager
|
|
228
|
+
// state at whatever moment the chain happened to run. Queue instead; the
|
|
229
|
+
// ExecutiveEngine pumps every facet once per tick (react() → supervisor
|
|
230
|
+
// .pump()) with that tick's frozen snapshot, so reasoning launches at a
|
|
231
|
+
// deterministic point with deterministic inputs. See
|
|
232
|
+
// .TODO/FACET_REPLAY_DETERMINISM.md (layer 3).
|
|
233
|
+
if( this._inbox ){
|
|
234
|
+
this._pendingReports.push( report )
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Legacy (bare facets in unit tests): trigger reasoning immediately.
|
|
239
|
+
this._reason( report ).catch( err =>
|
|
240
|
+
logger.error( `[executive.facet] ${this.facetId} reasoning error:`, err )
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Per-tick pump — called by the FacetSupervisor from the ExecutiveEngine's
|
|
246
|
+
* react() with the tick's FROZEN snapshot. Refreshes the state reference,
|
|
247
|
+
* then launches reasoning for every queued report, FIFO. The launch happens
|
|
248
|
+
* at a fixed point in the serial engine order, so prompt inputs are a pure
|
|
249
|
+
* function of (tick, seed, recorded external inputs) — replayable.
|
|
250
|
+
*/
|
|
251
|
+
pump( state: ReadonlySimulationState ): void {
|
|
252
|
+
if( this._destroyed ) return
|
|
253
|
+
this.setStateRef( state )
|
|
254
|
+
|
|
255
|
+
if( this._pendingReports.length === 0 ) return
|
|
256
|
+
const batch = this._pendingReports
|
|
257
|
+
this._pendingReports = []
|
|
258
|
+
|
|
259
|
+
for( const report of batch )
|
|
260
|
+
this._reason( report ).catch( err =>
|
|
261
|
+
logger.error( `[executive.facet] ${this.facetId} reasoning error:`, err )
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
subscribe( listener: FacetEventListener ): () => void {
|
|
266
|
+
this._listeners.add( listener )
|
|
267
|
+
return () => { this._listeners.delete( listener ) }
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
destroy(): void {
|
|
271
|
+
if( this._destroyed ) return
|
|
272
|
+
this._destroyed = true
|
|
273
|
+
|
|
274
|
+
this._bus.unsubscribe( `executive-facet-${this.facetId}` )
|
|
275
|
+
this._listeners.clear()
|
|
276
|
+
|
|
277
|
+
logger.info( `[executive.facet] ${this.facetId} destroyed` )
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ── Master sync ────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
private _onMasterSync = ( event: any ): void => {
|
|
283
|
+
if( this._destroyed ) return
|
|
284
|
+
|
|
285
|
+
const payload = event.payload as {
|
|
286
|
+
facetId?: string
|
|
287
|
+
reasoning?: string
|
|
288
|
+
confidence?: number
|
|
289
|
+
actionTypes?: string[]
|
|
290
|
+
coherenceVersion?: number
|
|
291
|
+
tick?: number
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Master syncs to all facets or a specific one
|
|
295
|
+
if( payload.facetId && payload.facetId !== this.facetId ) return
|
|
296
|
+
|
|
297
|
+
logger.info( `[executive.facet] ${this.facetId} synced from master (tick=${payload.tick})` )
|
|
298
|
+
|
|
299
|
+
// Store master's latest reasoning for context in next report()
|
|
300
|
+
if( payload.reasoning )
|
|
301
|
+
this._masterSyncHistory.push( `[Master sync — tick ${payload.tick}] ${payload.reasoning.slice( 0, 400 )}` )
|
|
302
|
+
|
|
303
|
+
// Keep only last 5 sync entries
|
|
304
|
+
if( this._masterSyncHistory.length > 5 )
|
|
305
|
+
this._masterSyncHistory = this._masterSyncHistory.slice( -5 )
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── Reasoning ──────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
private async _reason( report: FacetReport ): Promise<void> {
|
|
311
|
+
// Ensure we have current state
|
|
312
|
+
if( !this._currentStateRef )
|
|
313
|
+
throw new Error( `[executive.facet] ${this.facetId} no state reference available` )
|
|
314
|
+
|
|
315
|
+
if( !this._currentFocus )
|
|
316
|
+
throw new Error( `[executive.facet] ${this.facetId} no focus set. Call setFocus() before report().` )
|
|
317
|
+
|
|
318
|
+
const currentState = this._currentStateRef
|
|
319
|
+
|
|
320
|
+
// Build fresh context from current state
|
|
321
|
+
// A focus may supply a recall query (e.g. the live conversation message) to
|
|
322
|
+
// drive the single "## Relevant Memories" section — one recall surface (§5).
|
|
323
|
+
const execContext = await PromptFactory.buildFreshContext( this._contextDeps, currentState, this._currentFocus?.recallQuery )
|
|
324
|
+
|
|
325
|
+
const qualityModulation = PromptFactory.computeQualityModulation( currentState )
|
|
326
|
+
const epistemicUncertainty = PromptFactory.computeEpistemicUncertainty( execContext, currentState )
|
|
327
|
+
|
|
328
|
+
// Build focus section — the creator is responsible for providing
|
|
329
|
+
// the appropriate content via setFocus()
|
|
330
|
+
const focus: FocusSection = this._masterSyncHistory.length > 0
|
|
331
|
+
? {
|
|
332
|
+
...this._currentFocus,
|
|
333
|
+
content: `${this._currentFocus.content}\n\n## Master Consciousness Updates\n${this._masterSyncHistory.join('\n')}`
|
|
334
|
+
}
|
|
335
|
+
: this._currentFocus
|
|
336
|
+
|
|
337
|
+
// Build system prompt using PromptFactory — same schema as master, [REPLY] gated out.
|
|
338
|
+
const systemPrompt = PromptFactory.buildSystemPrompt( {
|
|
339
|
+
context: execContext,
|
|
340
|
+
focus,
|
|
341
|
+
deps: this._promptDeps,
|
|
342
|
+
mode: 'facet'
|
|
343
|
+
} )
|
|
344
|
+
|
|
345
|
+
// Build user message — same live-state baseline as master.
|
|
346
|
+
// Creator-engine context is injected via reportContent (per-report payload)
|
|
347
|
+
// and focus.instructions (per-focus recurring context).
|
|
348
|
+
// outputFormat overrides the standard schema when the creating engine needs it.
|
|
349
|
+
//
|
|
350
|
+
// Facet reasoning continuity: prepend this facet's own prior reasoning so it
|
|
351
|
+
// isn't cold on each cycle. Injected before the caller's instructions so the
|
|
352
|
+
// caller content always comes last (highest recency bias from the LLM).
|
|
353
|
+
const continuityBlock = this._facetReasoningHistory.length > 0
|
|
354
|
+
? `## My Prior Reasoning (this facet)\n${this._facetReasoningHistory.join( '\n' )}`
|
|
355
|
+
: ''
|
|
356
|
+
|
|
357
|
+
const reportContent = [
|
|
358
|
+
continuityBlock,
|
|
359
|
+
report.instructions ?? ''
|
|
360
|
+
].filter( Boolean ).join( '\n\n' ) || undefined
|
|
361
|
+
|
|
362
|
+
// Dual-process — a facet is "the master over its focus", so it deliberates the same
|
|
363
|
+
// way. The a-priori effort gate (shared `selectProcess`) picks fast vs deliberate
|
|
364
|
+
// using the SAME developable `deliberateThreshold` as master (engine-config-executive),
|
|
365
|
+
// so the Will's deliberativeness is unified across master and every focus. On the
|
|
366
|
+
// deliberate path a propose pass generates options FOR THIS FOCUS (e.g. a planning
|
|
367
|
+
// facet's continue/revise/replan/abandon), injected into the decision call below.
|
|
368
|
+
const deliberateThreshold = readEffectiveParams( currentState, 'engine-config-executive' ).deliberateThreshold ?? DELIBERATE_THRESHOLD
|
|
369
|
+
const processSelection = selectProcess( {
|
|
370
|
+
epistemicUncertainty,
|
|
371
|
+
priorConfidence: this._lastConfidence,
|
|
372
|
+
novelty: currentState.metrics.get( 'perception.novelty' ) ?? 0,
|
|
373
|
+
stressLoad: currentState.metrics.get( 'stress.load' ) ?? 0,
|
|
374
|
+
// A facet's "stakes-bearing moment" is a live message awaiting reply (conversation
|
|
375
|
+
// facets set focus.recallQuery to it); planning/other facets rely on uncertainty.
|
|
376
|
+
hasPendingMessage: !!this._currentFocus?.recallQuery,
|
|
377
|
+
}, deliberateThreshold )
|
|
378
|
+
|
|
379
|
+
let ideationCandidates: IdeationCandidate[] | undefined
|
|
380
|
+
if( processSelection.process === 'deliberate' ){
|
|
381
|
+
const proposeTemperature = ideationTemperature( execContext.identity.traits[ 'creativity' ] ?? 0.5 )
|
|
382
|
+
const ideationUserMessage = PromptFactory.buildUserMessage( {
|
|
383
|
+
context: execContext,
|
|
384
|
+
state: currentState,
|
|
385
|
+
qualityModulation,
|
|
386
|
+
epistemicUncertainty,
|
|
387
|
+
pendingMessages: [],
|
|
388
|
+
focus,
|
|
389
|
+
deps: this._promptDeps,
|
|
390
|
+
recentActionTypes: [],
|
|
391
|
+
mode: 'facet',
|
|
392
|
+
reportContent,
|
|
393
|
+
outputFormat: PromptFactory.buildIdeationFormatInstruction(),
|
|
394
|
+
} )
|
|
395
|
+
ideationCandidates = await proposeCandidates( {
|
|
396
|
+
director: this._llmDirector,
|
|
397
|
+
systemPrompt,
|
|
398
|
+
ideationUserMessage,
|
|
399
|
+
tick: currentState.tick,
|
|
400
|
+
proposeTemperature,
|
|
401
|
+
meta: { category: 'executive', attribute: 'facet', function: 'ideation', scope: this.facetId },
|
|
402
|
+
} )
|
|
403
|
+
logger.info(
|
|
404
|
+
`[executive.facet] ${this.facetId} ◆ deliberate propose tick=${currentState.tick} ` +
|
|
405
|
+
`candidates=${ideationCandidates?.length ?? 0} temp=${proposeTemperature.toFixed( 2 )}`
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const userMessage = PromptFactory.buildUserMessage( {
|
|
410
|
+
context: execContext,
|
|
411
|
+
state: currentState,
|
|
412
|
+
qualityModulation,
|
|
413
|
+
epistemicUncertainty,
|
|
414
|
+
pendingMessages: [],
|
|
415
|
+
focus,
|
|
416
|
+
deps: this._promptDeps,
|
|
417
|
+
recentActionTypes: [],
|
|
418
|
+
mode: 'facet',
|
|
419
|
+
reportContent,
|
|
420
|
+
outputFormat: focus.outputFormat,
|
|
421
|
+
ideationCandidates
|
|
422
|
+
} )
|
|
423
|
+
|
|
424
|
+
// Log the call
|
|
425
|
+
this._sessionLogger?.write( {
|
|
426
|
+
type: 'executive.facet.call',
|
|
427
|
+
tick: currentState.tick,
|
|
428
|
+
facetId: this.facetId,
|
|
429
|
+
promptChars: systemPrompt.length + userMessage.length,
|
|
430
|
+
promptTokensEst: Math.round( ( systemPrompt.length + userMessage.length ) / 4 ),
|
|
431
|
+
systemChars: systemPrompt.length,
|
|
432
|
+
userChars: userMessage.length
|
|
433
|
+
} )
|
|
434
|
+
|
|
435
|
+
let output: ExecutiveOutputFull
|
|
436
|
+
const llmStart = wallClock() // perf timing only — latency is telemetry, never replay state
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
// Use streaming call when a per-facet chunk handler is registered —
|
|
440
|
+
// this enables entity-scoped token delivery (e.g. AuditionEngine SSE).
|
|
441
|
+
// Falls back to regular call when no handler is present.
|
|
442
|
+
const facetMeta: LLMCallMeta = {
|
|
443
|
+
category: 'executive',
|
|
444
|
+
attribute: 'facet',
|
|
445
|
+
function: this._currentFocus?.function ?? 'facet',
|
|
446
|
+
scope: this.facetId,
|
|
447
|
+
}
|
|
448
|
+
const result = this._chunkHandler
|
|
449
|
+
? await this._llmDirector.callStream( systemPrompt, userMessage, currentState.tick, this._chunkHandler, undefined, facetMeta )
|
|
450
|
+
: await this._llmDirector.call( systemPrompt, userMessage, currentState.tick, undefined, facetMeta )
|
|
451
|
+
output = parseResponse( result.text, currentState, [] )
|
|
452
|
+
|
|
453
|
+
// System 2 — retain the options this facet weighed for its focus decision.
|
|
454
|
+
if( ideationCandidates && ideationCandidates.length > 0 )
|
|
455
|
+
output.consideredAlternatives = ideationCandidates.map( c => c.approach || c.description )
|
|
456
|
+
|
|
457
|
+
logger.info(
|
|
458
|
+
`[executive.facet] ${this.facetId} ✓ tick=${currentState.tick} ` +
|
|
459
|
+
`in=${result.inputTok} tok out=${result.outputTok} tok ` +
|
|
460
|
+
`cache=${result.cacheReadTok ?? 0}r/${result.cacheWriteTok ?? 0}w ` +
|
|
461
|
+
`latency=${wallClock() - llmStart}ms`
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
this._sessionLogger?.write( {
|
|
465
|
+
type: 'executive.facet.response',
|
|
466
|
+
tick: currentState.tick,
|
|
467
|
+
facetId: this.facetId,
|
|
468
|
+
latencyMs: wallClock() - llmStart,
|
|
469
|
+
responseChars: result.text.length,
|
|
470
|
+
promptTokens: result.inputTok,
|
|
471
|
+
completionTokens: result.outputTok,
|
|
472
|
+
cacheReadTokens: result.cacheReadTok ?? 0,
|
|
473
|
+
cacheWriteTokens: result.cacheWriteTok ?? 0,
|
|
474
|
+
responseExcerpt: result.text.slice( 0, 600 )
|
|
475
|
+
} )
|
|
476
|
+
}
|
|
477
|
+
catch( err ){
|
|
478
|
+
const msg = err instanceof Error ? err.message : String( err )
|
|
479
|
+
logger.error( `[executive.facet] ${this.facetId} LLM call failed: ${msg.slice( 0, 200 )}` )
|
|
480
|
+
|
|
481
|
+
this._sessionLogger?.write( {
|
|
482
|
+
type: 'executive.facet.response',
|
|
483
|
+
tick: currentState.tick,
|
|
484
|
+
facetId: this.facetId,
|
|
485
|
+
latencyMs: wallClock() - llmStart,
|
|
486
|
+
error: msg.slice( 0, 300 )
|
|
487
|
+
} )
|
|
488
|
+
|
|
489
|
+
output = buildFallbackOutput( currentState, [] )
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Log the parsed output
|
|
493
|
+
this._sessionLogger?.write( {
|
|
494
|
+
type: 'executive.facet.output',
|
|
495
|
+
tick: currentState.tick,
|
|
496
|
+
facetId: this.facetId,
|
|
497
|
+
confidence: output.confidence,
|
|
498
|
+
reasoning: output.reasoning.slice( 0, 500 ),
|
|
499
|
+
actions: output.actions,
|
|
500
|
+
newBeliefs: output.newBeliefs ?? [],
|
|
501
|
+
plansCount: output.plans?.length ?? 0,
|
|
502
|
+
goalsNew: output.newGoals ?? [],
|
|
503
|
+
goalsAbandon: output.goalsToAbandon ?? [],
|
|
504
|
+
hasIntrospection: !!output.introspection,
|
|
505
|
+
hasNarrative: !!output.narrative
|
|
506
|
+
} )
|
|
507
|
+
|
|
508
|
+
// Track this decision's confidence — a dual-process gate signal for the next report.
|
|
509
|
+
this._lastConfidence = output.confidence
|
|
510
|
+
|
|
511
|
+
// Store reasoning for continuity
|
|
512
|
+
this._facetReasoningHistory.push( `[Report: ${report.type}] ${output.reasoning.slice( 0, 400 )}` )
|
|
513
|
+
if( this._facetReasoningHistory.length > 10 )
|
|
514
|
+
this._facetReasoningHistory = this._facetReasoningHistory.slice( -10 )
|
|
515
|
+
|
|
516
|
+
// Convert executive output to facet decision — the creator defines the decision shape
|
|
517
|
+
// via the focus.outputFormat. The facet simply passes through the parsed output
|
|
518
|
+
// as the decision payload.
|
|
519
|
+
const decision: FacetDecision = {
|
|
520
|
+
facetId: this.facetId,
|
|
521
|
+
respondingToType: report.type,
|
|
522
|
+
decision: this._extractDecisionPayload( output, focus ),
|
|
523
|
+
reasoning: output.reasoning,
|
|
524
|
+
confidence: output.confidence
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Promote subscriber-visible fields from the decision object to the event payload
|
|
528
|
+
// top-level so that GoalManager and SemanticIntegrator can read them without
|
|
529
|
+
// knowing or casting the opaque `decision` type.
|
|
530
|
+
// PlanningEngine (and other creating engines) still receive the full `decision`.
|
|
531
|
+
const dec = ( typeof decision.decision === 'object' && decision.decision !== null )
|
|
532
|
+
? decision.decision as Record<string, unknown>
|
|
533
|
+
: {}
|
|
534
|
+
|
|
535
|
+
this._bus.publish( {
|
|
536
|
+
type: 'executive.facet.progress',
|
|
537
|
+
version: 1,
|
|
538
|
+
sourceEngine: `executive-facet-${this.facetId}`,
|
|
539
|
+
salience: Math.max( 0.5, decision.confidence ),
|
|
540
|
+
payload: {
|
|
541
|
+
facetId: this.facetId,
|
|
542
|
+
respondingToType: report.type,
|
|
543
|
+
decision: decision.decision,
|
|
544
|
+
// promoted for GoalManager
|
|
545
|
+
goalId: dec[ 'goalId' ] as string | undefined,
|
|
546
|
+
goalProgress: dec[ 'goalProgress' ] as number | undefined,
|
|
547
|
+
newGoals: dec[ 'newGoals' ],
|
|
548
|
+
goalsToAbandon: dec[ 'goalsToAbandon' ],
|
|
549
|
+
// promoted for SemanticIntegrator
|
|
550
|
+
newBeliefs: dec[ 'newBeliefs' ],
|
|
551
|
+
// promoted for SemanticIntegrator (learned facts about others → keid-tagged beliefs)
|
|
552
|
+
knownEntityUpdates: dec[ 'knownEntityUpdates' ],
|
|
553
|
+
contextId: report.contextId,
|
|
554
|
+
tick: currentState.tick
|
|
555
|
+
}
|
|
556
|
+
} )
|
|
557
|
+
|
|
558
|
+
// Conscious learning about others — route name/feeling to known.entity.tracker (the
|
|
559
|
+
// dossier owner) the same way the master does, so routine (un-escalated) conversation
|
|
560
|
+
// also records who/what it's dealing with. Facts ride the promoted field above.
|
|
561
|
+
const keUpdates = dec[ 'knownEntityUpdates' ] as ExecutiveOutputFull['knownEntityUpdates'] | undefined
|
|
562
|
+
if( keUpdates )
|
|
563
|
+
for( const u of keUpdates )
|
|
564
|
+
if( u.keid && u.keid !== 'agent-self' && ( u.name || u.feeling != null ) )
|
|
565
|
+
this._bus.publish( {
|
|
566
|
+
type: 'known.entity.learned', version: 1,
|
|
567
|
+
sourceEngine: `executive-facet-${this.facetId}`,
|
|
568
|
+
salience: 0.5, payload: { keid: u.keid, name: u.name, feeling: u.feeling }
|
|
569
|
+
} )
|
|
570
|
+
|
|
571
|
+
// Sync back to master
|
|
572
|
+
this._bus.publish( {
|
|
573
|
+
type: 'executive.facet.sync',
|
|
574
|
+
version: 1,
|
|
575
|
+
sourceEngine: `executive-facet-${this.facetId}`,
|
|
576
|
+
salience: Math.max( 0.5, decision.confidence ),
|
|
577
|
+
payload: {
|
|
578
|
+
facetId: this.facetId,
|
|
579
|
+
reasoning: output.reasoning,
|
|
580
|
+
confidence: output.confidence,
|
|
581
|
+
actionTypes: output.actions.map( a => a.type ),
|
|
582
|
+
tick: currentState.tick
|
|
583
|
+
}
|
|
584
|
+
} )
|
|
585
|
+
|
|
586
|
+
// Notify all listeners (PlanningEngine, AuditionEngine, etc.).
|
|
587
|
+
//
|
|
588
|
+
// Through the CompletionInbox when attached: this runs at LLM-promise
|
|
589
|
+
// resolution — an arbitrary wall-clock moment — and listeners mutate shared
|
|
590
|
+
// state (plans, the outbox). Staging the notification quantizes the landing
|
|
591
|
+
// to the next tick's Phase 2, alongside the bus events published above, so
|
|
592
|
+
// the whole decision (events + listener effects) lands atomically at one
|
|
593
|
+
// tick boundary. Without an inbox (bare facets in tests): direct, as before.
|
|
594
|
+
const notify = (): void => {
|
|
595
|
+
for( const listener of this._listeners )
|
|
596
|
+
try { listener( decision ) }
|
|
597
|
+
catch( err ){ logger.error( `[executive.facet] ${this.facetId} listener error:`, err ) }
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if( this._inbox ) this._inbox.enqueue( `${this.facetId}:decision`, notify )
|
|
601
|
+
else notify()
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Extract the decision payload from the executive output.
|
|
606
|
+
*
|
|
607
|
+
* If the creating engine supplied a `focus.extractDecision` callback, delegate
|
|
608
|
+
* entirely to it — the engine knows what fields it needs and how to derive them
|
|
609
|
+
* from the LLM output (e.g. reading action type as a directive, computing
|
|
610
|
+
* goalProgress from plan state, etc.).
|
|
611
|
+
*
|
|
612
|
+
* If no callback is provided, fall back to a generic passthrough of the
|
|
613
|
+
* LLM's raw actions and goal-mutation lists. This is safe for any facet that
|
|
614
|
+
* doesn't need domain-specific decision routing.
|
|
615
|
+
*/
|
|
616
|
+
private _extractDecisionPayload( output: ExecutiveOutputFull, focus: FocusSection ): unknown {
|
|
617
|
+
if( focus.extractDecision ) return focus.extractDecision( output )
|
|
618
|
+
|
|
619
|
+
// Generic fallback — no plan-specific assumptions
|
|
620
|
+
return {
|
|
621
|
+
actions: output.actions,
|
|
622
|
+
newGoals: output.newGoals,
|
|
623
|
+
goalsToAbandon: output.goalsToAbandon,
|
|
624
|
+
newBeliefs: output.newBeliefs,
|
|
625
|
+
knownEntityUpdates: output.knownEntityUpdates
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|