@mindot/will 0.9.0 → 0.10.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/README.md +15 -11
- package/dist/index.d.ts +188 -141
- package/dist/index.js +16660 -15206
- package/dist/index.js.map +1 -1
- package/dist/{channels → surface/channels}/discord.d.ts +50 -3
- package/dist/{channels → surface/channels}/discord.js +102 -7
- package/dist/surface/channels/discord.js.map +1 -0
- package/dist/{channels → surface/channels}/whatsapp.d.ts +2 -2
- package/dist/{channels → surface/channels}/whatsapp.js +7 -4
- package/dist/surface/channels/whatsapp.js.map +1 -0
- package/dist/{cli.js → surface/cli.js} +4406 -2904
- package/dist/surface/cli.js.map +1 -0
- package/dist/{mcp → surface/mcp}/effectors.d.ts +1 -1
- package/dist/{mcp → surface/mcp}/effectors.js +10 -4
- package/dist/surface/mcp/effectors.js.map +1 -0
- package/dist/{will-DbDj_TEH.d.ts → will-evj9_vrd.d.ts} +5699 -4854
- package/package.json +11 -12
- package/src/cognition/agency/consequence.ts +237 -3
- package/src/cognition/agency/conversation.aim.ts +32 -0
- package/src/cognition/agency/engines/action.selector.ts +59 -3
- package/src/cognition/agency/engines/affordance.synthesizer.ts +114 -10
- package/src/cognition/agency/engines/deliberation.engine.ts +76 -3
- package/src/cognition/agency/engines/motor.schema.executor.ts +362 -27
- package/src/cognition/agency/engines/reafference.engine.ts +43 -4
- package/src/cognition/agency/execution.primitives.ts +17 -4
- package/src/cognition/agency/reconcile.learning.ts +5 -4
- package/src/cognition/agency/schemas/innate.ts +96 -1
- package/src/cognition/agency/schemas/repertoire.ts +19 -1
- package/src/cognition/agency/selection.scoring.ts +16 -0
- package/src/cognition/agency/settlement.ts +203 -0
- package/src/cognition/agency/types.ts +38 -1
- package/src/cognition/config.mirror.entities.ts +0 -1
- package/src/cognition/event.schemas.ts +0 -8
- package/src/cognition/faculties/affective.blender.ts +21 -4
- package/src/cognition/faculties/circadian.oscillator.ts +36 -7
- package/src/cognition/faculties/executive.engine/action.record.ts +129 -0
- package/src/cognition/faculties/executive.engine/context.ts +41 -35
- package/src/cognition/faculties/executive.engine/engine.ts +270 -113
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +126 -59
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +10 -0
- package/src/cognition/faculties/executive.engine/parser.ts +100 -38
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +168 -23
- package/src/cognition/faculties/executive.engine/types.ts +18 -15
- package/src/cognition/faculties/exteroception.ts +99 -98
- package/src/cognition/faculties/goal.manager.ts +50 -2
- package/src/cognition/faculties/known.entity.tracker.ts +22 -3
- package/src/cognition/faculties/planning.engine/engine.ts +4 -4
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +1 -1
- package/src/cognition/faculties/semantic.engine/integrator.ts +1 -1
- package/src/cognition/faculties/working.memory.ts +6 -1
- package/src/cognition/instruction.handler.ts +1 -1
- package/src/cognition/memory/vector.embedder.ts +1 -3
- package/src/cognition/percept.entity.ts +122 -0
- package/src/cognition/sense.boundary.ts +176 -0
- package/src/cognition/senses/audition.engine/engine.ts +97 -16
- package/src/cognition/senses/base.sense.engine.ts +104 -6
- package/src/cognition/senses/index.ts +71 -13
- package/src/cognition/senses/provenance.ts +128 -0
- package/src/cognition/senses/somatosensation.engine.ts +117 -9
- package/src/cognition/social.identity.ts +23 -0
- package/src/cognition/utilities/token.tracker.ts +36 -5
- package/src/core/orchestrator.ts +13 -0
- package/src/index.ts +39 -5
- package/src/llm/gate.ts +48 -0
- package/src/llm/index.ts +102 -25
- package/src/llm/wire.contracts.ts +38 -0
- package/src/pma/index.ts +1 -1
- package/src/stem/index.ts +92 -21
- package/src/stem/mind.ts +26 -2
- package/src/stem/policy/arbiter.ts +10 -0
- package/src/stem/profiles/built-in.ts +7 -0
- package/src/{profiles → stem/profiles}/companion.ts +1 -1
- package/src/{profiles → stem/profiles}/company-brain.ts +1 -1
- package/src/{profiles → stem/profiles}/customer-service.ts +1 -1
- package/src/{profiles → stem/profiles}/game-npc.ts +1 -1
- package/src/{profiles → stem/profiles}/index.ts +1 -1
- package/src/{profiles → stem/profiles}/smart-home.ts +1 -1
- package/src/stem/tracts/effector/escalation.lifecycle.ts +239 -0
- package/src/stem/tracts/effector/policy.enforcement.ts +261 -0
- package/src/stem/tracts/effector/types.ts +110 -0
- package/src/stem/tracts/effector.controller.ts +112 -401
- package/src/stem/tracts/outbox.controller.ts +30 -17
- package/src/stem/tracts/sensory.controller.ts +6 -6
- package/src/stem/tracts/session.logger.ts +6 -1
- package/src/stem/tracts/transport/types.ts +20 -1
- package/src/stem/tracts/transport.controller.ts +33 -9
- package/src/{channels → surface/channels}/discord.ts +224 -7
- package/src/{channels → surface/channels}/roster.ts +1 -1
- package/src/{channels → surface/channels}/types.ts +1 -1
- package/src/{channels → surface/channels}/whatsapp.ts +9 -6
- package/src/{cli.ts → surface/cli.ts} +6 -6
- package/src/{host → surface/host}/boot.ts +3 -3
- package/src/{host → surface/host}/utterances.ts +2 -2
- package/src/{mcp → surface/mcp}/effectors.ts +19 -6
- package/src/{mcp → surface/mcp}/server.ts +19 -5
- package/src/{sdk → surface/sdk}/will.ts +156 -28
- package/src/{serve → surface/serve}/server.ts +11 -4
- package/src/types.ts +24 -3
- package/dist/channels/discord.js.map +0 -1
- package/dist/channels/whatsapp.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/mcp/effectors.js.map +0 -1
- package/src/extensions/livestream.ext.ts +0 -570
- package/src/extensions/time.ext.ts +0 -339
- package/src/profiles/built-in.ts +0 -7
- package/src/runners/coherence.runner.ts +0 -49
- package/src/runners/outreach.runner.ts +0 -155
- package/src/runners/social.runner.ts +0 -179
- package/src/runners/thin-shim.runner.ts +0 -205
- /package/dist/{cli.d.ts → surface/cli.d.ts} +0 -0
|
@@ -32,6 +32,7 @@ import { schemaEntityId, availabilityEntityId } from '#agency/schemas/repertoire
|
|
|
32
32
|
import { asFinality } from '#stem/policy/arbiter'
|
|
33
33
|
import { AWAIT_TIMEOUT } from '#agency/engines/motor.schema.executor'
|
|
34
34
|
import { readEffectiveParams } from '#cognition/persona.prior'
|
|
35
|
+
import { asProvenance } from '#senses/provenance'
|
|
35
36
|
import {
|
|
36
37
|
SENT_TYPE, DEFAULT_REPLY_WINDOW_TICKS, resolveReplyExpectations,
|
|
37
38
|
} from '#agency/conversation.aim'
|
|
@@ -161,7 +162,11 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
161
162
|
for( const [ , e ] of state.entities ){
|
|
162
163
|
if( e.type !== 'percept') continue
|
|
163
164
|
const m = ( e.metadata ?? {} ) as Record<string, unknown>
|
|
164
|
-
|
|
165
|
+
// asProvenance(), not a literal compare: this is an untyped read out of
|
|
166
|
+
// entity metadata, exactly what that normalizer exists for. Behaviour is
|
|
167
|
+
// unchanged — absent and garbage both normalize to 'exafferent', which
|
|
168
|
+
// fails this test the same way `str()` returning undefined did.
|
|
169
|
+
if( asProvenance( m['provenance'] ) !== 'reafferent') continue
|
|
165
170
|
const iid = str( m['sourceIntentId'] )
|
|
166
171
|
if( !iid || gradedIntentIds.has( iid ) || sensedIntentIds.has( iid ) ) continue
|
|
167
172
|
const aw = awaiting.get( iid )
|
|
@@ -201,6 +206,7 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
201
206
|
let updates = 0
|
|
202
207
|
let discovered = 0
|
|
203
208
|
let refused = 0
|
|
209
|
+
let withheld = 0
|
|
204
210
|
for( const { id, meta: m, fromState } of outcomes ){
|
|
205
211
|
const schema = str( m['schema'] )
|
|
206
212
|
if( !schema ){ if( fromState ) del.push( id ); continue }
|
|
@@ -210,6 +216,25 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
210
216
|
// habit, param priors), or the Will learns it is unskilled at something it
|
|
211
217
|
// is merely forbidden to do. The awaiting intent is still freed, and a
|
|
212
218
|
// refused plan step is signalled unsuccessful so the plan doesn't hang.
|
|
219
|
+
// Chosen silence. The mind considered speaking and decided not to, which
|
|
220
|
+
// is a decision — not a failure, and not a refusal either: nothing forbade
|
|
221
|
+
// it, so availability must not be dented and `recordRefusal` must not run.
|
|
222
|
+
// Free the intent, signal any plan step, and teach NOTHING: the question
|
|
223
|
+
// was never whether it can speak.
|
|
224
|
+
//
|
|
225
|
+
// Before this, a declined outreach timed out at AWAIT_TIMEOUT and folded
|
|
226
|
+
// into `reach-out`'s competence as a failure — the mind learning it is bad
|
|
227
|
+
// at speaking from the times it chose not to speak.
|
|
228
|
+
if( m['withheld'] === true ){
|
|
229
|
+
if( fromState ) del.push( id )
|
|
230
|
+
const heldIntent = str( m['intentId'] )
|
|
231
|
+
if( heldIntent ) del.push( heldIntent )
|
|
232
|
+
const heldPlan = str( m['planId'] )
|
|
233
|
+
if( heldPlan ) this._emitPlanOutcome( heldPlan, str( m['stepId'] ), schema, false, 0, 0, tick )
|
|
234
|
+
withheld++
|
|
235
|
+
continue
|
|
236
|
+
}
|
|
237
|
+
|
|
213
238
|
if( m['refused'] === true ){
|
|
214
239
|
const finality = asFinality( m['finality'] )
|
|
215
240
|
|
|
@@ -251,13 +276,13 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
251
276
|
updates++
|
|
252
277
|
|
|
253
278
|
// Plan advancement for the async path: a host-acked outcome that carries plan
|
|
254
|
-
//
|
|
279
|
+
// the plan link is the ONLY signal the PlanningEngine will get (the executor never
|
|
255
280
|
// saw the ack — the intent was 'awaiting'). Emit the action.outcome it advances
|
|
256
281
|
// on. Sync/timeout outcomes never carry planId here (the executor emitted their
|
|
257
282
|
// action.outcome already), so this never double-advances.
|
|
258
283
|
const planId = str( m['planId'] )
|
|
259
284
|
if( planId )
|
|
260
|
-
this._emitPlanOutcome( planId, str( m['stepId'] ), schema, m['success'] === true, num( m['outcomeQuality'], 0 ), num( m['surprise'], 0 ), tick )
|
|
285
|
+
this._emitPlanOutcome( planId, str( m['stepId'] ), schema, m['success'] === true, num( m['outcomeQuality'], 0 ), num( m['surprise'], 0 ), tick, str( m['description'] ) )
|
|
261
286
|
|
|
262
287
|
// Discovery: the first time the Will enacts a schema, it becomes a known part
|
|
263
288
|
// of its repertoire (the new model's "discovered" — earned by doing, not catalogued).
|
|
@@ -320,6 +345,10 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
320
345
|
// Only emit the refusal metric when it fired — a never-refused Will writes
|
|
321
346
|
// nothing here, preserving the byte-identical quiet path (cf. EXAFFERENCE P3).
|
|
322
347
|
if( refused > 0 ) metrics.push([ 'agency.refused.count', refused ])
|
|
348
|
+
// Counted separately from refusals on purpose: one is the world saying no,
|
|
349
|
+
// the other is the mind saying not now. Collapsing them would read as a
|
|
350
|
+
// policy problem in the telemetry when nothing was forbidden.
|
|
351
|
+
if( withheld > 0 ) metrics.push([ 'agency.withheld.count', withheld ])
|
|
323
352
|
|
|
324
353
|
return { commands: { set, delete: del, metrics } }
|
|
325
354
|
}
|
|
@@ -435,6 +464,9 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
435
464
|
private _emitPlanOutcome(
|
|
436
465
|
planId: string, stepId: string | undefined, schema: string,
|
|
437
466
|
success: boolean, outcomeQuality: number, surprise: number, tick: Tick,
|
|
467
|
+
/** The host's own words for what happened. Absent on withheld/refused paths,
|
|
468
|
+
* where there was no host and nothing to say beyond the fate. */
|
|
469
|
+
description?: string,
|
|
438
470
|
): void {
|
|
439
471
|
if( !this._bus ) return
|
|
440
472
|
try {
|
|
@@ -443,7 +475,14 @@ export class ReafferenceEngine implements CognitiveEngine {
|
|
|
443
475
|
salience: Math.min( 1, outcomeQuality * 0.6 ),
|
|
444
476
|
payload: {
|
|
445
477
|
actionType: schema, domain: schema, success, outcomeQuality, surprise,
|
|
446
|
-
|
|
478
|
+
// The host's own words for what happened, not a stock sentence. This
|
|
479
|
+
// hardcoded `'The world confirmed the action.'` — and since
|
|
480
|
+
// `action.record` is built from this payload, that phrase (or nothing,
|
|
481
|
+
// from the executor's side) is ALL the prompt's `## Recent Action
|
|
482
|
+
// Outcomes` ever showed. The `agency.outcome` entity has carried the
|
|
483
|
+
// real description the whole time (`reconcile.learning.ts:89`); it was
|
|
484
|
+
// read here as `m['description']` and dropped on the floor.
|
|
485
|
+
description: description ?? ( success ? 'The world confirmed the action.' : 'The world rejected the action.'),
|
|
447
486
|
planId,
|
|
448
487
|
...( stepId ? { stepId } : {} ),
|
|
449
488
|
tick,
|
|
@@ -95,10 +95,23 @@ function syncStance( ctx: EnactionContext ): Enaction {
|
|
|
95
95
|
return sync( 0.5, 0.0, 'I let time pass; regulatory processes continue their quiet work.')
|
|
96
96
|
case 'express':
|
|
97
97
|
return sync( 0.6, 0.1, 'My inner state becomes outwardly visible.')
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
// No `inspect` case, deliberately.
|
|
99
|
+
//
|
|
100
|
+
// Looking is now outward ONLY — a question put to the world, tagged external
|
|
101
|
+
// and dispatched. Turning attention inward already has three names on this
|
|
102
|
+
// very floor: `orient` sweeps the situation, `attend` mobilises attention,
|
|
103
|
+
// `reflect` turns inward and lets patterns settle. `inspect` naming that too
|
|
104
|
+
// was a second name for an act that already had one.
|
|
105
|
+
//
|
|
106
|
+
// The cost of the collision was not stylistic. The two readings have
|
|
107
|
+
// DIFFERENT failure modes — "I hold no record of it" versus "the world did not
|
|
108
|
+
// answer" — and one verb covering both meant a pure function needed three
|
|
109
|
+
// flags passed in to tell which it was. Live, the inward reading could not
|
|
110
|
+
// fail at all: a fresh Will proceduralized inspect to habit 0.64 in fifteen
|
|
111
|
+
// ticks, examining its own affordance entities and being told it went well.
|
|
112
|
+
//
|
|
113
|
+
// Outward-only, that is structurally impossible rather than conditionally
|
|
114
|
+
// caught. An unanswered look fails because nothing answered.
|
|
102
115
|
default:
|
|
103
116
|
return sync( 0.5, 0.0, `I enact ${ schema.id }.`)
|
|
104
117
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import type { EntityInput, Tick } from '#core/types'
|
|
19
19
|
import type { DenialFinality, PolicyCounterfactual } from '#stem/policy/arbiter'
|
|
20
|
+
import type { PlanLink } from '#agency/types'
|
|
20
21
|
|
|
21
22
|
export interface HostAckResult {
|
|
22
23
|
success: boolean
|
|
@@ -55,7 +56,7 @@ export interface HostAckResult {
|
|
|
55
56
|
* `agency.invocation` payload); `predictedReward`/`predictedValence` are the
|
|
56
57
|
* efference copy the executor persisted on the intent, so surprise is honest.
|
|
57
58
|
*
|
|
58
|
-
* `
|
|
59
|
+
* `planLink` carries the awaiting intent's plan step (planId/stepId) when it was
|
|
59
60
|
* committed from a plan's frontier prior. It rides on the agency.outcome so the
|
|
60
61
|
* ReafferenceEngine — the engine that consumes async-acked outcomes — can emit the
|
|
61
62
|
* `action.outcome{planId,stepId}` the PlanningEngine advances on. (The executor
|
|
@@ -68,7 +69,7 @@ export function reconcileInvocation(
|
|
|
68
69
|
result: HostAckResult,
|
|
69
70
|
tick: Tick,
|
|
70
71
|
predicted: { reward: number; valence: number } = { reward: 0.5, valence: 0 },
|
|
71
|
-
|
|
72
|
+
planLink: PlanLink = {},
|
|
72
73
|
): EntityInput {
|
|
73
74
|
const outcomeQuality = result.outcomeQuality ?? ( result.success ? 0.8 : 0.1 )
|
|
74
75
|
const valence = result.valence ?? ( result.success ? 0.2 : -0.2 )
|
|
@@ -94,8 +95,8 @@ export function reconcileInvocation(
|
|
|
94
95
|
// Only when the arbiter actually reported a bound — a refusal without one
|
|
95
96
|
// writes no key at all, so the quiet path is unchanged.
|
|
96
97
|
...( result.refused && result.counterfactual ? { counterfactual: result.counterfactual } : {} ),
|
|
97
|
-
...(
|
|
98
|
-
...(
|
|
98
|
+
...( planLink.planId ? { planId: planLink.planId } : {} ),
|
|
99
|
+
...( planLink.stepId ? { stepId: planLink.stepId } : {} ),
|
|
99
100
|
},
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -92,7 +92,102 @@ export const INNATE_SCHEMAS: MotorSchema[] = [
|
|
|
92
92
|
cost: 0.06,
|
|
93
93
|
preconditions: [ { metric: 'energy.level', op: 'gt', value: 8 } ],
|
|
94
94
|
baseValence: 0.05,
|
|
95
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Outward only — a question put to the world, which is the only thing that
|
|
97
|
+
* can answer it.
|
|
98
|
+
*
|
|
99
|
+
* Turning attention inward already has three names on this very floor:
|
|
100
|
+
* `orient` sweeps the situation, `attend` mobilises attention, `reflect`
|
|
101
|
+
* turns inward and lets patterns settle. `inspect` naming that too was a
|
|
102
|
+
* second name for an act that already had one.
|
|
103
|
+
*
|
|
104
|
+
* The collision was not stylistic. The two readings have DIFFERENT failure
|
|
105
|
+
* modes — "I hold no record of it" versus "the world did not answer" — so one
|
|
106
|
+
* verb covering both forced three disambiguation flags into a pure function,
|
|
107
|
+
* and left the inward reading unable to fail at all. Live, a fresh Will
|
|
108
|
+
* proceduralized this to habit 0.64 within fifteen ticks of birth and spent
|
|
109
|
+
* five of its first eight decisions on it, examining its own affordance
|
|
110
|
+
* entities and being told each time that it went well.
|
|
111
|
+
*
|
|
112
|
+
* Tagged external it rides the path `reach-out` already rides: dispatched to
|
|
113
|
+
* the host, held awaiting, acked or timed out. The ACK ITSELF carries the
|
|
114
|
+
* answer — `observation`, in whatever shape the host keeps it — and the
|
|
115
|
+
* engine turns that into a reafferent percept the mind judges for itself
|
|
116
|
+
* (SIGNAL_BOUNDARY P2). One act, one answer, one percept.
|
|
117
|
+
*
|
|
118
|
+
* This paragraph used to say the opposite: that an ack carried only
|
|
119
|
+
* `{success, description}` and so a host CANNOT hand facts back, which
|
|
120
|
+
* obliged every host to call `perceive()` a second time with its own result
|
|
121
|
+
* — the laundering that made a Will's own act arrive looking like news from
|
|
122
|
+
* the world. P2 removed the obligation; this comment outlived it by two
|
|
123
|
+
* merges, which is the ordinary way a false comment survives: nothing
|
|
124
|
+
* compiles it.
|
|
125
|
+
*
|
|
126
|
+
* A look nothing answers still fails, and that is what teaches a mind to
|
|
127
|
+
* stop examining what will not resolve. Through the SDK an effector with no
|
|
128
|
+
* handler is acked failed inside the tick; a host driving the stem directly
|
|
129
|
+
* leaves the intent awaiting until AWAIT_TIMEOUT abandons it.
|
|
130
|
+
*
|
|
131
|
+
* Innate AND host-dependent is not a contradiction; `reach-out` is both.
|
|
132
|
+
* Every mind can look, but whether looking finds anything depends on there
|
|
133
|
+
* being a world.
|
|
134
|
+
*/
|
|
135
|
+
tags: [ 'perception', 'information', 'external' ],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
/**
|
|
139
|
+
* Look at a clock.
|
|
140
|
+
*
|
|
141
|
+
* WHY THIS IS AN ACT AND NOT A FACT THE PROMPT HANDS OVER. A body knows its
|
|
142
|
+
* own rhythm — tired, alert, trough — because a rhythm is something a body
|
|
143
|
+
* DOES. It does not know that it is 15:42, because that is a fact about the
|
|
144
|
+
* world, and the only way a fact about the world reaches a mind is by the
|
|
145
|
+
* mind going and getting it. Every prompt used to carry the hour for free,
|
|
146
|
+
* and it was wrong in three ways at once precisely because nobody had to
|
|
147
|
+
* ask where it came from.
|
|
148
|
+
*
|
|
149
|
+
* Innate AND host-dependent, exactly as `inspect` and `reach-out` are:
|
|
150
|
+
* every mind can ask what time it is; whether anything answers depends on
|
|
151
|
+
* there being a world with a clock in it.
|
|
152
|
+
*
|
|
153
|
+
* A host that has one answers on the ack — `observation`, in whatever shape
|
|
154
|
+
* it keeps time: an ISO string, an hour and a zone, a mission-elapsed count.
|
|
155
|
+
* It does not have to phrase it, and it should not: the mind reads the data
|
|
156
|
+
* and makes the meaning of it (SIGNAL_BOUNDARY P2). The answer lands as a
|
|
157
|
+
* reafferent percept stamped with the intent that sought it, so what she
|
|
158
|
+
* knows about the hour is something she went and found, with a record of
|
|
159
|
+
* having found it.
|
|
160
|
+
*
|
|
161
|
+
* A host that has none never answers, and the failure is honest in either
|
|
162
|
+
* shape it takes: through the SDK an unregistered effector is acked failed
|
|
163
|
+
* inside the tick ("No handler registered for effector ..."), through the
|
|
164
|
+
* raw stem the intent sits awaiting until AWAIT_TIMEOUT abandons it. Either
|
|
165
|
+
* way the mind learns that time is not available here — which is a true
|
|
166
|
+
* thing about this world — rather than being handed a fiction.
|
|
167
|
+
*
|
|
168
|
+
* That degradation is the point of putting it here rather than in a config.
|
|
169
|
+
* A clock injected per-host is a fact one Will has and another does not,
|
|
170
|
+
* with no way for either to know which it is. Sought, it is the same
|
|
171
|
+
* mechanism for all of them, and the answer — or its absence — is
|
|
172
|
+
* something the mind can weigh.
|
|
173
|
+
*
|
|
174
|
+
* `binds: 'none'` because the time is not a referent. There is nothing to
|
|
175
|
+
* point at; you just look.
|
|
176
|
+
*/
|
|
177
|
+
id: 'check-time',
|
|
178
|
+
kind: 'primitive',
|
|
179
|
+
// 'innate', not 'perceptual'. `inspect` is perceptual because a percept
|
|
180
|
+
// EVOKES it — it binds the thing it looks at. Nothing evokes this; it is
|
|
181
|
+
// always there, like `orient` and `rest`. The synthesizer caps
|
|
182
|
+
// percept-evoked affordances at attention capacity and never caps the
|
|
183
|
+
// floor, and a glance at a clock belongs to the floor.
|
|
184
|
+
source: 'innate',
|
|
185
|
+
binds: 'none',
|
|
186
|
+
// Cheaper than `inspect` (0.06): a glance at a clock, not an examination.
|
|
187
|
+
cost: 0.03,
|
|
188
|
+
preconditions: [ { metric: 'energy.level', op: 'gt', value: 5 } ],
|
|
189
|
+
baseValence: 0.0,
|
|
190
|
+
tags: [ 'perception', 'information', 'external' ],
|
|
96
191
|
},
|
|
97
192
|
{
|
|
98
193
|
id: 'reach-out',
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
// paramPriors — last known-good parameters become defaults
|
|
25
25
|
// ─────────────────────────────────────────────────────────────
|
|
26
26
|
|
|
27
|
+
import { logger } from '#core/logger'
|
|
27
28
|
import type { MotorSchema, LearnedSkill } from '#agency/types'
|
|
28
29
|
import type { EntityInput, ReadonlySimulationState } from '#core/types'
|
|
29
30
|
import type { DenialFinality } from '#stem/policy/arbiter'
|
|
30
|
-
import { INNATE_SCHEMAS } from '#agency/schemas/innate'
|
|
31
|
+
import { INNATE_SCHEMAS, INNATE_SCHEMA_BY_ID } from '#agency/schemas/innate'
|
|
31
32
|
|
|
32
33
|
const VALUE_ALPHA = 0.2 // value EMA rate
|
|
33
34
|
const ERROR_BETA = 0.2 // prediction-error EMA rate
|
|
@@ -99,6 +100,23 @@ export class SchemaRepertoire {
|
|
|
99
100
|
* reafference then builds skill on. Idempotent; re-registering updates it.
|
|
100
101
|
*/
|
|
101
102
|
registerExternal( schema: MotorSchema ): void {
|
|
103
|
+
// An innate schema is part of the body, not a slot a tenant may redefine.
|
|
104
|
+
//
|
|
105
|
+
// This sets by id, so a host registering a handler for a name the floor
|
|
106
|
+
// already uses would REPLACE the innate schema with a generated one —
|
|
107
|
+
// silently dropping its `binds`, cost, preconditions and tags. `inspect` is
|
|
108
|
+
// the live case: a Discord bridge answering inquiries would have overwritten
|
|
109
|
+
// the very schema whose `binds: 'percept'` is how inquiry finds its targets,
|
|
110
|
+
// and the mind would have lost the ability to look at what it cannot place in
|
|
111
|
+
// exchange for gaining an answerer.
|
|
112
|
+
//
|
|
113
|
+
// Registering the HANDLER is still what the host wanted and still happens —
|
|
114
|
+
// only the redeclaration is refused. The container supplies the mechanism; a
|
|
115
|
+
// tenant supplies what answers it.
|
|
116
|
+
if( INNATE_SCHEMA_BY_ID.has( schema.id ) ){
|
|
117
|
+
logger.debug(`[repertoire] "${ schema.id }" is innate — keeping the body's schema, binding the handler only`)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
102
120
|
this._templates.set( schema.id, schema )
|
|
103
121
|
}
|
|
104
122
|
|
|
@@ -77,6 +77,20 @@ export interface ScoreWeights {
|
|
|
77
77
|
* pressing reason to speak again still wins.
|
|
78
78
|
*/
|
|
79
79
|
repeat: number
|
|
80
|
+
/**
|
|
81
|
+
* How strongly a verdict System 2 already reached holds the competition.
|
|
82
|
+
*
|
|
83
|
+
* Peer of `will` and `repeat` in magnitude, and for the same reason each of
|
|
84
|
+
* those is: having thought a choice through should weigh about what willing it
|
|
85
|
+
* does, and no more. Large enough that the settled option clears the
|
|
86
|
+
* selector's ambiguity gate (0.06) for most of the settlement's life, so the
|
|
87
|
+
* same question is not re-deliberated every few ticks; small enough that a
|
|
88
|
+
* genuinely pressing affordance — threat, a person waiting, a drive gone
|
|
89
|
+
* urgent — still out-competes a standing verdict.
|
|
90
|
+
*
|
|
91
|
+
* Decays to 0 with the settlement, so the question re-opens on its own.
|
|
92
|
+
*/
|
|
93
|
+
settled: number
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
export const DEFAULT_WEIGHTS: ScoreWeights = {
|
|
@@ -92,6 +106,7 @@ export const DEFAULT_WEIGHTS: ScoreWeights = {
|
|
|
92
106
|
inhib: 0.30,
|
|
93
107
|
risk: 0.20,
|
|
94
108
|
repeat: 0.30,
|
|
109
|
+
settled: 0.30,
|
|
95
110
|
}
|
|
96
111
|
|
|
97
112
|
/**
|
|
@@ -190,6 +205,7 @@ export function scoreAffordance(
|
|
|
190
205
|
- w.inhib * bias.inhibition
|
|
191
206
|
- w.risk * risk( a, bias )
|
|
192
207
|
- w.repeat * ( a.justEnacted ?? 0 )
|
|
208
|
+
+ w.settled * ( a.settled ?? 0 )
|
|
193
209
|
)
|
|
194
210
|
// POLICY_REAFFERENCE P2 — policy availability damps a POSITIVE activation only,
|
|
195
211
|
// never flipping its sign: a refused ability competes weakly (so it is rarely
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/agency/settlement.ts — having thought about it
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// The agency has always been able to represent having ACTED. It could not
|
|
6
|
+
// represent having DECIDED.
|
|
7
|
+
//
|
|
8
|
+
// System 2 is recruited when the field is ambiguous — `margin < marginGate` in
|
|
9
|
+
// the ActionSelector, where margin is the activation gap between the top two
|
|
10
|
+
// affordances. The DeliberationEngine then resolves the contest and writes back
|
|
11
|
+
// exactly one thing: that intent flipped to 'selected', carrying `deliberated:
|
|
12
|
+
// true`. Nothing reads `deliberated`. The synthesizer rebuilds the field from
|
|
13
|
+
// scratch next tick, the same rivals score the same, the margin is again below
|
|
14
|
+
// the gate, and the same question is deliberated again.
|
|
15
|
+
//
|
|
16
|
+
// Measured on a live COO over 7 hours: 149 deliberations, median gap between
|
|
17
|
+
// them 17 seconds, 139 of 148 gaps under a minute — one LLM call every ~3 ticks
|
|
18
|
+
// for the whole run, and the single largest line in her cost. She could see it
|
|
19
|
+
// and had no mechanism to leave it:
|
|
20
|
+
//
|
|
21
|
+
// "Nothing has changed since my last ten deliberation cycles. Goal-7 is
|
|
22
|
+
// externally blocked — this is a fact, not a diagnosis I need to reach again."
|
|
23
|
+
// "I have composed the same message to FKEM across twelve deliberation cycles
|
|
24
|
+
// and not sent it."
|
|
25
|
+
// "There is nothing left to deliberate. I am looking."
|
|
26
|
+
//
|
|
27
|
+
// So: a settlement is the trace a verdict leaves in the field it was called in
|
|
28
|
+
// to resolve. It is the missing peer of a family the engine already has —
|
|
29
|
+
// having acted damps re-acting (`justEnacted`), having spoken damps re-speaking
|
|
30
|
+
// (`spokeAnywhereAt`), having committed can be withdrawn (revocation
|
|
31
|
+
// tombstones) — and having thought damped nothing at all.
|
|
32
|
+
//
|
|
33
|
+
// The mechanism is deliberately ONE quantity doing both jobs. Raising the
|
|
34
|
+
// settled option's activation means (a) the verdict has force, so System 1 does
|
|
35
|
+
// not coin-flip its way to a different answer on the next tick of the same flat
|
|
36
|
+
// field, and (b) the margin to the runner-up widens past the gate, so System 2
|
|
37
|
+
// is not recruited for a question it has already answered. The flatness is what
|
|
38
|
+
// summoned deliberation; the verdict is what gives the field the shape it
|
|
39
|
+
// lacked.
|
|
40
|
+
//
|
|
41
|
+
// And it DECAYS, like everything else here. This is a refractory period, not a
|
|
42
|
+
// lock — the user's objection to "a hard conditional gate that does not promise
|
|
43
|
+
// any flexibility or dynamism of the mind" is the whole design constraint. As
|
|
44
|
+
// the settlement ages the field flattens again and the question genuinely
|
|
45
|
+
// re-opens; a rupture revokes it outright through the machinery that already
|
|
46
|
+
// exists. A mind may always change its mind. It should not have to reach the
|
|
47
|
+
// same conclusion every three seconds to keep holding it.
|
|
48
|
+
// ─────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
import type { EntityInput, Tick } from '#core/types'
|
|
51
|
+
|
|
52
|
+
export const SETTLEMENT_TYPE = 'agency.settlement'
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Ticks a verdict holds before the question is genuinely open again.
|
|
56
|
+
*
|
|
57
|
+
* Set to the satiation window rather than the echo window, and the symmetry is
|
|
58
|
+
* the argument: having thought about something should hold about as long as
|
|
59
|
+
* having done it. The echo window (`CONSEQUENCE_TTL_TICKS`, 30) answers "could
|
|
60
|
+
* the world still be replying to me" — a different and necessarily shorter
|
|
61
|
+
* question that has nothing to do with whether a choice is settled.
|
|
62
|
+
*
|
|
63
|
+
* At the COO's observed rate (~5s/tick) this is roughly five minutes of held
|
|
64
|
+
* verdict, against a mind that was re-deciding every three ticks.
|
|
65
|
+
*/
|
|
66
|
+
export const SETTLEMENT_TTL_TICKS = 60
|
|
67
|
+
|
|
68
|
+
export interface SettlementDescriptor {
|
|
69
|
+
/** The act that won the contest. */
|
|
70
|
+
schema: string
|
|
71
|
+
/** Its object, when it has one — keyed exactly as satiation is, on purpose. */
|
|
72
|
+
targetEntityId?: string
|
|
73
|
+
/**
|
|
74
|
+
* The rivals it was weighed against.
|
|
75
|
+
*
|
|
76
|
+
* Not used for matching — a settlement is keyed on what WON, so a slightly
|
|
77
|
+
* differently-framed contest still meets a verdict the mind has already
|
|
78
|
+
* reached. Carried because "I decided this over that" is the introspectable
|
|
79
|
+
* fact, and a settlement with no memory of what it beat is a preference with
|
|
80
|
+
* no reason attached.
|
|
81
|
+
*/
|
|
82
|
+
over?: readonly string[]
|
|
83
|
+
tick: Tick
|
|
84
|
+
expiresAt: Tick
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Stable id — one settlement per (schema, target), so re-deciding refreshes it. */
|
|
88
|
+
export function settlementId( schema: string, targetEntityId?: string ): string {
|
|
89
|
+
return `agency-settlement-${ schema }${ targetEntityId ? `-${ targetEntityId }` : '' }`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Build the settlement entity. Ordinary state, so it snapshots and replays (FN9). */
|
|
93
|
+
export function settlementEntity( d: SettlementDescriptor ): EntityInput {
|
|
94
|
+
return {
|
|
95
|
+
id: settlementId( d.schema, d.targetEntityId ),
|
|
96
|
+
type: SETTLEMENT_TYPE,
|
|
97
|
+
metadata: { ...d, ...( d.over ? { over: [ ...d.over ] } : {} ) },
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Read a settlement back off entity metadata.
|
|
103
|
+
*
|
|
104
|
+
* Decoded field-for-field, including `over`. A value written by one side and
|
|
105
|
+
* never read by the other is the defect shape this codebase has now hit six
|
|
106
|
+
* times, and it is the exact defect this module exists to fix — `deliberated:
|
|
107
|
+
* true` was written on every deliberated intent and read by nobody.
|
|
108
|
+
*/
|
|
109
|
+
export function readSettlement(
|
|
110
|
+
m: ReadonlyMap<string, unknown> | Record<string, unknown> | undefined,
|
|
111
|
+
): SettlementDescriptor | null {
|
|
112
|
+
const meta = ( m instanceof Map ? Object.fromEntries( m ) : m ?? {} ) as Record<string, unknown>
|
|
113
|
+
const schema = typeof meta['schema'] === 'string' ? meta['schema'] as string : undefined
|
|
114
|
+
if( !schema ) return null
|
|
115
|
+
|
|
116
|
+
const over = Array.isArray( meta['over'] )
|
|
117
|
+
? ( meta['over'] as unknown[] ).filter( ( x ): x is string => typeof x === 'string' )
|
|
118
|
+
: undefined
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
schema,
|
|
122
|
+
targetEntityId: typeof meta['targetEntityId'] === 'string' ? meta['targetEntityId'] as string : undefined,
|
|
123
|
+
...( over && over.length > 0 ? { over } : {} ),
|
|
124
|
+
tick: typeof meta['tick'] === 'number' ? meta['tick'] as number : 0,
|
|
125
|
+
expiresAt: typeof meta['expiresAt'] === 'number' ? meta['expiresAt'] as number : 0,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The live (unexpired) settlements in frozen state, in stable id order. */
|
|
130
|
+
export function liveSettlements(
|
|
131
|
+
entities: ReadonlyMap<string, { type: string; metadata?: ReadonlyMap<string, unknown> | Record<string, unknown> }>,
|
|
132
|
+
tick: Tick,
|
|
133
|
+
/**
|
|
134
|
+
* alias id → anchor id. Settlement is keyed exactly as satiation is — see the
|
|
135
|
+
* note on `settlementForce` — so it has to share satiation's id space too, or a
|
|
136
|
+
* verdict reached about a person under one id would not hold about them under
|
|
137
|
+
* the other. A function, not the table: see `liveConsequences`.
|
|
138
|
+
*/
|
|
139
|
+
canon?: ( id: string ) => string,
|
|
140
|
+
): SettlementDescriptor[] {
|
|
141
|
+
const out: Array<{ id: string; d: SettlementDescriptor }> = []
|
|
142
|
+
|
|
143
|
+
for( const [ id, e ] of entities ){
|
|
144
|
+
if( e.type !== SETTLEMENT_TYPE ) continue
|
|
145
|
+
const d = readSettlement( e.metadata )
|
|
146
|
+
if( !d ) continue
|
|
147
|
+
// Stamped LATER than now ⇒ restored from a previous session, where the tick
|
|
148
|
+
// counter restarts at 1 on wake. Without this a woken mind reads every
|
|
149
|
+
// settlement it ever made as freshly decided and cannot deliberate at all.
|
|
150
|
+
// The same trap `liveConsequences` documents, and the same fix.
|
|
151
|
+
if( d.tick > tick ) continue
|
|
152
|
+
if( tick < d.expiresAt )
|
|
153
|
+
out.push({ id, d: canon && d.targetEntityId
|
|
154
|
+
? { ...d, targetEntityId: canon( d.targetEntityId ) }
|
|
155
|
+
: d })
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return out.sort( ( a, b ) => ( a.id < b.id ? -1 : a.id > b.id ? 1 : 0 ) ).map( x => x.d )
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Settlement ids that have aged out — swept by the DeliberationEngine. */
|
|
162
|
+
export function expiredSettlementIds(
|
|
163
|
+
entities: ReadonlyMap<string, { type: string; metadata?: ReadonlyMap<string, unknown> | Record<string, unknown> }>,
|
|
164
|
+
tick: Tick,
|
|
165
|
+
): string[] {
|
|
166
|
+
const out: string[] = []
|
|
167
|
+
for( const [ id, e ] of entities ){
|
|
168
|
+
if( e.type !== SETTLEMENT_TYPE ) continue
|
|
169
|
+
const d = readSettlement( e.metadata )
|
|
170
|
+
if( d && tick >= d.expiresAt ) out.push( id )
|
|
171
|
+
}
|
|
172
|
+
return out
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* How much of a verdict on this (schema, target) is still standing, 1 → 0.
|
|
177
|
+
*
|
|
178
|
+
* Linear decay over the window, matching `enactionFootprint` — the two are
|
|
179
|
+
* mirror images and should feel the same from inside: one is the fading pull
|
|
180
|
+
* not to repeat an act, this is the fading weight of having already chosen.
|
|
181
|
+
*
|
|
182
|
+
* An objectless act matches an objectless settlement only. Deciding to `rest`
|
|
183
|
+
* says nothing about whether to `reach-out` to Ada, and a verdict that leaked
|
|
184
|
+
* across objects would be a mind that mistakes one question for another.
|
|
185
|
+
*/
|
|
186
|
+
export function settlementForce(
|
|
187
|
+
settlements: readonly SettlementDescriptor[],
|
|
188
|
+
schema: string,
|
|
189
|
+
targetEntityId: string | undefined,
|
|
190
|
+
tick: Tick,
|
|
191
|
+
windowTicks: number = SETTLEMENT_TTL_TICKS,
|
|
192
|
+
): number {
|
|
193
|
+
if( windowTicks <= 0 ) return 0
|
|
194
|
+
|
|
195
|
+
let strongest = 0
|
|
196
|
+
for( const s of settlements ){
|
|
197
|
+
if( s.schema !== schema || s.targetEntityId !== targetEntityId ) continue
|
|
198
|
+
const remaining = ( windowTicks - ( tick - s.tick ) ) / windowTicks
|
|
199
|
+
if( remaining > strongest ) strongest = remaining
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return strongest < 0 ? 0 : strongest > 1 ? 1 : strongest
|
|
203
|
+
}
|
|
@@ -118,7 +118,7 @@ export interface Affordance {
|
|
|
118
118
|
parameters: Record<string, unknown>
|
|
119
119
|
/** Bound target entity id, when the schema binds an entity. */
|
|
120
120
|
targetEntityId?: string
|
|
121
|
-
/** Entity id of the percept / known-entity that evoked this
|
|
121
|
+
/** Entity id of the percept / known-entity that evoked this. */
|
|
122
122
|
evokedBy?: string
|
|
123
123
|
/** Anticipated affective outcome −1..1 (learned, falling back to schema prior). */
|
|
124
124
|
expectedValence: number
|
|
@@ -184,6 +184,26 @@ export interface Affordance {
|
|
|
184
184
|
* each time as though it were the first.
|
|
185
185
|
*/
|
|
186
186
|
justEnacted?: number
|
|
187
|
+
/**
|
|
188
|
+
* How much of a DELIBERATED verdict on this act is still standing, 1 → 0.
|
|
189
|
+
*
|
|
190
|
+
* The mirror of `justEnacted`: that one is the fading pull not to repeat
|
|
191
|
+
* something done, this is the fading weight of having already chosen. Set
|
|
192
|
+
* when System 2 resolved a contest in this act's favour and the settlement
|
|
193
|
+
* has not yet aged out.
|
|
194
|
+
*
|
|
195
|
+
* It does two things with one quantity, which is why it is a quantity and not
|
|
196
|
+
* a flag. The verdict gets force, so System 1 does not coin-flip its way to a
|
|
197
|
+
* different answer on the next tick of the same flat field; and the margin to
|
|
198
|
+
* the runner-up widens past the selector's gate, so System 2 is not recruited
|
|
199
|
+
* for a question it has already answered. Flatness is what summons
|
|
200
|
+
* deliberation — the verdict is what gives the field the shape it lacked.
|
|
201
|
+
*
|
|
202
|
+
* Without it, a live COO deliberated 149 times in 7 hours, median 17s apart,
|
|
203
|
+
* re-reaching the same conclusion about an externally-blocked goal until she
|
|
204
|
+
* wrote "this is a fact, not a diagnosis I need to reach again".
|
|
205
|
+
*/
|
|
206
|
+
settled?: number
|
|
187
207
|
/** Provenance: the plan whose frontier step projected this affordance. */
|
|
188
208
|
planId?: string
|
|
189
209
|
/** Provenance: the frontier step id — flows through to action.outcome so the plan advances. */
|
|
@@ -191,6 +211,23 @@ export interface Affordance {
|
|
|
191
211
|
tick: number
|
|
192
212
|
}
|
|
193
213
|
|
|
214
|
+
/**
|
|
215
|
+
* PlanLink — where an act sits in a plan: the frontier step it serves, if any.
|
|
216
|
+
* Rides on the `agency.outcome` so the PlanningEngine can advance the step the
|
|
217
|
+
* act was committed from.
|
|
218
|
+
*
|
|
219
|
+
* NAMED, AND NAMED THIS, BECAUSE IT USED TO BE CALLED `provenance`
|
|
220
|
+
* (SIGNAL_BOUNDARY P3). Two unrelated concepts shared that word in one codebase:
|
|
221
|
+
* this one, and `SignalProvenance` — whether a signal came from the world or
|
|
222
|
+
* from the mind's own act. The signal sense is the one the vocabulary needs, so
|
|
223
|
+
* this one gives the word up. An inline `{ planId?, stepId? }` in three
|
|
224
|
+
* signatures is also how the collision stayed invisible; a name can be grepped.
|
|
225
|
+
*/
|
|
226
|
+
export interface PlanLink {
|
|
227
|
+
planId?: string
|
|
228
|
+
stepId?: string
|
|
229
|
+
}
|
|
230
|
+
|
|
194
231
|
/**
|
|
195
232
|
* EfferenceCopy — the forward model's prediction of an action's own consequences,
|
|
196
233
|
* emitted *before* enaction so reafference has something to compare against.
|
|
@@ -74,14 +74,6 @@ globalSchemaRegistry.register({
|
|
|
74
74
|
},
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
-
globalSchemaRegistry.register({
|
|
78
|
-
type: 'percept.batch.ingested', version: 1,
|
|
79
|
-
validate( p ){
|
|
80
|
-
if( !isObj(p) ) return 'payload must be object'
|
|
81
|
-
return hasNum( p, 'count')
|
|
82
|
-
},
|
|
83
|
-
})
|
|
84
|
-
|
|
85
77
|
globalSchemaRegistry.register({
|
|
86
78
|
type: 'social.agents.present', version: 1,
|
|
87
79
|
validate( p ){
|