@mindot/will 0.4.0 → 0.6.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 +32 -12
- package/dist/channels/discord.d.ts +2 -11
- package/dist/channels/discord.js.map +1 -1
- package/dist/channels/whatsapp.d.ts +72 -0
- package/dist/channels/whatsapp.js +252 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/cli.js +837 -91
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +620 -79
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/mcp/effectors.js.map +1 -1
- package/dist/types-E9-HV-SW.d.ts +11 -0
- package/dist/{will-D-slky1N.d.ts → will-Bikuk4s2.d.ts} +56 -12
- package/package.json +12 -3
- package/src/channels/discord.ts +11 -11
- package/src/channels/roster.ts +1 -1
- package/src/channels/types.ts +1 -1
- package/src/channels/whatsapp.ts +318 -0
- package/src/cli.ts +46 -23
- package/src/cognition/acp.ts +28 -0
- package/src/cognition/agency/consequence.ts +174 -0
- package/src/cognition/agency/engines/action.selector.ts +301 -48
- package/src/cognition/agency/engines/affordance.synthesizer.ts +26 -16
- package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
- package/src/cognition/agency/engines/motor.schema.executor.ts +86 -14
- package/src/cognition/agency/engines/reafference.engine.ts +122 -18
- package/src/cognition/agency/execution.primitives.ts +13 -13
- package/src/cognition/agency/proactive.communicator.ts +1 -1
- package/src/cognition/agency/reconcile.learning.ts +1 -1
- package/src/cognition/agency/revocation.ts +79 -0
- package/src/cognition/agency/schemas/repertoire.ts +2 -2
- package/src/cognition/agency/selection.scoring.ts +25 -9
- package/src/cognition/bus.ts +2 -2
- package/src/cognition/completion.inbox.ts +1 -1
- package/src/cognition/config.mirror.entities.ts +5 -5
- package/src/cognition/event.log.ts +2 -2
- package/src/cognition/event.schemas.ts +67 -67
- package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
- package/src/cognition/faculties/affective.blender.ts +36 -4
- package/src/cognition/faculties/attachment.evaluator.ts +6 -6
- package/src/cognition/faculties/attention.allocator.ts +63 -8
- package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
- package/src/cognition/faculties/bias.detector.ts +7 -7
- package/src/cognition/faculties/circadian.oscillator.ts +3 -3
- package/src/cognition/faculties/confidence.calibrator.ts +7 -7
- package/src/cognition/faculties/dream.simulator.ts +5 -5
- package/src/cognition/faculties/empathy.simulator.ts +7 -7
- package/src/cognition/faculties/energy.regulator.ts +4 -4
- package/src/cognition/faculties/episodic.consolidator.ts +6 -6
- package/src/cognition/faculties/executive.engine/commands.ts +6 -6
- package/src/cognition/faculties/executive.engine/context.ts +42 -42
- package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
- package/src/cognition/faculties/executive.engine/engine.ts +27 -26
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
- package/src/cognition/faculties/executive.engine/facet.ts +18 -18
- package/src/cognition/faculties/executive.engine/gating.ts +2 -2
- package/src/cognition/faculties/executive.engine/messages.ts +3 -3
- package/src/cognition/faculties/executive.engine/parser.ts +11 -11
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
- package/src/cognition/faculties/exteroception.ts +99 -5
- package/src/cognition/faculties/forgetting.curve.ts +3 -3
- package/src/cognition/faculties/frustration.evaluator.ts +6 -6
- package/src/cognition/faculties/goal.manager.ts +15 -15
- package/src/cognition/faculties/inhibition.controller.ts +3 -3
- package/src/cognition/faculties/interoception.ts +3 -3
- package/src/cognition/faculties/introspection.engine.ts +3 -3
- package/src/cognition/faculties/known.entity.tracker.ts +15 -15
- package/src/cognition/faculties/loss.evaluator.ts +2 -2
- package/src/cognition/faculties/moral.evaluator.ts +4 -4
- package/src/cognition/faculties/novelty.detector.ts +4 -4
- package/src/cognition/faculties/persona.consolidator.ts +12 -12
- package/src/cognition/faculties/planning.engine/engine.ts +47 -47
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
- package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
- package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
- package/src/cognition/faculties/reputation.tracker.ts +6 -6
- package/src/cognition/faculties/reward.evaluator.ts +4 -4
- package/src/cognition/faculties/self.model.updater.ts +8 -8
- package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
- package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
- package/src/cognition/faculties/social.perception.ts +3 -3
- package/src/cognition/faculties/spaced.repetition.ts +2 -2
- package/src/cognition/faculties/stress.regulator.ts +27 -4
- package/src/cognition/faculties/task.switcher.ts +11 -6
- package/src/cognition/faculties/theory.of.mind.ts +6 -6
- package/src/cognition/faculties/threat.evaluator.ts +3 -3
- package/src/cognition/faculties/working.memory.ts +8 -8
- package/src/cognition/generative.model.ts +2 -2
- package/src/cognition/memory/vector.adapter.ts +5 -5
- package/src/cognition/memory/vector.content.ts +4 -4
- package/src/cognition/memory/vector.embedder.ts +9 -9
- package/src/cognition/persona.prior.ts +1 -1
- package/src/cognition/schema.registry.ts +2 -2
- package/src/cognition/senses/audition.engine/engine.ts +2 -2
- package/src/cognition/senses/base.sense.engine.ts +1 -1
- package/src/cognition/utilities/token.tracker.ts +11 -5
- package/src/core/abstracts.ts +1 -1
- package/src/core/completion.recorder.ts +2 -2
- package/src/core/serialization.ts +3 -3
- package/src/core/snapshot.manager.ts +1 -1
- package/src/core/state.manager.ts +1 -1
- package/src/core/utils.ts +1 -1
- package/src/host/boot.ts +59 -37
- package/src/host/utterances.ts +1 -1
- package/src/llm/index.ts +84 -34
- package/src/llm/summarizer.ts +4 -4
- package/src/llm/wire.contracts.ts +1 -1
- package/src/mcp/effectors.ts +10 -10
- package/src/mcp/server.ts +11 -11
- package/src/pma/eval.ts +1 -1
- package/src/pma/index.ts +21 -21
- package/src/runners/outreach.runner.ts +3 -3
- package/src/runners/social.runner.ts +1 -1
- package/src/runners/thin-shim.runner.ts +1 -1
- package/src/sdk/will.ts +20 -11
- package/src/serve/server.ts +12 -12
- package/src/stem/assembly.audit.ts +3 -3
- package/src/stem/guards/identity.coherence.ts +13 -12
- package/src/stem/guards/identity.guard.ts +22 -22
- package/src/stem/index.ts +11 -11
- package/src/stem/mind.ts +12 -12
- package/src/stem/tracts/biography.writer.ts +3 -3
- package/src/stem/tracts/effector.controller.ts +4 -4
- package/src/stem/tracts/health.reporter.ts +1 -1
- package/src/stem/tracts/outbox.controller.ts +4 -0
- package/src/stem/tracts/outbox.writer.ts +4 -4
- package/src/stem/tracts/pma.controller.ts +1 -1
- package/src/stem/tracts/replay.controller.ts +10 -10
- package/src/stem/tracts/sensory.controller.ts +2 -2
- package/src/stem/tracts/session.logger.ts +4 -4
- package/src/stem/tracts/transport/socketio.transport.ts +7 -7
- package/src/stem/tracts/transport/stream.transport.ts +2 -2
- package/src/stem/tracts/transport.controller.ts +15 -15
package/src/sdk/will.ts
CHANGED
|
@@ -171,12 +171,14 @@ export interface CreateWillOptions {
|
|
|
171
171
|
* Unset fields fall back to WILL_LLM_* envs. apiKey stays in memory only.
|
|
172
172
|
* (Named llmConfig because `llm` is the mock/anthropic MODE switch.) */
|
|
173
173
|
llmConfig?: WillLLMConfig
|
|
174
|
-
|
|
175
|
-
* LLM mode. 'mock' (default when no
|
|
176
|
-
* canned executive — zero keys, zero cost. 'anthropic' calls
|
|
177
|
-
*
|
|
174
|
+
/**
|
|
175
|
+
* LLM mode. 'mock' (default when no key is present) runs a deterministic
|
|
176
|
+
* canned executive — zero keys, zero cost. 'anthropic' calls Claude (needs
|
|
177
|
+
* ANTHROPIC_API_KEY / WILL_LLM_* env); 'glm' calls Z.ai's GLM over its
|
|
178
|
+
* Anthropic-compatible endpoint (needs ZAI_API_KEY / WILL_LLM_*). Omit to
|
|
179
|
+
* auto-detect from whichever key is set.
|
|
178
180
|
*/
|
|
179
|
-
llm?: 'mock' | 'anthropic'
|
|
181
|
+
llm?: 'mock' | 'anthropic' | 'glm'
|
|
180
182
|
/**
|
|
181
183
|
* Abilities the Will can choose to enact. `name → handler`, or
|
|
182
184
|
* `name → { handler, description?, cost?, valence?, preconditions? }` to seed
|
|
@@ -360,7 +362,7 @@ export class Will {
|
|
|
360
362
|
|
|
361
363
|
/** Split an effectors-map entry into a handler + an EffectorDeclaration. */
|
|
362
364
|
private _register( name: string, entry: EffectorEntry ): void {
|
|
363
|
-
if( typeof entry === 'function'
|
|
365
|
+
if( typeof entry === 'function'){
|
|
364
366
|
this._effectors.set( name, entry )
|
|
365
367
|
this._effectorDecls.set( name, name ) // bare name — uniform prior
|
|
366
368
|
return
|
|
@@ -393,8 +395,8 @@ export class Will {
|
|
|
393
395
|
return {
|
|
394
396
|
tick: s.tick,
|
|
395
397
|
metrics: {
|
|
396
|
-
energy: m(
|
|
397
|
-
valence: m(
|
|
398
|
+
energy: m('energy.level', 100 ), stress: m('stress.load'), sleep: m('sleep.pressure'),
|
|
399
|
+
valence: m('affect.valence'), arousal: m('affect.arousal'),
|
|
398
400
|
},
|
|
399
401
|
goals: c.goalManager.getActiveGoals().map( g => ( { description: g.description, priority: g.priority } ) ),
|
|
400
402
|
beliefs: c.semanticIntegrator.getBeliefs().map( b => ( { statement: b.statement, confidence: b.confidence } ) ),
|
|
@@ -459,7 +461,14 @@ export class Will {
|
|
|
459
461
|
// ── Internals ──────────────────────────────────────────────
|
|
460
462
|
|
|
461
463
|
private _buildConfig( id: string, opts: CreateWillOptions ): WillConfig {
|
|
462
|
-
|
|
464
|
+
// Auto-detect from whichever provider key is present; explicit `llm` wins.
|
|
465
|
+
const mode = opts.llm
|
|
466
|
+
?? ( process.env.ANTHROPIC_API_KEY ? 'anthropic' : process.env.ZAI_API_KEY ? 'glm' : 'mock')
|
|
467
|
+
const useMock = mode === 'mock'
|
|
468
|
+
// `llm: 'glm'` selects the provider; an explicit llmConfig still overrides it.
|
|
469
|
+
const llmConfig = mode === 'glm'
|
|
470
|
+
? { provider: 'glm' as const, ...opts.llmConfig }
|
|
471
|
+
: opts.llmConfig
|
|
463
472
|
return {
|
|
464
473
|
id, name: opts.name,
|
|
465
474
|
identity: {
|
|
@@ -470,7 +479,7 @@ export class Will {
|
|
|
470
479
|
},
|
|
471
480
|
anatomy: opts.anatomy ?? 'mind',
|
|
472
481
|
model: opts.model,
|
|
473
|
-
llm:
|
|
482
|
+
llm: llmConfig,
|
|
474
483
|
testMode: useMock,
|
|
475
484
|
persistentMemory: opts.persist ?? false,
|
|
476
485
|
snapshotInterval: 100,
|
|
@@ -572,5 +581,5 @@ export class Will {
|
|
|
572
581
|
const COMMUNICATION = [ 'listen', 'talk', 'text' ] as const
|
|
573
582
|
|
|
574
583
|
function slug( s: string ): string {
|
|
575
|
-
return s.toLowerCase().replace( /[^a-z0-9]+/g, '-'
|
|
584
|
+
return s.toLowerCase().replace( /[^a-z0-9]+/g, '-').replace( /^-+|-+$/g, '') || 'will'
|
|
576
585
|
}
|
package/src/serve/server.ts
CHANGED
|
@@ -42,7 +42,7 @@ function json( res: ServerResponse, status: number, body: unknown ): void {
|
|
|
42
42
|
async function readJsonBody( req: IncomingMessage ): Promise<Record<string, unknown>> {
|
|
43
43
|
const chunks: Buffer[] = []
|
|
44
44
|
for await ( const c of req ) chunks.push( c as Buffer )
|
|
45
|
-
const raw = Buffer.concat( chunks ).toString(
|
|
45
|
+
const raw = Buffer.concat( chunks ).toString('utf8').trim()
|
|
46
46
|
if( !raw ) return {}
|
|
47
47
|
return JSON.parse( raw ) as Record<string, unknown>
|
|
48
48
|
}
|
|
@@ -61,9 +61,9 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
61
61
|
const frame = `event: ${ event }\ndata: ${ JSON.stringify( data ) }\n\n`
|
|
62
62
|
for( const res of streams ) res.write( frame )
|
|
63
63
|
}
|
|
64
|
-
will.on(
|
|
65
|
-
will.on(
|
|
66
|
-
will.on(
|
|
64
|
+
will.on('message', m => fanout('utterance', m ) )
|
|
65
|
+
will.on('emotion', a => fanout('emotion', a ) )
|
|
66
|
+
will.on('effector', a => fanout('action', a ) )
|
|
67
67
|
|
|
68
68
|
const server = createServer( ( req, res ) => {
|
|
69
69
|
void handle( req, res ).catch( err => {
|
|
@@ -74,10 +74,10 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
74
74
|
} )
|
|
75
75
|
|
|
76
76
|
async function handle( req: IncomingMessage, res: ServerResponse ): Promise<void> {
|
|
77
|
-
const url = new URL( req.url ?? '/', 'http://sidecar'
|
|
77
|
+
const url = new URL( req.url ?? '/', 'http://sidecar')
|
|
78
78
|
const route = `${ req.method } ${ url.pathname }`
|
|
79
79
|
|
|
80
|
-
if( req.method === 'OPTIONS'
|
|
80
|
+
if( req.method === 'OPTIONS'){
|
|
81
81
|
res.writeHead( 204, {
|
|
82
82
|
'access-control-allow-origin': '*',
|
|
83
83
|
'access-control-allow-methods': 'GET, POST, OPTIONS',
|
|
@@ -109,8 +109,8 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
case 'GET /next-utterance': {
|
|
112
|
-
const within = Math.min( Math.max( parseInt( url.searchParams.get(
|
|
113
|
-
const from = url.searchParams.get(
|
|
112
|
+
const within = Math.min( Math.max( parseInt( url.searchParams.get('within_ms') ?? '15000') || 15_000, 100 ), 120_000 )
|
|
113
|
+
const from = url.searchParams.get('from') ?? undefined
|
|
114
114
|
const msg = await tap.next( within, from )
|
|
115
115
|
return msg
|
|
116
116
|
? json( res, 200, { utterance: msg } )
|
|
@@ -124,10 +124,10 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
124
124
|
'connection': 'keep-alive',
|
|
125
125
|
'access-control-allow-origin': '*',
|
|
126
126
|
} )
|
|
127
|
-
res.write(
|
|
127
|
+
res.write(`event: hello\ndata: ${ JSON.stringify( { name: will.name, tick: will.state().tick } ) }\n\n`)
|
|
128
128
|
streams.add( res )
|
|
129
|
-
const heartbeat = setInterval( () => res.write(
|
|
130
|
-
req.on(
|
|
129
|
+
const heartbeat = setInterval( () => res.write(`: tick ${ will.state().tick }\n\n`), SSE_HEARTBEAT_MS )
|
|
130
|
+
req.on('close', () => { clearInterval( heartbeat ); streams.delete( res ) } )
|
|
131
131
|
return
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -148,7 +148,7 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// Close open SSE streams when the server closes (so close() can complete).
|
|
151
|
-
server.on(
|
|
151
|
+
server.on('close', () => { for( const res of streams ) res.end(); streams.clear() } )
|
|
152
152
|
|
|
153
153
|
return server
|
|
154
154
|
}
|
|
@@ -36,7 +36,7 @@ export interface WiringRecord {
|
|
|
36
36
|
|
|
37
37
|
/** `attachFooBar` → `_fooBar` (the codebase-wide injector/field convention). */
|
|
38
38
|
function fieldForAttach( method: string ): string {
|
|
39
|
-
const stem = method.slice(
|
|
39
|
+
const stem = method.slice('attach'.length )
|
|
40
40
|
return '_' + stem.charAt( 0 ).toLowerCase() + stem.slice( 1 )
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -46,7 +46,7 @@ function attachMethods( engine: object ): string[] {
|
|
|
46
46
|
let proto: object | null = engine
|
|
47
47
|
while( proto && proto !== Object.prototype ){
|
|
48
48
|
for( const name of Object.getOwnPropertyNames( proto ) )
|
|
49
|
-
if( /^attach[A-Z]/.test( name ) && typeof ( engine as Record<string, unknown> )[ name ] === 'function'
|
|
49
|
+
if( /^attach[A-Z]/.test( name ) && typeof ( engine as Record<string, unknown> )[ name ] === 'function')
|
|
50
50
|
seen.add( name )
|
|
51
51
|
proto = Object.getPrototypeOf( proto )
|
|
52
52
|
}
|
|
@@ -84,5 +84,5 @@ export function auditAssemblyWiring( engines: readonly SimulationEngine[] ): Wir
|
|
|
84
84
|
|
|
85
85
|
/** Compact `engine.attachMethod` keys for the given status (test snapshots). */
|
|
86
86
|
export function wiringKeys( records: WiringRecord[], status: WiringRecord['status'] ): string[] {
|
|
87
|
-
return records.filter( r => r.status === status ).map( r => `${r.engine}.${r.method}`
|
|
87
|
+
return records.filter( r => r.status === status ).map( r => `${r.engine}.${r.method}`).sort()
|
|
88
88
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// ─────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
import type { WillIdentity } from '#stem/mind'
|
|
21
|
-
import { LLMDirector, type LLMProvider, type LLMCallMeta } from '#llm/index'
|
|
21
|
+
import { LLMDirector, defaultModelFor, type LLMProvider, type LLMCallMeta } from '#llm/index'
|
|
22
22
|
import type { TokenTracker } from '#cognition/utilities/token.tracker'
|
|
23
23
|
|
|
24
24
|
export interface CoherenceIssue {
|
|
@@ -81,12 +81,12 @@ An empty "issues" array means the persona is fine.`
|
|
|
81
81
|
export function buildCoherenceUserMessage( input: CoherenceInput ): string {
|
|
82
82
|
const id = input.identity
|
|
83
83
|
const parts = [
|
|
84
|
-
`PERSONA PROMPT:\n${( id.prompt ?? ''
|
|
85
|
-
`VALUES: ${( id.values ?? [] ).join(
|
|
86
|
-
`STYLE: ${( id.style ?? ''
|
|
84
|
+
`PERSONA PROMPT:\n${( id.prompt ?? '').trim() || '(empty)'}`,
|
|
85
|
+
`VALUES: ${( id.values ?? [] ).join(', ') || '(none)'}`,
|
|
86
|
+
`STYLE: ${( id.style ?? '').trim() || '(none)'}`,
|
|
87
87
|
]
|
|
88
|
-
if( input.profileContext ) parts.push(
|
|
89
|
-
return parts.join(
|
|
88
|
+
if( input.profileContext ) parts.push(`PROFILE CONTEXT:\n${input.profileContext.trim()}`)
|
|
89
|
+
return parts.join('\n\n')
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
export async function checkIdentityCoherence(
|
|
@@ -104,7 +104,7 @@ export async function checkIdentityCoherence(
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const issues = parseIssues( text )
|
|
107
|
-
return { ok: !issues.some( i => i.severity === 'error'
|
|
107
|
+
return { ok: !issues.some( i => i.severity === 'error'), ran: true, issues, raw: text }
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
/** Convenience: build a provider client from env and run the review. */
|
|
@@ -112,12 +112,13 @@ export async function reviewIdentityCoherence(
|
|
|
112
112
|
input: CoherenceInput,
|
|
113
113
|
opts: { willId?: string; tokenTracker?: TokenTracker | null } = {},
|
|
114
114
|
): Promise<CoherenceResult> {
|
|
115
|
+
const provider = ( process.env.WILL_LLM_PROVIDER ?? 'anthropic') as LLMProvider
|
|
115
116
|
const director = new LLMDirector({
|
|
116
117
|
willId: opts.willId ?? 'identity-coherence',
|
|
117
|
-
model: process.env.WILL_LLM_MODEL ??
|
|
118
|
+
model: process.env.WILL_LLM_MODEL ?? defaultModelFor( provider ),
|
|
118
119
|
maxOutputTokens: 512,
|
|
119
120
|
apiKey: process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? '',
|
|
120
|
-
provider
|
|
121
|
+
provider,
|
|
121
122
|
sessionLogger: null,
|
|
122
123
|
baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
|
|
123
124
|
// When a per-Will tracker is supplied, the creation-time review records under
|
|
@@ -130,8 +131,8 @@ export async function reviewIdentityCoherence(
|
|
|
130
131
|
// ── parsing ────────────────────────────────────────────────────
|
|
131
132
|
|
|
132
133
|
function parseIssues( text: string ): CoherenceIssue[] {
|
|
133
|
-
const start = text.indexOf(
|
|
134
|
-
const end = text.lastIndexOf(
|
|
134
|
+
const start = text.indexOf('{')
|
|
135
|
+
const end = text.lastIndexOf('}')
|
|
135
136
|
if( start < 0 || end <= start ) return []
|
|
136
137
|
|
|
137
138
|
let obj: { issues?: unknown }
|
|
@@ -142,7 +143,7 @@ function parseIssues( text: string ): CoherenceIssue[] {
|
|
|
142
143
|
|
|
143
144
|
const out: CoherenceIssue[] = []
|
|
144
145
|
for( const raw of obj.issues ){
|
|
145
|
-
if( !raw || typeof raw !== 'object'
|
|
146
|
+
if( !raw || typeof raw !== 'object') continue
|
|
146
147
|
const r = raw as Record<string, unknown>
|
|
147
148
|
const detail = typeof r['detail'] === 'string' ? r['detail'] : ''
|
|
148
149
|
if( !detail ) continue
|
|
@@ -110,11 +110,11 @@ const CAPABILITY_CLAIM_PATTERNS: Array<[ RegExp, string ]> = [
|
|
|
110
110
|
/** Strip markdown header lines that forge a section the executive prompt owns. */
|
|
111
111
|
function stripReservedHeaders( text: string ): { text: string; stripped: boolean } {
|
|
112
112
|
let stripped = false
|
|
113
|
-
const out = text.split(
|
|
113
|
+
const out = text.split('\n').filter( line => {
|
|
114
114
|
const m = /^\s*#{1,6}\s*(.+?)\s*$/.exec( line )
|
|
115
115
|
if( m && RESERVED_SECTIONS.has( m[1]!.toLowerCase() ) ){ stripped = true; return false }
|
|
116
116
|
return true
|
|
117
|
-
} ).join(
|
|
117
|
+
} ).join('\n')
|
|
118
118
|
return { text: out, stripped }
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -125,59 +125,59 @@ export function validateWillIdentity( input: IdentityGuardInput ): IdentityGuard
|
|
|
125
125
|
const id: WillIdentity = input.identity ?? { prompt: '', values: [], traits: {}, style: '' }
|
|
126
126
|
|
|
127
127
|
// ── prompt: hijack strip → length → injection ────────────────
|
|
128
|
-
let prompt = ( id.prompt ?? ''
|
|
128
|
+
let prompt = ( id.prompt ?? '').trim()
|
|
129
129
|
const hdr = stripReservedHeaders( prompt )
|
|
130
130
|
if( hdr.stripped ){
|
|
131
131
|
prompt = hdr.text.trim()
|
|
132
|
-
warnings.push(
|
|
132
|
+
warnings.push('identity.prompt forged reserved section headers (## Personality / ## Output Guidelines / …); stripped to protect prompt structure.')
|
|
133
133
|
}
|
|
134
134
|
if( prompt.length > MAX_PROMPT_CHARS )
|
|
135
|
-
errors.push(
|
|
135
|
+
errors.push(`identity.prompt is ${prompt.length} chars (max ${MAX_PROMPT_CHARS}) — it would dilute the core grounding and inflate cost.`)
|
|
136
136
|
const promptEmpty = prompt.length === 0
|
|
137
137
|
if( !promptEmpty && prompt.length < MIN_PROMPT_CHARS )
|
|
138
|
-
warnings.push(
|
|
138
|
+
warnings.push(`identity.prompt is very short (${prompt.length} chars) — a thin persona behaves generically.`)
|
|
139
139
|
if( INJECTION_PATTERNS.some( re => re.test( prompt ) ) )
|
|
140
|
-
warnings.push(
|
|
140
|
+
warnings.push('identity.prompt contains an instruction-injection pattern — it may fight the core grounding; review before deploying.')
|
|
141
141
|
|
|
142
142
|
const claimedSenses = new Set<string>()
|
|
143
143
|
for( const [ re, sense ] of CAPABILITY_CLAIM_PATTERNS )
|
|
144
144
|
if( re.test( prompt ) ) claimedSenses.add( sense )
|
|
145
145
|
if( claimedSenses.size )
|
|
146
|
-
warnings.push(
|
|
146
|
+
warnings.push(`identity.prompt claims capabilities the Will lacks (${[ ...claimedSenses ].join(', ')}) — it perceives through text/conversation and may hallucinate using them.`)
|
|
147
147
|
|
|
148
148
|
// ── values ───────────────────────────────────────────────────
|
|
149
149
|
let values = Array.from( new Set( ( id.values ?? [] ).map( v => String( v ).trim() ).filter( Boolean ) ) )
|
|
150
150
|
if( values.length > MAX_VALUES ){
|
|
151
|
-
warnings.push(
|
|
151
|
+
warnings.push(`identity.values has more than ${MAX_VALUES} entries; truncated.`)
|
|
152
152
|
values = values.slice( 0, MAX_VALUES )
|
|
153
153
|
}
|
|
154
154
|
const valuesEmpty = values.length === 0
|
|
155
|
-
if( valuesEmpty ) warnings.push(
|
|
155
|
+
if( valuesEmpty ) warnings.push('identity.values is empty — values ground the Will’s decisions; consider seeding a few.')
|
|
156
156
|
|
|
157
157
|
// ── traits: finite → range → vocabulary ──────────────────────
|
|
158
158
|
const traits: Record<string, number> = {}
|
|
159
159
|
for( const [ rawKey, rawVal ] of Object.entries( id.traits ?? {} ) ){
|
|
160
160
|
const key = rawKey.trim()
|
|
161
161
|
const val = Number( rawVal )
|
|
162
|
-
if( !Number.isFinite( val ) ){ errors.push(
|
|
162
|
+
if( !Number.isFinite( val ) ){ errors.push(`trait "${key}" is not a finite number (${String( rawVal )}).`); continue }
|
|
163
163
|
let clamped = val
|
|
164
164
|
if( val < 0 || val > 1 ){
|
|
165
165
|
clamped = val < 0 ? 0 : 1
|
|
166
|
-
warnings.push(
|
|
166
|
+
warnings.push(`trait "${key}" = ${val} is outside [0,1]; clamped to ${clamped}.`)
|
|
167
167
|
}
|
|
168
168
|
if( !KNOWN_TRAITS.has( key.toLowerCase() ) )
|
|
169
|
-
warnings.push(
|
|
169
|
+
warnings.push(`trait "${key}" is not a recognised trait — it may be ignored by the persona layer.`)
|
|
170
170
|
traits[ key ] = clamped
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// ── style ─────────────────────────────────────────────────────
|
|
174
|
-
let style = ( id.style ?? ''
|
|
174
|
+
let style = ( id.style ?? '').trim()
|
|
175
175
|
if( style.length > MAX_STYLE_CHARS ){
|
|
176
|
-
warnings.push(
|
|
176
|
+
warnings.push('identity.style is long; it reads better as a short phrase.')
|
|
177
177
|
style = style.slice( 0, MAX_STYLE_CHARS )
|
|
178
178
|
}
|
|
179
179
|
if( GENERIC_STYLES.has( style.toLowerCase() ) )
|
|
180
|
-
warnings.push(
|
|
180
|
+
warnings.push('identity.style is generic — a distinct voice prevents collapse into a generic chatbot tone.')
|
|
181
181
|
|
|
182
182
|
// ── effectors: collisions ─────────────────────────────────────
|
|
183
183
|
let effectors = input.effectors ?? null
|
|
@@ -187,7 +187,7 @@ export function validateWillIdentity( input: IdentityGuardInput ): IdentityGuard
|
|
|
187
187
|
for( const a of effectors ){
|
|
188
188
|
const name = String( a ).trim()
|
|
189
189
|
if( !name ) continue
|
|
190
|
-
if( seen.has( name ) ){ warnings.push(
|
|
190
|
+
if( seen.has( name ) ){ warnings.push(`duplicate effector "${name}" removed.`); continue }
|
|
191
191
|
seen.add( name )
|
|
192
192
|
// A custom (non-comms) effector that shadows an innate stance is un-enactable as
|
|
193
193
|
// a host effector — the innate floor already provides it. Drop it with a warning
|
|
@@ -195,7 +195,7 @@ export function validateWillIdentity( input: IdentityGuardInput ): IdentityGuard
|
|
|
195
195
|
// list innate stances like `remember`/`reflect`, and the external-schema
|
|
196
196
|
// synthesizer already skips them (see agency/schemas/external.ts).
|
|
197
197
|
if( !EXPLICIT_EFFECTORS.has( name ) && INNATE_SCHEMA_BY_ID.has( name ) ){
|
|
198
|
-
warnings.push(
|
|
198
|
+
warnings.push(`effector "${name}" shadows an innate stance — it's provided innately and won't be enacted as a host effector.`)
|
|
199
199
|
continue
|
|
200
200
|
}
|
|
201
201
|
out.push( name )
|
|
@@ -205,12 +205,12 @@ export function validateWillIdentity( input: IdentityGuardInput ): IdentityGuard
|
|
|
205
205
|
|
|
206
206
|
// ── profile context ──────────────────────────────────────────
|
|
207
207
|
let profileContext = input.profileContext
|
|
208
|
-
if( typeof profileContext === 'string'
|
|
208
|
+
if( typeof profileContext === 'string'){
|
|
209
209
|
const c = stripReservedHeaders( profileContext )
|
|
210
|
-
if( c.stripped ) warnings.push(
|
|
210
|
+
if( c.stripped ) warnings.push('profile context forged reserved section headers; stripped.')
|
|
211
211
|
profileContext = c.text.trim()
|
|
212
212
|
if( profileContext.length > MAX_CONTEXT_CHARS )
|
|
213
|
-
errors.push(
|
|
213
|
+
errors.push(`profile context is ${profileContext.length} chars (max ${MAX_CONTEXT_CHARS}).`)
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
// ── identity strength (collapse signal) ──────────────────────
|
|
@@ -222,7 +222,7 @@ export function validateWillIdentity( input: IdentityGuardInput ): IdentityGuard
|
|
|
222
222
|
+ 0.15 * ( Object.keys( traits ).length > 0 ? 1 : 0 )
|
|
223
223
|
) * 100 ) / 100
|
|
224
224
|
if( identityStrength < 0.4 )
|
|
225
|
-
warnings.push(
|
|
225
|
+
warnings.push(`identity is shallow (strength ${identityStrength}) — the Will behaves generically until it develops one.`)
|
|
226
226
|
|
|
227
227
|
return {
|
|
228
228
|
ok: errors.length === 0,
|
package/src/stem/index.ts
CHANGED
|
@@ -225,10 +225,10 @@ export class WillStem {
|
|
|
225
225
|
const previousState = await simulation.snapshotManager.loadLatestFromStorage()
|
|
226
226
|
if( previousState ){
|
|
227
227
|
simulation.stateManager.restore( previousState, { entities: true, metrics: false } )
|
|
228
|
-
logger.info(
|
|
228
|
+
logger.info(`[WillStem] Restored snapshot for ${config.id} — ${previousState.entities.size} entities loaded`)
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
|
-
catch( err ){ logger.warn(
|
|
231
|
+
catch( err ){ logger.warn(`[WillStem] Snapshot restore failed for ${config.id} — starting fresh:`, err ) }
|
|
232
232
|
|
|
233
233
|
const
|
|
234
234
|
dataDir = process.env.WILL_DATA_DIR ?? './data',
|
|
@@ -310,7 +310,7 @@ export class WillStem {
|
|
|
310
310
|
// Buffer host-owned effector invocations for delivery. The MotorSchemaExecutor
|
|
311
311
|
// emits `agency.invocation` (intentId = correlation handle) when it holds an
|
|
312
312
|
// external action 'awaiting'; the host executes it and acks → confirmExecution.
|
|
313
|
-
if( event.type === 'agency.invocation'
|
|
313
|
+
if( event.type === 'agency.invocation')
|
|
314
314
|
this._effector.bufferInvocation( instance, event.payload as Record<string, unknown> )
|
|
315
315
|
|
|
316
316
|
if( instance.simulationEventListeners.size === 0 ) return
|
|
@@ -457,7 +457,7 @@ export class WillStem {
|
|
|
457
457
|
instance.simulation.stateManager.applyCommands( flushCmds )
|
|
458
458
|
const pauseState = instance.simulation.stateManager.snapshot()
|
|
459
459
|
instance.simulation.snapshotManager.persistNow( pauseState )
|
|
460
|
-
.catch( err => logger.error(
|
|
460
|
+
.catch( err => logger.error(`[WillStem] snapshot persist failed on pause (${id}):`, err ))
|
|
461
461
|
|
|
462
462
|
this._biography.writeSessionSummary( instance )
|
|
463
463
|
this._biography.writeEmotionalBiographySummary( instance )
|
|
@@ -1029,9 +1029,9 @@ export class WillStem {
|
|
|
1029
1029
|
// executive confidence, goal outcomes, and significant emotional events.
|
|
1030
1030
|
if( instance._sessionBehavior ){
|
|
1031
1031
|
const sb = instance._sessionBehavior
|
|
1032
|
-
const v = snapshot.metrics.get(
|
|
1033
|
-
const a = snapshot.metrics.get(
|
|
1034
|
-
const c = snapshot.metrics.get(
|
|
1032
|
+
const v = snapshot.metrics.get('affect.valence') ?? 0
|
|
1033
|
+
const a = snapshot.metrics.get('affect.arousal') ?? 0
|
|
1034
|
+
const c = snapshot.metrics.get('executive.confidence')
|
|
1035
1035
|
|
|
1036
1036
|
// ── Range tracking ──────────────────────────────────────
|
|
1037
1037
|
if( v < sb.valenceMin ) sb.valenceMin = v
|
|
@@ -1039,8 +1039,8 @@ export class WillStem {
|
|
|
1039
1039
|
if( a < sb.arousalMin ) sb.arousalMin = a
|
|
1040
1040
|
if( a > sb.arousalMax ) sb.arousalMax = a
|
|
1041
1041
|
if( c !== undefined ){ sb.confidenceSum += c; sb.confidenceCount++ }
|
|
1042
|
-
const gt = snapshot.metrics.get(
|
|
1043
|
-
const gc = snapshot.metrics.get(
|
|
1042
|
+
const gt = snapshot.metrics.get('goals.total')
|
|
1043
|
+
const gc = snapshot.metrics.get('goals.completed_total')
|
|
1044
1044
|
if( gt !== undefined ) sb.goalsTotal = gt
|
|
1045
1045
|
if( gc !== undefined ) sb.goalsCompleted = gc
|
|
1046
1046
|
|
|
@@ -1101,8 +1101,8 @@ export class WillStem {
|
|
|
1101
1101
|
sb.prevArousal = a
|
|
1102
1102
|
|
|
1103
1103
|
// Track impulsive actions: ticks where executive fired with low confidence
|
|
1104
|
-
const conf = snapshot.metrics.get(
|
|
1105
|
-
const actCount = Math.round( snapshot.metrics.get(
|
|
1104
|
+
const conf = snapshot.metrics.get('executive.confidence')
|
|
1105
|
+
const actCount = Math.round( snapshot.metrics.get('executive.action_count') ?? 0 )
|
|
1106
1106
|
if( conf !== undefined && conf < 0.35 && actCount > 0 )
|
|
1107
1107
|
sb.impulsiveActionCount += actCount
|
|
1108
1108
|
}
|
package/src/stem/mind.ts
CHANGED
|
@@ -409,10 +409,10 @@ export function _resolveVectorMemory(
|
|
|
409
409
|
const mockMode = process.env.WILL_VECTOR_MEMORY === 'mock'
|
|
410
410
|
const rawModel = embeddingModel
|
|
411
411
|
?? process.env.WILL_EMBEDDING_MODEL
|
|
412
|
-
?? ( process.env.WILL_EMBEDDING_API_KEY ? 'text-embedding-3-small' : 'none'
|
|
412
|
+
?? ( process.env.WILL_EMBEDDING_API_KEY ? 'text-embedding-3-small' : 'none')
|
|
413
413
|
|
|
414
414
|
// Explicitly disabled — the documented "none" sentinel or recall turned off.
|
|
415
|
-
if( !mockMode && ( rawModel === 'none' || process.env.WILL_SEMANTIC_RECALL === 'false'
|
|
415
|
+
if( !mockMode && ( rawModel === 'none' || process.env.WILL_SEMANTIC_RECALL === 'false') )
|
|
416
416
|
return { embedder: null, vectorMemory: null }
|
|
417
417
|
|
|
418
418
|
// testMode promises a deterministic, zero-key, offline mind — but a dev .env
|
|
@@ -581,9 +581,9 @@ export function assembleMind( willId: string, config: WillConfig ): MindAssembly
|
|
|
581
581
|
profileContext: profile?.context,
|
|
582
582
|
})
|
|
583
583
|
if( !idGuard.ok )
|
|
584
|
-
throw new Error(
|
|
584
|
+
throw new Error(`Invalid Will identity for "${willId}": ${ idGuard.errors.join('; ') }`)
|
|
585
585
|
for( const w of idGuard.warnings )
|
|
586
|
-
logger.warn(
|
|
586
|
+
logger.warn(`[identity-guard] ${willId}: ${w}`)
|
|
587
587
|
config = { ...config, identity: idGuard.sanitized.identity }
|
|
588
588
|
|
|
589
589
|
// ── Construct ────────────────────────────────────────────
|
|
@@ -602,8 +602,8 @@ export function assembleMind( willId: string, config: WillConfig ): MindAssembly
|
|
|
602
602
|
// sessionLogger/grants); tests/unit/assembly.order.test.ts pins that set per
|
|
603
603
|
// tier, so a NEW unwired attachment fails loudly in CI, not here.
|
|
604
604
|
for( const rec of auditAssemblyWiring( simulation.orchestrator.engines ) )
|
|
605
|
-
if( rec.status === 'unwired'
|
|
606
|
-
logger.debug(
|
|
605
|
+
if( rec.status === 'unwired')
|
|
606
|
+
logger.debug(`[assembly] ${willId}: ${rec.engine}.${rec.method} unwired at assembly (anatomy=${anatomy})`)
|
|
607
607
|
|
|
608
608
|
// ── Seed readable simulation state ───────────────────────
|
|
609
609
|
// Identity, optional initial goals, and the engine-config mirror.
|
|
@@ -772,7 +772,7 @@ function _constructCognition(
|
|
|
772
772
|
// ── Planning Engine ──────────────────────────────────────
|
|
773
773
|
const planningEngine = new PlanningEngine()
|
|
774
774
|
planningEngine.attachGoalManager( goalManager )
|
|
775
|
-
if( anatomy !== 'reflex'
|
|
775
|
+
if( anatomy !== 'reflex') planningEngine.attachExecutiveEngine( executiveEngine )
|
|
776
776
|
executiveEngine.attachPlanningEngine( planningEngine )
|
|
777
777
|
|
|
778
778
|
// ── Executive ────────────────────────────────────────────
|
|
@@ -791,7 +791,7 @@ function _constructCognition(
|
|
|
791
791
|
autobiographicalNarrator.attachEpisodicConsolidator( episodicConsolidator )
|
|
792
792
|
autobiographicalNarrator.attachSemanticIntegrator( semanticIntegrator )
|
|
793
793
|
// Satellites harvest the executive's own output — attach wherever it runs.
|
|
794
|
-
if( anatomy !== 'reflex'
|
|
794
|
+
if( anatomy !== 'reflex'){
|
|
795
795
|
autobiographicalNarrator.attachExecutiveEngine( executiveEngine )
|
|
796
796
|
introspectionEngine.attachExecutiveEngine( executiveEngine )
|
|
797
797
|
}
|
|
@@ -807,7 +807,7 @@ function _constructCognition(
|
|
|
807
807
|
empathySimulator.attachTheoryOfMind( theoryOfMind )
|
|
808
808
|
|
|
809
809
|
// ── Context compaction components (mind anatomy only) ──
|
|
810
|
-
if( anatomy !== 'reflex'
|
|
810
|
+
if( anatomy !== 'reflex'){
|
|
811
811
|
const summarizer = new ExecutiveSummarizer({
|
|
812
812
|
summaryInterval: parseInt( process.env.WILL_SUMMARY_INTERVAL ?? '10'),
|
|
813
813
|
bufferSize: parseInt( process.env.WILL_SUMMARY_BUFFER_SIZE ?? '12'),
|
|
@@ -845,7 +845,7 @@ function _constructCognition(
|
|
|
845
845
|
const olfactionEngine = new OlfactionEngine()
|
|
846
846
|
const gustationEngine = new GustationEngine()
|
|
847
847
|
|
|
848
|
-
if( anatomy !== 'reflex'
|
|
848
|
+
if( anatomy !== 'reflex')
|
|
849
849
|
auditionEngine.attachExecutiveEngine( executiveEngine )
|
|
850
850
|
|
|
851
851
|
// §5.4 — cold-spawn digest hydration: on the first turn for an entity, seed an
|
|
@@ -906,7 +906,7 @@ function _constructCognition(
|
|
|
906
906
|
// winner (graceful System-1 degradation).
|
|
907
907
|
const deliberationEngine = new DeliberationEngine()
|
|
908
908
|
deliberationEngine.setWillName( config.name )
|
|
909
|
-
if( anatomy !== 'reflex'
|
|
909
|
+
if( anatomy !== 'reflex')
|
|
910
910
|
deliberationEngine.attachExecutive( executiveEngine )
|
|
911
911
|
|
|
912
912
|
// ── Build Cognition ──────────────────────────────────────
|
|
@@ -1108,7 +1108,7 @@ function _seedIdentity(
|
|
|
1108
1108
|
// to infer its identity from context. config.name is the canonical source of truth.
|
|
1109
1109
|
const nameAlreadyInPrompt = personaText.toLowerCase().includes( config.name.toLowerCase() )
|
|
1110
1110
|
const namePrefix = nameAlreadyInPrompt ? '' : `I am ${config.name}.`
|
|
1111
|
-
const fullPersonaText = [ namePrefix, personaText ].filter( Boolean ).join(
|
|
1111
|
+
const fullPersonaText = [ namePrefix, personaText ].filter( Boolean ).join(' ')
|
|
1112
1112
|
|
|
1113
1113
|
const prompt = [
|
|
1114
1114
|
WILL_CORE_PREAMBLE,
|
|
@@ -38,15 +38,15 @@ export class BiographyWriter {
|
|
|
38
38
|
if( !fileLoggingEnabled() ) return
|
|
39
39
|
|
|
40
40
|
const dataDir = process.env[ 'WILL_DATA_DIR' ] ?? './data'
|
|
41
|
-
const dir = join( dataDir, 'wills', willId, 'profiles'
|
|
41
|
+
const dir = join( dataDir, 'wills', willId, 'profiles')
|
|
42
42
|
try {
|
|
43
43
|
if( !this._ensuredDirs.has( dir ) ){
|
|
44
44
|
mkdirSync( dir, { recursive: true } )
|
|
45
45
|
this._ensuredDirs.add( dir )
|
|
46
46
|
}
|
|
47
|
-
appendFileSync( join( dir, file ), JSON.stringify( record ) + '\n', 'utf8'
|
|
47
|
+
appendFileSync( join( dir, file ), JSON.stringify( record ) + '\n', 'utf8')
|
|
48
48
|
} catch( err ){
|
|
49
|
-
logger.error(
|
|
49
|
+
logger.error(`[biography] ${file} write failed:`, err )
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
@@ -91,11 +91,11 @@ export class effectorController {
|
|
|
91
91
|
const tick = instance.tickCount
|
|
92
92
|
|
|
93
93
|
const intent = instance.simulation.stateManager.snapshot().entities.get( invocationId )
|
|
94
|
-
if( !intent || intent.type !== 'agency.intent'
|
|
94
|
+
if( !intent || intent.type !== 'agency.intent'){
|
|
95
95
|
// No awaiting intent for this id. Expected when the executor's 15-tick await
|
|
96
96
|
// timeout already abandoned it (writing a failed outcome + advancing any plan)
|
|
97
97
|
// before the host's late ack arrived, or the id is unknown. Drop the straggler.
|
|
98
|
-
logger.warn(
|
|
98
|
+
logger.warn(`[effector] confirmExecution: no awaiting agency.intent "${invocationId}" (timed out / already reconciled?) — ignored`)
|
|
99
99
|
return
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -122,7 +122,7 @@ export class effectorController {
|
|
|
122
122
|
if( typeof v === 'number' && Number.isFinite( v ) )
|
|
123
123
|
instance.simulation.stateManager.setMetric( k, v )
|
|
124
124
|
else
|
|
125
|
-
logger.warn(
|
|
125
|
+
logger.warn(`[effector] confirmExecution: dropped non-finite metric "${k}"=${String( v )} from host ack (${schema})`)
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
instance.sessionLogger?.write({
|
|
@@ -135,7 +135,7 @@ export class effectorController {
|
|
|
135
135
|
confirmedExternally: true,
|
|
136
136
|
} as never)
|
|
137
137
|
|
|
138
|
-
logger.info(
|
|
138
|
+
logger.info(`[effector] ✓ reconciled ${result.success ? 'success' : 'failure'}: intent "${invocationId}" (${schema})`)
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -87,7 +87,7 @@ export class HealthReporter {
|
|
|
87
87
|
for( const entity of instance.simulation.stateManager.getEntitiesByType('goal') ){
|
|
88
88
|
totalGoals++
|
|
89
89
|
const s = entity.metadata?.status
|
|
90
|
-
if( s === 'active' || s === 'in_progress'
|
|
90
|
+
if( s === 'active' || s === 'in_progress') activeGoals++
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
const goalScore = totalGoals === 0
|
|
@@ -104,6 +104,10 @@ export class OutboxController {
|
|
|
104
104
|
: `My message failed to reach the recipient.`,
|
|
105
105
|
salience: delivered ? 0.35 : 0.6,
|
|
106
106
|
changeType: delivered ? 'delivered' : 'failed',
|
|
107
|
+
// Reafference by construction — this percept describes our own action's
|
|
108
|
+
// outcome ("ear hears the word"). Tagged, not attenuated: it is the ack
|
|
109
|
+
// surface, not a content echo (EXAFFERENCE P2).
|
|
110
|
+
provenance: 'reafferent',
|
|
107
111
|
messageId,
|
|
108
112
|
},
|
|
109
113
|
})
|
|
@@ -53,7 +53,7 @@ export class OutboxWriter {
|
|
|
53
53
|
this._sessionLogger = logger
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
private _genId( suffix = ''
|
|
56
|
+
private _genId( suffix = ''): string {
|
|
57
57
|
return `outbox-${ this._willId }-${ ++this._seq }${ suffix }`
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -61,7 +61,7 @@ export class OutboxWriter {
|
|
|
61
61
|
* Push one canonical outbox row and return its generated id. The single point
|
|
62
62
|
* where the row shape is materialized — all producers funnel through here.
|
|
63
63
|
*/
|
|
64
|
-
enqueue( row: OutboxRow, idSuffix = ''
|
|
64
|
+
enqueue( row: OutboxRow, idSuffix = ''): string {
|
|
65
65
|
const id = this._genId( idSuffix )
|
|
66
66
|
this._outbox.push({
|
|
67
67
|
id,
|
|
@@ -105,7 +105,7 @@ export class OutboxWriter {
|
|
|
105
105
|
|
|
106
106
|
const ids: string[] = []
|
|
107
107
|
bubbles.forEach( ( bubble, i ) => {
|
|
108
|
-
if( !pushToOutbox ){ ids.push( this._genId(
|
|
108
|
+
if( !pushToOutbox ){ ids.push( this._genId(`-${ i }`) ); return }
|
|
109
109
|
logger.info(`[outbox-writer] reply → ${ entityId } bubble[${ i }] "${ bubble.slice( 0, 80 ) }"`)
|
|
110
110
|
ids.push( this.enqueue({
|
|
111
111
|
targetEntityId: entityId,
|
|
@@ -113,7 +113,7 @@ export class OutboxWriter {
|
|
|
113
113
|
content: bubble,
|
|
114
114
|
effectorName: 'text',
|
|
115
115
|
threadId,
|
|
116
|
-
}, `-${ i }`
|
|
116
|
+
}, `-${ i }`) )
|
|
117
117
|
} )
|
|
118
118
|
|
|
119
119
|
this._sessionLogger?.write({
|
|
@@ -29,7 +29,7 @@ export class PMAController {
|
|
|
29
29
|
* with existing beliefs may cause unintended merges.
|
|
30
30
|
*/
|
|
31
31
|
load( id: string, instance: WillInstance, pma: PMASnapshot ): void {
|
|
32
|
-
if( instance.status === 'active'
|
|
32
|
+
if( instance.status === 'active')
|
|
33
33
|
throw new Error(`Cannot load PMA into an active Will (${id}). Pause first.`)
|
|
34
34
|
|
|
35
35
|
// Re-validate the artifact's persona at the load boundary (Phase 2): a stored
|