@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,532 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/agency/motor.schema.executor.ts — enaction & the efference copy
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// The MotorSchemaExecutor consumes the committed `agency.intent`, predicts its
|
|
6
|
+
// own consequences (the efference copy), enacts it, and writes `agency.outcome`
|
|
7
|
+
// — the reafference record whose prediction error becomes the teaching signal in
|
|
8
|
+
// Phase 4. No LLM.
|
|
9
|
+
//
|
|
10
|
+
// Three things happen here that the old catalog executor could not:
|
|
11
|
+
// 1. Every enaction emits an efference copy (predicted reward/valence) BEFORE
|
|
12
|
+
// the result is known, so an async outcome can still be compared on arrival.
|
|
13
|
+
// 2. Parameters are already bound (by the synthesizer), so there is no
|
|
14
|
+
// "empty args" failure mode.
|
|
15
|
+
// 3. A COMPOSITE schema actually executes — it expands into ordered primitive
|
|
16
|
+
// sub-intents that run one per tick, accumulate, and finalize into the
|
|
17
|
+
// composite's own outcome. Created skills are runnable, not inert records.
|
|
18
|
+
//
|
|
19
|
+
// Intent lifecycle (the selector commits 'selected' and then defers while busy):
|
|
20
|
+
// selected ─┬─ primitive sync → outcome written, intent deleted
|
|
21
|
+
// ├─ composite → 'expanding' + first sub-intent ('selected')
|
|
22
|
+
// └─ communicate/external→ 'awaiting' (reconciled in Phase 4)
|
|
23
|
+
// ─────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
import { logger } from '#core/logger'
|
|
26
|
+
import type {
|
|
27
|
+
Duration, Tick, SimulationContext,
|
|
28
|
+
ReadonlySimulationState, StateCommands, EntityInput,
|
|
29
|
+
} from '#core/types'
|
|
30
|
+
import type { CognitiveBus } from '#cognition/bus'
|
|
31
|
+
import type { CognitiveEngine, EngineResult } from '#cognition/types'
|
|
32
|
+
import type { CognitiveEventSchema } from '#cognition/schema.registry'
|
|
33
|
+
import type { ProactiveCommunicator } from '#agency/proactive.communicator'
|
|
34
|
+
import type { SchemaRepertoire } from '#agency/schemas/repertoire'
|
|
35
|
+
import type { SessionLogger } from '#stem/tracts/session.logger'
|
|
36
|
+
import type { MotorSchema, EfferenceCopy } from '#agency/types'
|
|
37
|
+
import type { AccessGrants } from '#agency/access.grants'
|
|
38
|
+
import type { ActionRequest, ActionResult } from '#types'
|
|
39
|
+
import { INNATE_SCHEMAS } from '#agency/schemas/innate'
|
|
40
|
+
import { enact, type Enaction } from '#agency/execution.primitives'
|
|
41
|
+
|
|
42
|
+
/** Ticks an async (communicate/external) intent may stay 'awaiting' before it is abandoned. */
|
|
43
|
+
const AWAIT_TIMEOUT = 15
|
|
44
|
+
|
|
45
|
+
/** Map a communicate schema to the ProactiveCommunicator effector + the permission name. */
|
|
46
|
+
const COMM_SCHEMA_TO_EFFECTOR: Record<string, string> = {
|
|
47
|
+
'reach-out': 'text', talk: 'talk', text: 'text', gesture: 'gesture', broadcast: 'broadcast',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface Intent {
|
|
51
|
+
id: string
|
|
52
|
+
schema: string
|
|
53
|
+
affordanceId?: string
|
|
54
|
+
targetEntityId?: string
|
|
55
|
+
parameters: Record<string, unknown>
|
|
56
|
+
expectedReward: number
|
|
57
|
+
expectedValence: number
|
|
58
|
+
parentIntentId?: string
|
|
59
|
+
stepIndex?: number
|
|
60
|
+
/**
|
|
61
|
+
* Provenance: set when this intent was committed from a plan's frontier-step prior
|
|
62
|
+
* that won the competition (NOT a dispatched step — there is no dispatch). Threaded
|
|
63
|
+
* out on `action.outcome` so the PlanningEngine advances the plan that biased it.
|
|
64
|
+
*/
|
|
65
|
+
planId?: string
|
|
66
|
+
planStepId?: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Authors the words for a self-initiated communicate the agency selected (no inbound triggered it). */
|
|
70
|
+
export interface OutreachAuthor {
|
|
71
|
+
authorOutreach( entityId: string, entityName: string, gist?: string ): Promise<string[]>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class MotorSchemaExecutor implements CognitiveEngine {
|
|
75
|
+
readonly name = 'motor-schema-executor'
|
|
76
|
+
|
|
77
|
+
private _schemas = new Map<string, MotorSchema>()
|
|
78
|
+
private _repertoire: SchemaRepertoire | null = null
|
|
79
|
+
private _comms: ProactiveCommunicator | null = null
|
|
80
|
+
private _author: OutreachAuthor | null = null
|
|
81
|
+
private _grants: AccessGrants | null = null
|
|
82
|
+
private _bus: CognitiveBus | null = null
|
|
83
|
+
|
|
84
|
+
constructor( schemas: MotorSchema[] = INNATE_SCHEMAS ){
|
|
85
|
+
for( const s of schemas ) this._schemas.set( s.id, s )
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
attachBus( bus: CognitiveBus ): void { this._bus = bus }
|
|
89
|
+
/** Resolve schemas (incl. learned composites) from the live repertoire first. */
|
|
90
|
+
attachRepertoire( repertoire: SchemaRepertoire ): void { this._repertoire = repertoire }
|
|
91
|
+
/** Inject the communicate-enaction handler (owned here; the only caller). */
|
|
92
|
+
attachProactiveCommunicator( c: ProactiveCommunicator ): void { this._comms = c }
|
|
93
|
+
/** Inject the outreach author — words for a self-initiated communicate are authored on selection. */
|
|
94
|
+
attachOutreachAuthor( a: OutreachAuthor ): void { this._author = a }
|
|
95
|
+
/** Forward the session logger to the owned ProactiveCommunicator (it audits outbound). */
|
|
96
|
+
attachSessionLogger( logger: SessionLogger | null ): void { this._comms?.attachSessionLogger( logger ) }
|
|
97
|
+
/** Inject the permission gate so outbound communication is grant-checked. */
|
|
98
|
+
attachGrants( g: AccessGrants ): void { this._grants = g }
|
|
99
|
+
registerSchema( schema: MotorSchema ): void { this._schemas.set( schema.id, schema ) }
|
|
100
|
+
|
|
101
|
+
/** Repertoire-first schema resolution, falling back to the local seed set. */
|
|
102
|
+
private _resolve( id: string ): MotorSchema | undefined {
|
|
103
|
+
return this._repertoire?.getSchema( id ) ?? this._schemas.get( id )
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
publishes(): CognitiveEventSchema[] {
|
|
107
|
+
return [
|
|
108
|
+
{ type: 'agency.enacted', version: 1, validate: () => null },
|
|
109
|
+
{ type: 'agency.invocation', version: 1, validate: () => null },
|
|
110
|
+
{ type: 'agency.communicate', version: 1, validate: () => null },
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
subscribes(): string[] { return [] }
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The executor is plan-agnostic. A plan does NOT dispatch steps here — it biases
|
|
117
|
+
* the affordance competition (see PLANNING_AS_PRIOR_TODO.md), so a plan-driven
|
|
118
|
+
* action reaches the executor as an ordinary committed `agency.intent` the
|
|
119
|
+
* selector won. That intent already carries planId/stepId provenance (stamped by
|
|
120
|
+
* the selector from the winning affordance); `_emitActionOutcome` threads it back
|
|
121
|
+
* out, which is how the PlanningEngine advances. Nothing plan-specific here.
|
|
122
|
+
*/
|
|
123
|
+
onCognitiveEvent(): void { /* pull model — reads committed intents from frozen state */ }
|
|
124
|
+
|
|
125
|
+
snapshot(): Record<string, unknown> { return { schemas: this._schemas.size } }
|
|
126
|
+
|
|
127
|
+
async react(
|
|
128
|
+
_delta: Duration,
|
|
129
|
+
tick: Tick,
|
|
130
|
+
state: ReadonlySimulationState,
|
|
131
|
+
_context: SimulationContext,
|
|
132
|
+
): Promise<EngineResult> {
|
|
133
|
+
const set: EntityInput[] = []
|
|
134
|
+
const del: string[] = []
|
|
135
|
+
const metrics: Array<[ string, number ]> = []
|
|
136
|
+
|
|
137
|
+
const energy = state.metrics.get( 'energy.level' ) ?? 100
|
|
138
|
+
const stress = state.metrics.get( 'stress.load' ) ?? 0
|
|
139
|
+
|
|
140
|
+
// ── Timeout stranded async intents ───────────────────────────
|
|
141
|
+
// An 'awaiting' intent whose host/delivery never returned would block the
|
|
142
|
+
// serial Will forever. After AWAIT_TIMEOUT ticks, abandon it as a failed
|
|
143
|
+
// outcome (which also teaches reafference the action is unreliable here).
|
|
144
|
+
for( const [ id, e ] of state.entities ){
|
|
145
|
+
if( e.type !== 'agency.intent' || str( e.metadata?.['status'] ) !== 'awaiting' ) continue
|
|
146
|
+
const dispatchedAt = num( e.metadata?.['dispatchedAt'], tick )
|
|
147
|
+
if( tick - dispatchedAt < AWAIT_TIMEOUT ) continue
|
|
148
|
+
|
|
149
|
+
const intent = readIntent( id, e.metadata )
|
|
150
|
+
const predicted: EfferenceCopy = {
|
|
151
|
+
expectedReward: num( e.metadata?.['predictedReward'], intent.expectedReward ),
|
|
152
|
+
expectedValence: num( e.metadata?.['predictedValence'], intent.expectedValence ),
|
|
153
|
+
}
|
|
154
|
+
const timedOut: Enaction = {
|
|
155
|
+
mode: 'sync', success: false, outcomeQuality: 0, valence: -0.1,
|
|
156
|
+
description: `"${ intent.schema }" timed out — the world never answered.`,
|
|
157
|
+
}
|
|
158
|
+
set.push( outcomeEntity( tick, intent, timedOut, predicted ) )
|
|
159
|
+
del.push( id )
|
|
160
|
+
this._emitEnacted( intent, timedOut, predicted, tick )
|
|
161
|
+
if( intent.planId && intent.planStepId )
|
|
162
|
+
this._emitActionOutcome( intent, false, 0, 1, tick )
|
|
163
|
+
logger.info( `[motor] ⏱ "${ intent.schema }" timed out after ${ tick - dispatchedAt } ticks` )
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Process committed intents in a stable order (determinism / replay).
|
|
167
|
+
const selected = [ ...state.entities.entries() ]
|
|
168
|
+
.filter( ( [ , e ] ) => e.type === 'agency.intent' && str( e.metadata?.['status'] ) === 'selected' )
|
|
169
|
+
.sort( ( a, b ) => ( a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0 ) )
|
|
170
|
+
|
|
171
|
+
let enactedCount = 0
|
|
172
|
+
|
|
173
|
+
for( const [ id, e ] of selected ){
|
|
174
|
+
const intent = readIntent( id, e.metadata )
|
|
175
|
+
const schema = this._resolve( intent.schema )
|
|
176
|
+
|
|
177
|
+
// ── composite → expand into ordered primitive sub-intents ──
|
|
178
|
+
if( schema?.kind === 'composite' && schema.composedOf && schema.composedOf.length > 0 ){
|
|
179
|
+
this._expand( id, e.metadata, schema.composedOf, intent, tick, set )
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const predicted: EfferenceCopy = {
|
|
184
|
+
expectedReward: intent.expectedReward,
|
|
185
|
+
expectedValence: intent.expectedValence,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const enaction: Enaction = schema
|
|
189
|
+
? enact({ schema, parameters: intent.parameters, targetEntityId: intent.targetEntityId, energy, stress })
|
|
190
|
+
: { mode: 'external', success: true, outcomeQuality: 0.5, valence: 0,
|
|
191
|
+
description: `Unknown schema "${ intent.schema }" routed to the host.` }
|
|
192
|
+
|
|
193
|
+
if( enaction.mode === 'sync' ){
|
|
194
|
+
set.push( outcomeEntity( tick, intent, enaction, predicted ) )
|
|
195
|
+
del.push( id )
|
|
196
|
+
if( enaction.metricDeltas ) metrics.push( ...enaction.metricDeltas )
|
|
197
|
+
enactedCount++
|
|
198
|
+
this._emitEnacted( intent, enaction, predicted, tick )
|
|
199
|
+
this._emitActionOutcome( intent, enaction.success, enaction.outcomeQuality,
|
|
200
|
+
clamp01( Math.abs( predicted.expectedReward - enaction.outcomeQuality ) ), tick )
|
|
201
|
+
|
|
202
|
+
// If this was a macro step, advance (or finalize) its parent.
|
|
203
|
+
if( intent.parentIntentId )
|
|
204
|
+
this._advance( intent, enaction, state, tick, set, del )
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
// Communicate: deliver through the shared ProactiveCommunicator (Phase 5b).
|
|
208
|
+
// Returns false when it can't (no delivery layer, no content to send) → fall
|
|
209
|
+
// through to the async 'awaiting' hold (host delivers, or it times out).
|
|
210
|
+
const delivered = enaction.mode === 'communicate'
|
|
211
|
+
&& await this._deliver( id, intent, predicted, state, tick, set, del, metrics )
|
|
212
|
+
|
|
213
|
+
if( delivered ){
|
|
214
|
+
enactedCount++
|
|
215
|
+
} else {
|
|
216
|
+
// Async hold: persist the efference copy so reconciliation can score it.
|
|
217
|
+
set.push({
|
|
218
|
+
id, type: 'agency.intent',
|
|
219
|
+
metadata: {
|
|
220
|
+
...( e.metadata as Record<string, unknown> ),
|
|
221
|
+
status: 'awaiting',
|
|
222
|
+
dispatchedAt: tick,
|
|
223
|
+
predictedReward: predicted.expectedReward,
|
|
224
|
+
predictedValence: predicted.expectedValence,
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
this._emitDispatch( intent, enaction.mode, tick )
|
|
228
|
+
metrics.push([ enaction.mode === 'communicate'
|
|
229
|
+
? 'agency.communicate.dispatched'
|
|
230
|
+
: 'agency.invocation.dispatched', 1 ])
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
metrics.push([ 'agency.executor.enacted', enactedCount ])
|
|
236
|
+
return { commands: { set, delete: del, metrics } }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ── composite machinery ──────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
private _expand(
|
|
242
|
+
parentId: string,
|
|
243
|
+
parentMeta: ReadonlyMap<string, unknown> | Record<string, unknown> | undefined,
|
|
244
|
+
steps: string[],
|
|
245
|
+
intent: Intent,
|
|
246
|
+
tick: Tick,
|
|
247
|
+
set: EntityInput[],
|
|
248
|
+
): void {
|
|
249
|
+
const meta = ( parentMeta ?? {} ) as Record<string, unknown>
|
|
250
|
+
// Parent stays open as 'expanding' and tracks progress.
|
|
251
|
+
set.push({
|
|
252
|
+
id: parentId, type: 'agency.intent',
|
|
253
|
+
metadata: { ...meta, status: 'expanding', steps, cursor: 0, accumQuality: 0, accumValence: 0, completed: 0 },
|
|
254
|
+
})
|
|
255
|
+
set.push( this._subIntent( parentId, intent.targetEntityId, intent.parameters, steps[0]!, 0, tick ) )
|
|
256
|
+
logger.info( `[motor] composite "${ intent.schema }" → ${ steps.length } steps` )
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private _advance(
|
|
260
|
+
sub: Intent,
|
|
261
|
+
enaction: Enaction,
|
|
262
|
+
state: ReadonlySimulationState,
|
|
263
|
+
tick: Tick,
|
|
264
|
+
set: EntityInput[],
|
|
265
|
+
del: string[],
|
|
266
|
+
): void {
|
|
267
|
+
const parentId = sub.parentIntentId!
|
|
268
|
+
const parent = state.entities.get( parentId )
|
|
269
|
+
if( !parent ){ return } // parent already finalized / gone
|
|
270
|
+
|
|
271
|
+
const pm = ( parent.metadata ?? {} ) as Record<string, unknown>
|
|
272
|
+
const steps = Array.isArray( pm['steps'] ) ? ( pm['steps'] as string[] ) : []
|
|
273
|
+
const accumQuality = num( pm['accumQuality'], 0 ) + enaction.outcomeQuality
|
|
274
|
+
const accumValence = num( pm['accumValence'], 0 ) + enaction.valence
|
|
275
|
+
const completed = num( pm['completed'], 0 ) + 1
|
|
276
|
+
const nextK = ( sub.stepIndex ?? 0 ) + 1
|
|
277
|
+
|
|
278
|
+
if( nextK < steps.length ){
|
|
279
|
+
set.push({ id: parentId, type: 'agency.intent',
|
|
280
|
+
metadata: { ...pm, cursor: nextK, accumQuality, accumValence, completed } })
|
|
281
|
+
set.push( this._subIntent(
|
|
282
|
+
parentId,
|
|
283
|
+
str( pm['targetEntityId'] ),
|
|
284
|
+
( pm['parameters'] as Record<string, unknown> ) ?? {},
|
|
285
|
+
steps[nextK]!, nextK, tick,
|
|
286
|
+
) )
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// All steps done → finalize the composite's own outcome.
|
|
291
|
+
const avgQuality = completed > 0 ? accumQuality / completed : 0
|
|
292
|
+
const avgValence = completed > 0 ? accumValence / completed : 0
|
|
293
|
+
const compIntent: Intent = {
|
|
294
|
+
id: parentId,
|
|
295
|
+
schema: str( pm['schema'] ) ?? 'composite',
|
|
296
|
+
affordanceId: str( pm['affordanceId'] ),
|
|
297
|
+
targetEntityId: str( pm['targetEntityId'] ),
|
|
298
|
+
parameters: ( pm['parameters'] as Record<string, unknown> ) ?? {},
|
|
299
|
+
expectedReward: num( pm['expectedReward'], 0.5 ),
|
|
300
|
+
expectedValence: num( pm['expectedValence'], 0 ),
|
|
301
|
+
}
|
|
302
|
+
const compEnaction: Enaction = {
|
|
303
|
+
mode: 'sync', success: true, outcomeQuality: avgQuality, valence: avgValence,
|
|
304
|
+
description: `Composite "${ compIntent.schema }" completed — ${ completed } steps.`,
|
|
305
|
+
}
|
|
306
|
+
set.push( outcomeEntity( tick, compIntent, compEnaction,
|
|
307
|
+
{ expectedReward: compIntent.expectedReward, expectedValence: compIntent.expectedValence } ) )
|
|
308
|
+
del.push( parentId )
|
|
309
|
+
this._emitEnacted( compIntent, compEnaction,
|
|
310
|
+
{ expectedReward: compIntent.expectedReward, expectedValence: compIntent.expectedValence }, tick )
|
|
311
|
+
this._emitActionOutcome( compIntent, true, avgQuality,
|
|
312
|
+
clamp01( Math.abs( compIntent.expectedReward - avgQuality ) ), tick )
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private _subIntent(
|
|
316
|
+
parentId: string,
|
|
317
|
+
targetEntityId: string | undefined,
|
|
318
|
+
parameters: Record<string, unknown>,
|
|
319
|
+
subSchemaId: string,
|
|
320
|
+
k: number,
|
|
321
|
+
tick: Tick,
|
|
322
|
+
): EntityInput {
|
|
323
|
+
const sub = this._resolve( subSchemaId )
|
|
324
|
+
const baseV = sub?.baseValence ?? 0
|
|
325
|
+
return {
|
|
326
|
+
id: `${ parentId }-sub-${ k }`,
|
|
327
|
+
type: 'agency.intent',
|
|
328
|
+
metadata: {
|
|
329
|
+
schema: subSchemaId,
|
|
330
|
+
parentIntentId: parentId,
|
|
331
|
+
stepIndex: k,
|
|
332
|
+
status: 'selected',
|
|
333
|
+
targetEntityId,
|
|
334
|
+
parameters,
|
|
335
|
+
expectedReward: clamp01( ( baseV + 1 ) / 2 ),
|
|
336
|
+
expectedValence: baseV,
|
|
337
|
+
tick,
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ── communication delivery (Phase 5b) ────────────────────────
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Deliver a communicate intent through the shared ProactiveCommunicator, gated by
|
|
346
|
+
* AccessGrants. Returns true when it handled the intent (delivered + outcome written),
|
|
347
|
+
* false when it cannot (no delivery layer / no authored content) so the caller holds
|
|
348
|
+
* the intent 'awaiting'. Message *content* is authored upstream (the deliberation /
|
|
349
|
+
* conversation facet); this is the single enaction → delivery path.
|
|
350
|
+
*/
|
|
351
|
+
private async _deliver(
|
|
352
|
+
id: string, intent: Intent, predicted: EfferenceCopy,
|
|
353
|
+
state: ReadonlySimulationState, tick: Tick,
|
|
354
|
+
set: EntityInput[], del: string[], metrics: Array<[ string, number ]>,
|
|
355
|
+
): Promise<boolean> {
|
|
356
|
+
if( !this._comms ) return false
|
|
357
|
+
const effector = COMM_SCHEMA_TO_EFFECTOR[ intent.schema ] ?? intent.schema
|
|
358
|
+
|
|
359
|
+
// Permission gate — denial RESOLVES as a failed outcome (not an awaiting hold).
|
|
360
|
+
if( this._grants && !this._grants.isAllowed( effector ) ){
|
|
361
|
+
const blocked: Enaction = {
|
|
362
|
+
mode: 'communicate',
|
|
363
|
+
success: false,
|
|
364
|
+
outcomeQuality: 0,
|
|
365
|
+
valence: -0.1,
|
|
366
|
+
description: `"${ effector }" is not permitted for this Will.`
|
|
367
|
+
}
|
|
368
|
+
set.push( outcomeEntity( tick, intent, blocked, predicted ) )
|
|
369
|
+
del.push( id )
|
|
370
|
+
this._emitEnacted( intent, blocked, predicted, tick )
|
|
371
|
+
metrics.push([ 'agency.communicate.blocked', 1 ])
|
|
372
|
+
return true
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Content authored upstream when present (host / host-facet); otherwise, for a
|
|
376
|
+
// self-initiated communicate the agency just selected, author it now via a facet
|
|
377
|
+
// — the unified conversation voice, since no inbound triggered it. Empty ⇒ await.
|
|
378
|
+
const authored = str( intent.parameters['content'] ) ?? firstMessage( intent.parameters['messages'] )
|
|
379
|
+
let bubbles: string[] = authored ? [ authored ] : []
|
|
380
|
+
if( bubbles.length === 0 && this._author ){
|
|
381
|
+
const name = str( intent.parameters['targetEntityName'] ) ?? intent.targetEntityId ?? 'them'
|
|
382
|
+
try { bubbles = await this._author.authorOutreach( intent.targetEntityId ?? '', name, str( intent.parameters['gist'] ) ) }
|
|
383
|
+
catch( err ){ logger.warn( `[motor] outreach authoring failed: ${ errMsg( err ) }` ) }
|
|
384
|
+
}
|
|
385
|
+
if( bubbles.length === 0 ) return false // nothing authored to send → await
|
|
386
|
+
|
|
387
|
+
const request: ActionRequest = {
|
|
388
|
+
effector,
|
|
389
|
+
parameters: { ...intent.parameters, messages: bubbles },
|
|
390
|
+
targetEntityId: intent.targetEntityId,
|
|
391
|
+
reasoning: '',
|
|
392
|
+
expectedOutcome: '',
|
|
393
|
+
decidedAt: tick,
|
|
394
|
+
}
|
|
395
|
+
let result: ActionResult
|
|
396
|
+
try { result = await this._comms.executeAction( request, state ) }
|
|
397
|
+
catch( err ){ logger.warn( `[motor] communicate delivery failed: ${ errMsg( err ) }` ); return false }
|
|
398
|
+
|
|
399
|
+
if( result.commands.set?.length ) set.push( ...result.commands.set )
|
|
400
|
+
const out: Enaction = { mode: 'communicate', success: result.success,
|
|
401
|
+
outcomeQuality: result.feedback.outcomeQuality, valence: 0, description: result.description }
|
|
402
|
+
set.push( outcomeEntity( tick, intent, out, predicted ) )
|
|
403
|
+
del.push( id )
|
|
404
|
+
this._emitEnacted( intent, out, predicted, tick )
|
|
405
|
+
this._emitActionOutcome( intent, result.success, result.feedback.outcomeQuality,
|
|
406
|
+
clamp01( Math.abs( predicted.expectedReward - result.feedback.outcomeQuality ) ), tick )
|
|
407
|
+
metrics.push([ 'agency.communicate.delivered', 1 ])
|
|
408
|
+
return true
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ── bus emission ─────────────────────────────────────────────
|
|
412
|
+
|
|
413
|
+
private _emitEnacted( intent: Intent, enaction: Enaction, predicted: EfferenceCopy, tick: Tick ): void {
|
|
414
|
+
if( !this._bus ) return
|
|
415
|
+
const surprise = clamp01( Math.abs( predicted.expectedReward - enaction.outcomeQuality ) )
|
|
416
|
+
try {
|
|
417
|
+
this._bus.publish({
|
|
418
|
+
type: 'agency.enacted', version: 1, sourceEngine: this.name,
|
|
419
|
+
salience: Math.min( 1, 0.4 + surprise ),
|
|
420
|
+
payload: { schema: intent.schema, success: enaction.success, outcomeQuality: enaction.outcomeQuality, surprise, tick },
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
catch( err ){ logger.warn( `[motor] enacted publish failed: ${ errMsg( err ) }` ) }
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Publish `action.outcome` for EVERY enaction — the shared metacognitive/affective
|
|
428
|
+
* sink. The PlanningEngine consumes it (when planId/stepId are present) to advance
|
|
429
|
+
* a plan; the ConfidenceCalibrator calibrates predicted-vs-actual from it; the
|
|
430
|
+
* RewardEvaluator reads it as a reward signal. `confidence` carries the agency's
|
|
431
|
+
* own forward-model prior so calibration has a real prediction to score.
|
|
432
|
+
*/
|
|
433
|
+
private _emitActionOutcome(
|
|
434
|
+
intent: Intent, success: boolean, outcomeQuality: number, surprise: number, tick: Tick,
|
|
435
|
+
): void {
|
|
436
|
+
if( !this._bus ) return
|
|
437
|
+
try {
|
|
438
|
+
this._bus.publish({
|
|
439
|
+
type: 'action.outcome', version: 1, sourceEngine: this.name,
|
|
440
|
+
salience: Math.min( 1, outcomeQuality * 0.6 ),
|
|
441
|
+
payload: {
|
|
442
|
+
actionType: intent.schema,
|
|
443
|
+
domain: intent.schema,
|
|
444
|
+
confidence: intent.expectedReward,
|
|
445
|
+
success,
|
|
446
|
+
outcomeQuality,
|
|
447
|
+
surprise,
|
|
448
|
+
targetEntityId: intent.targetEntityId,
|
|
449
|
+
...( intent.planId ? { planId: intent.planId } : {} ),
|
|
450
|
+
...( intent.planStepId ? { stepId: intent.planStepId } : {} ),
|
|
451
|
+
tick,
|
|
452
|
+
},
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
catch( err ){ logger.warn( `[motor] action outcome publish failed: ${ errMsg( err ) }` ) }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private _emitDispatch( intent: Intent, mode: 'communicate' | 'external', tick: Tick ): void {
|
|
459
|
+
if( !this._bus ) return
|
|
460
|
+
try {
|
|
461
|
+
this._bus.publish({
|
|
462
|
+
type: mode === 'communicate' ? 'agency.communicate' : 'agency.invocation',
|
|
463
|
+
version: 1, sourceEngine: this.name, salience: 0.6,
|
|
464
|
+
payload: {
|
|
465
|
+
schema: intent.schema, intentId: intent.id,
|
|
466
|
+
targetEntityId: intent.targetEntityId, parameters: intent.parameters, tick,
|
|
467
|
+
},
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
catch( err ){ logger.warn( `[motor] dispatch publish failed: ${ errMsg( err ) }` ) }
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ─── entity helpers ──────────────────────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
function outcomeEntity( tick: Tick, intent: Intent, enaction: Enaction, predicted: EfferenceCopy ): EntityInput {
|
|
477
|
+
const surprise = clamp01( Math.abs( predicted.expectedReward - enaction.outcomeQuality ) )
|
|
478
|
+
return {
|
|
479
|
+
id: `agency-outcome-${ tick }-${ intent.id }`,
|
|
480
|
+
type: 'agency.outcome',
|
|
481
|
+
metadata: {
|
|
482
|
+
schema: intent.schema,
|
|
483
|
+
affordanceId: intent.affordanceId,
|
|
484
|
+
intentId: intent.id,
|
|
485
|
+
targetEntityId: intent.targetEntityId,
|
|
486
|
+
success: enaction.success,
|
|
487
|
+
outcomeQuality: enaction.outcomeQuality,
|
|
488
|
+
valence: enaction.valence,
|
|
489
|
+
predictedReward: predicted.expectedReward,
|
|
490
|
+
predictedValence: predicted.expectedValence,
|
|
491
|
+
surprise,
|
|
492
|
+
description: enaction.description,
|
|
493
|
+
mode: enaction.mode,
|
|
494
|
+
tick,
|
|
495
|
+
},
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function readIntent( id: string, m: ReadonlyMap<string, unknown> | Record<string, unknown> | undefined ): Intent {
|
|
500
|
+
const meta = ( m ?? {} ) as Record<string, unknown>
|
|
501
|
+
return {
|
|
502
|
+
id,
|
|
503
|
+
schema: str( meta['schema'] ) ?? '',
|
|
504
|
+
affordanceId: str( meta['affordanceId'] ),
|
|
505
|
+
targetEntityId: str( meta['targetEntityId'] ),
|
|
506
|
+
parameters: ( meta['parameters'] as Record<string, unknown> ) ?? {},
|
|
507
|
+
expectedReward: num( meta['expectedReward'], 0.5 ),
|
|
508
|
+
expectedValence: num( meta['expectedValence'], 0 ),
|
|
509
|
+
parentIntentId: str( meta['parentIntentId'] ),
|
|
510
|
+
stepIndex: typeof meta['stepIndex'] === 'number' ? ( meta['stepIndex'] as number ) : undefined,
|
|
511
|
+
planId: str( meta['planId'] ),
|
|
512
|
+
planStepId: str( meta['stepId'] ),
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ─── primitives ──────────────────────────────────────────────────────────────
|
|
517
|
+
|
|
518
|
+
function str( v: unknown ): string | undefined {
|
|
519
|
+
return typeof v === 'string' ? v : undefined
|
|
520
|
+
}
|
|
521
|
+
function firstMessage( v: unknown ): string | undefined {
|
|
522
|
+
return Array.isArray( v ) ? v.find( ( m ): m is string => typeof m === 'string' && m.length > 0 ) : undefined
|
|
523
|
+
}
|
|
524
|
+
function num( v: unknown, fallback: number ): number {
|
|
525
|
+
return typeof v === 'number' && Number.isFinite( v ) ? v : fallback
|
|
526
|
+
}
|
|
527
|
+
function clamp01( n: number ): number {
|
|
528
|
+
return n < 0 ? 0 : n > 1 ? 1 : n
|
|
529
|
+
}
|
|
530
|
+
function errMsg( err: unknown ): string {
|
|
531
|
+
return err instanceof Error ? err.message : String( err )
|
|
532
|
+
}
|