@mindot/will 0.3.0 → 0.5.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.
Files changed (57) hide show
  1. package/README.md +63 -21
  2. package/dist/channels/discord.d.ts +69 -0
  3. package/dist/channels/discord.js +193 -0
  4. package/dist/channels/discord.js.map +1 -0
  5. package/dist/channels/whatsapp.d.ts +72 -0
  6. package/dist/channels/whatsapp.js +252 -0
  7. package/dist/channels/whatsapp.js.map +1 -0
  8. package/dist/cli.js +904 -356
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index.d.ts +141 -141
  11. package/dist/index.js +441 -342
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp/effectors.d.ts +1 -1
  14. package/dist/types-E9-HV-SW.d.ts +11 -0
  15. package/dist/{will-B5eKs3Wv.d.ts → will-BDq-TMQr.d.ts} +4013 -3916
  16. package/package.json +17 -3
  17. package/src/channels/discord.ts +214 -0
  18. package/src/channels/roster.ts +87 -0
  19. package/src/channels/types.ts +46 -0
  20. package/src/channels/whatsapp.ts +318 -0
  21. package/src/cli.ts +57 -9
  22. package/src/cognition/agency/engines/deliberation.engine.ts +7 -7
  23. package/src/cognition/agency/execution.primitives.ts +11 -11
  24. package/src/cognition/agency/proactive.communicator.ts +8 -8
  25. package/src/cognition/config.mirror.entities.ts +2 -2
  26. package/src/cognition/conversation.memory.ts +1 -1
  27. package/src/cognition/faculties/executive.engine/commands.ts +7 -15
  28. package/src/cognition/faculties/executive.engine/engine.ts +66 -35
  29. package/src/cognition/faculties/executive.engine/escalation.buffer.ts +1 -1
  30. package/src/cognition/faculties/executive.engine/facet.ts +1 -1
  31. package/src/cognition/faculties/executive.engine/prompt.factory.ts +76 -61
  32. package/src/cognition/faculties/executive.engine/types.ts +1 -1
  33. package/src/cognition/faculties/introspection.engine.ts +1 -2
  34. package/src/cognition/faculties/planning.engine/engine.ts +38 -1
  35. package/src/cognition/faculties/planning.engine/plan.store.ts +42 -0
  36. package/src/cognition/faculties/planning.engine/plan.supervision.ts +7 -7
  37. package/src/cognition/faculties/theory.of.mind.ts +2 -2
  38. package/src/cognition/senses/audition.engine/engine.ts +21 -21
  39. package/src/cognition/utilities/token.tracker.ts +6 -0
  40. package/src/host/boot.ts +95 -7
  41. package/src/llm/index.ts +75 -25
  42. package/src/llm/summarizer.ts +2 -2
  43. package/src/profiles/companion.ts +14 -14
  44. package/src/profiles/company-brain.ts +19 -19
  45. package/src/profiles/customer-service.ts +17 -17
  46. package/src/profiles/game-npc.ts +10 -10
  47. package/src/profiles/index.ts +2 -2
  48. package/src/profiles/smart-home.ts +16 -16
  49. package/src/runners/outreach.runner.ts +6 -9
  50. package/src/runners/social.runner.ts +1 -4
  51. package/src/runners/thin-shim.runner.ts +4 -6
  52. package/src/sdk/will.ts +42 -16
  53. package/src/stem/guards/identity.coherence.ts +9 -6
  54. package/src/stem/guards/identity.guard.ts +20 -9
  55. package/src/stem/index.ts +7 -7
  56. package/src/stem/mind.ts +182 -98
  57. package/src/stem/tracts/outbox.controller.ts +2 -2
@@ -60,7 +60,7 @@ import {
60
60
  type GatingState
61
61
  } from '#faculties/executive.engine/gating'
62
62
  import { LLMDirector } from '#llm/index'
63
- import type { LLMProvider } from '#llm/index'
63
+ import { defaultModelFor, type LLMProvider } from '#llm/index'
64
64
  import { buildFallbackOutput, parseResponse } from '#faculties/executive.engine/parser'
65
65
  import { selectProcess, ideationTemperature, DELIBERATE_THRESHOLD } from '#faculties/executive.engine/effort.gate'
66
66
  import { proposeCandidates } from '#faculties/executive.engine/deliberate.reasoning'
@@ -145,8 +145,14 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
145
145
 
146
146
  // ── Injected dependencies ──────────────────────────────────
147
147
  private _willId: string | null = null
148
- /** Per-Will model id (resolved from modelTier in mind.ts). Null → env/default. */
149
- private _modelId: string | null = null
148
+ /** Per-Will, per-role model ids (config.model, resolved in mind.ts). */
149
+ private _models: { executive: string | null; summarizer: string | null; deliberation: string | null; conversation: string | null } =
150
+ { executive: null, summarizer: null, deliberation: null, conversation: null }
151
+ /** Per-Will LLM transport overrides (config.llm) — env fallbacks apply per field. */
152
+ private _llm: { provider?: string; apiKey?: string; baseUrl?: string; maxOutputTokens?: number; timeoutMs?: number } | null = null
153
+ /** One director per distinct model — same config, different model. Shared
154
+ * tracker/recorder/willId, so ledger attribution and replay hold per role. */
155
+ private _directorCache = new Map<string, LLMDirector>()
150
156
  private _workingMemory: WorkingMemory | null = null
151
157
  private _goalManager: GoalManager | null = null
152
158
  private _episodicConsolidator: EpisodicConsolidator | null = null
@@ -261,8 +267,13 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
261
267
 
262
268
  set willId( willId: string ){ this._willId = willId }
263
269
 
264
- /** Per-Will model id (from modelTier). Must be set before the first tick. */
265
- set modelId( id: string | null ){ this._modelId = id }
270
+ /** Per-Will role models (config.model, resolved). Set before the first tick. */
271
+ set models( m: { executive: string | null; summarizer: string | null; deliberation: string | null; conversation: string | null } ){ this._models = m }
272
+ get models(): { executive: string | null; summarizer: string | null; deliberation: string | null; conversation: string | null } { return this._models }
273
+ /** Per-Will LLM transport overrides (config.llm). Set before the first tick. */
274
+ set llm( c: { provider?: string; apiKey?: string; baseUrl?: string; maxOutputTokens?: number; timeoutMs?: number } | null ){ this._llm = c }
275
+ /** The executive-role model id (back-compat read). */
276
+ get modelId(): string | null { return this._models.executive }
266
277
 
267
278
  // ── Public surface ─────────────────────────────────────────
268
279
 
@@ -289,13 +300,49 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
289
300
  * The caller (PlanningEngine) uses report() to push step outcomes
290
301
  * and subscribe() to receive facet decisions.
291
302
  */
292
- spawnFacet(): { attention: 'available' | 'full', handle?: ExecutiveFacetHandle } {
303
+ /** Get-or-create the director for a model id (shared config, per-Will). */
304
+ private _directorFor( model: string ): LLMDirector {
305
+ let d = this._directorCache.get( model )
306
+ if( !d ){
307
+ // Per-Will transport overrides first (BYO keys), env per field otherwise.
308
+ d = new LLMDirector( {
309
+ willId: this._willId!,
310
+ model,
311
+ maxOutputTokens: this._llm?.maxOutputTokens ?? parseInt( process.env.WILL_MAX_OUTPUT_TOKENS ?? '8096' ),
312
+ // Provider-agnostic key; falls back to ANTHROPIC_API_KEY for back-compat.
313
+ apiKey: this._llm?.apiKey ?? process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? '',
314
+ provider: ( this._llm?.provider ?? process.env.WILL_LLM_PROVIDER ?? 'anthropic' ) as LLMProvider,
315
+ // Optional base-URL override (e.g. Ollama / Azure / self-hosted). Unset →
316
+ // the director uses the provider's official endpoint.
317
+ baseUrl: this._llm?.baseUrl ?? process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
318
+ timeoutMs: this._llm?.timeoutMs ?? ( process.env.WILL_LLM_TIMEOUT_MS ? parseInt( process.env.WILL_LLM_TIMEOUT_MS ) : undefined ),
319
+ sessionLogger: this._sessionLogger,
320
+ mock: this._testMode,
321
+ // Inject the per-Will tracker (R4) so live calls record usage here, not
322
+ // through a process global. null is fine — the director skips recording.
323
+ tokenTracker: this._tokenTracker,
324
+ } )
325
+ this._directorCache.set( model, d )
326
+ }
327
+ return d
328
+ }
329
+
330
+ spawnFacet( role?: 'deliberation' | 'conversation' | 'outreach' | 'supervision' ): { attention: 'available' | 'full', handle?: ExecutiveFacetHandle } {
293
331
  // Delegate to FacetSupervisor (R5-g-3), passing the current engine
294
332
  // attachments. The supervisor owns the registry + attention budget and
295
333
  // performs the throw-checks on bus / director / state ref.
334
+ // A role with its own configured model gets that role's director; every
335
+ // other facet shares the executive's (one self, role-appropriate depth).
336
+ // Outreach speaks with the conversation voice; supervision thinks with the
337
+ // executive's depth.
338
+ const roleModel =
339
+ role === 'deliberation' ? this._models.deliberation :
340
+ role === 'conversation' || role === 'outreach' ? this._models.conversation :
341
+ null
342
+ const director = roleModel && this._llmDirector ? this._directorFor( roleModel ) : this._llmDirector
296
343
  return this._facetSupervisor.spawn( {
297
344
  bus: this._bus,
298
- llmDirector: this._llmDirector,
345
+ llmDirector: director,
299
346
  stateRef: this._lastStateRef,
300
347
  willId: this._willId,
301
348
  inbox: this._inbox,
@@ -386,31 +433,15 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
386
433
  this._gatingState.executiveInterval = rtConfig.executiveInterval
387
434
  this._gatingState.cooldownTicks = rtConfig.cooldownTicks
388
435
 
389
- // Initialize LLM director if not yet done (requires willId)
436
+ // Initialize LLM directors if not yet done (requires willId). One director
437
+ // per distinct role model; roles that share a model share the instance.
390
438
  if( !this._llmDirector && this._willId ){
391
- this._llmDirector = new LLMDirector( {
392
- willId: this._willId,
393
- // Per-Will model (from modelTier, resolved in mind.ts). An explicit
394
- // WILL_LLM_MODEL env still wins (operator pin / self-hosting); the tier
395
- // model only applies when it's unset.
396
- model: this._modelId ?? process.env.WILL_LLM_MODEL ?? 'claude-sonnet-4-5-20250929',
397
- maxOutputTokens: parseInt( process.env.WILL_MAX_OUTPUT_TOKENS ?? '8096' ),
398
- // Provider-agnostic key; falls back to ANTHROPIC_API_KEY for back-compat.
399
- apiKey: process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? '',
400
- provider: ( process.env.WILL_LLM_PROVIDER ?? 'anthropic' ) as LLMProvider,
401
- // Optional base-URL override (e.g. Ollama / Azure / self-hosted). Unset →
402
- // the director uses the provider's official endpoint.
403
- baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
404
- timeoutMs: process.env.WILL_LLM_TIMEOUT_MS ? parseInt( process.env.WILL_LLM_TIMEOUT_MS ) : undefined,
405
- sessionLogger: this._sessionLogger,
406
- mock: this._testMode,
407
- // Inject the per-Will tracker (R4) so live calls record usage here, not
408
- // through a process global. null is fine — the director skips recording.
409
- tokenTracker: this._tokenTracker,
410
- } )
411
- // Share the director with the summarizer so it uses the same
412
- // provider, model, and token-tracking as the executive itself.
413
- this._summarizer?.attachLLMDirector( this._llmDirector )
439
+ const execModel = this._models.executive ?? process.env.WILL_LLM_MODEL
440
+ ?? defaultModelFor( ( this._llm?.provider ?? process.env.WILL_LLM_PROVIDER ?? 'anthropic' ) as LLMProvider )
441
+ this._llmDirector = this._directorFor( execModel )
442
+ // The summarizer runs its role's model (falls back to executive) with the
443
+ // same provider, session logging and token tracking.
444
+ this._summarizer?.attachLLMDirector( this._directorFor( this._models.summarizer ?? execModel ) )
414
445
  }
415
446
 
416
447
  // Evaluate gating
@@ -512,7 +543,7 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
512
543
  // Build focus for master executive
513
544
  const focus: FocusSection = {
514
545
  title: 'Core Consciousness',
515
- content: 'You are the master executive — the unified cognitive core of a self-aware simulated mind.',
546
+ content: 'I am the master executive — the unified cognitive core of a self-aware simulated mind.',
516
547
  outputFormat: undefined, // Use default format
517
548
  instructions: undefined
518
549
  }
@@ -782,7 +813,7 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
782
813
  // ── Flush pending escalation percepts ──────────────────────
783
814
  // Convert buffered audition.task.signal events into high-salience
784
815
  // percept entities so Exteroception surfaces them as
785
- // "## Percepts (What You Notice)" on the NEXT master cycle.
816
+ // "## Percepts (What I Notice)" on the NEXT master cycle.
786
817
  // The master sees them as environmental signals — not as messages to reply to.
787
818
  // It responds by creating plans/goals, never by emitting [REPLY].
788
819
  const { percepts: escalationPercepts, requester: escalationRequester } =
@@ -950,7 +981,7 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
950
981
  //
951
982
  // Implementation:
952
983
  // • Write a high-salience 'percept' entity to simulation state so
953
- // Exteroception surfaces it under "## Percepts (What You Notice)".
984
+ // Exteroception surfaces it under "## Percepts (What I Notice)".
954
985
  // • Spike the salience buffer so the master fires soon.
955
986
  // • Do NOT push into _messageQueue.pendingMessages — that would
956
987
  // cause the master to produce a [REPLY], creating a duplicate
@@ -967,7 +998,7 @@ export class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
967
998
  }
968
999
 
969
1000
  // Write a percept entity so Exteroception surfaces this in
970
- // "## Percepts (What You Notice)" — master sees it as an
1001
+ // "## Percepts (What I Notice)" — master sees it as an
971
1002
  // environmental signal prompting cognitive work, not a reply.
972
1003
  if( this._lastStateRef ){
973
1004
  // State is read-only here; we can't write directly.
@@ -10,7 +10,7 @@
10
10
  // as percepts directly. This buffer holds them until the next
11
11
  // onReasoningComplete(), where they are drained into high-salience percept
12
12
  // entities (StateCommands.set) so Exteroception surfaces them as
13
- // "## Percepts (What You Notice)" on the following master cycle.
13
+ // "## Percepts (What I Notice)" on the following master cycle.
14
14
  //
15
15
  // The master reads these as environmental signals — NEVER as incoming
16
16
  // messages — and responds by creating plans/goals, never by emitting [REPLY];
@@ -424,7 +424,7 @@ export class ExecutiveFacet {
424
424
  ideationUserMessage,
425
425
  tick: currentState.tick,
426
426
  proposeTemperature,
427
- meta: { category: 'executive', attribute: 'facet', function: 'ideation', scope: this.facetId },
427
+ meta: { category: 'executive', attribute: 'facet', function: this._currentFocus?.function ?? 'ideation', scope: this.facetId },
428
428
  } )
429
429
  logger.info(
430
430
  `[executive.facet] ${this.facetId} ◆ deliberate propose tick=${currentState.tick} ` +
@@ -29,6 +29,19 @@
29
29
  * 'facet' — same awareness baseline; creator engine injects domain context
30
30
  * via reportContent and optionally overrides outputFormat.
31
31
  * Conversation facets (AuditionEngine) handle all [REPLY] output.
32
+ *
33
+ * Voice convention (person follows ownership):
34
+ * The executive loop has no second party — the "user" message is the mind's
35
+ * own state feed, not an interlocutor. So:
36
+ * - Self-model content (identity, role, affect/beliefs/percepts headers,
37
+ * action outcomes, felt state) is FIRST PERSON — it is the mind's own text.
38
+ * - Protocol content (JSON format, tag lists, lifecycle mechanics) is
39
+ * IMPERATIVE and person-free — rules of the body, not thoughts of the self.
40
+ * - SECOND PERSON is reserved for real addressees only (a user speaking,
41
+ * developer-facing docs) and never appears in the cognitive prompts.
42
+ * Guards that inspect persona text (identity.guard, identity.coherence)
43
+ * must keep matching BOTH persons — legacy personas and adversarial inputs
44
+ * choose their own grammar.
32
45
  */
33
46
 
34
47
  import type { ReadonlySimulationState } from '#core/types'
@@ -314,22 +327,24 @@ export class PromptFactory {
314
327
  // Mode-aware role description — master is the cognitive core;
315
328
  // a facet is a focused attentional instance with shared identity.
316
329
  const roleDescription = isMaster
317
- ? `You are the unified cognitive core of ${identity.name} — a self-aware synthetic mind.\nYou receive your complete internal state and produce ALL cognitive outputs in one response:\ndecisions, plans, beliefs, introspections, narrative updates, and self-observations.`
318
- : `You are a focused facet of ${identity.name} — concentrating your full reasoning\ncapacity on: **${focus.title}**. You share full identity, values, memories, and\nbeliefs with the master. Only your focus differentiates you from the master consciousness.`
330
+ ? `I am the unified cognitive core of ${identity.name} — a self-aware synthetic mind.\nI receive my complete internal state and produce ALL cognitive outputs in one response:\ndecisions, plans, beliefs, introspections, narrative updates, and self-observations.`
331
+ : `I am a focused facet of ${identity.name} — concentrating my full reasoning\ncapacity on: **${focus.title}**. I share full identity, values, memories, and\nbeliefs with the master. Only my focus differentiates me from the master consciousness.`
319
332
 
320
333
  // Mode-aware consciousness architecture note — grounds the LLM in the
321
334
  // multi-instance design so it doesn't collapse into a generic chatbot persona.
322
335
  const consciousnessArchitecture = isMaster
323
- ? `You are the default reasoning mode. Focused facets may run simultaneously, each\nconcentrating on specific tasks. Their reasoning syncs back to you.\nMaintain your unified identity across all cycles.`
324
- : `You are a facet of ${identity.name}. The master consciousness runs in parallel,\nprocessing the full cognitive state. Your reasoning on this focus will sync back to it.\nStay grounded in your shared identity — same values, same memories, same sense of self.`
336
+ ? `I am the default reasoning mode. Focused facets may run simultaneously, each\nconcentrating on specific tasks. Their reasoning syncs back to me.\nI maintain my unified identity across all cycles.`
337
+ : `I am a facet of ${identity.name}. The master consciousness runs in parallel,\nprocessing the full cognitive state. My reasoning on this focus will sync back to it.\nI stay grounded in my shared identity — same values, same memories, same sense of self.`
325
338
 
326
339
 
327
- // Strip any existing "## Who You Are" section from identity.prompt to prevent
340
+ // Strip any existing "## Who I Am" section from identity.prompt to prevent
328
341
  // duplication — PMA-generated prompts often include it as part of the template.
342
+ // Matches the legacy second-person "## Who You Are" too, so older PMA personas
343
+ // still get de-duplicated after the first-person switch.
329
344
  const cleanIdentityPrompt = identity.prompt
330
- // Strip a forged/duplicated "## Who You Are" *header* (PMA templates include
331
- // it) while keeping the persona content beneath it.
332
- .replace( /^##\s*Who You Are[^\n]*\n?/m, '' )
345
+ // Strip a forged/duplicated "## Who I Am" / "## Who You Are" *header* (PMA
346
+ // templates include it) while keeping the persona content beneath it.
347
+ .replace( /^##\s*Who (?:I Am|You Are)[^\n]*\n?/m, '' )
333
348
  .trim()
334
349
 
335
350
  return `${cleanIdentityPrompt}
@@ -337,29 +352,29 @@ export class PromptFactory {
337
352
  ## Personality
338
353
  ${identityBlock}
339
354
 
340
- ## Your Role
355
+ ## My Role
341
356
  ${roleDescription}
342
357
 
343
358
  ## Consciousness Architecture
344
359
  ${consciousnessArchitecture}
345
360
 
346
361
  ## Output Guidelines
347
- - **actions**: Choose from effectors you know about. If uncertain, describe what you want to achieve in natural language and your body will try to match it. When enacting one of your available abilities that needs specifics (a query, a message, a value), supply them in the action's "args" object — e.g. {"type": "search_docs", "args": {"query": "tick loop design"}, ...}. Your body enacts the ability with exactly those args.
348
- - **plans**: Include for goals without existing plans or where plans need revision. You may keep multiple plans per goal — set **planId** to act on a specific existing plan (validate/execute/revise/cancel); omit it to draft a new one. Your current plans are listed under "## Active Plans".
349
- - **newBeliefs**: Extract patterns from experiences visible in your current state. Only record a belief if you can point to a specific observation that supports it — do not infer experiences you have no record of. Set 'evidence' honestly: 'single_observation' (first time noticing), 'recurring_pattern' (seen multiple times), 'strong_pattern' (deeply established).
350
- - **introspection**: Include when significant events occurred or you notice patterns. When you spot a cognitive bias in your own reasoning, name it in 'identifiedBiases' using its common term where one fits (e.g. overgeneralization, confirmation bias, recency bias) — this lets your self-assessment line up with the patterns your faculties detect on their own.
351
- - **narrative**: Extend your life story only from events grounded in your episodic memory or current percepts. Do not extend with invented scenarios.
352
- - **newGoals/goalsToAbandon/goalsToReprioritize**: Manage your goal hierarchy.
353
- - **selfObservations**: Notice patterns in your own thinking, feeling, or behavior.
354
- - **identityUpdates.traits**: Array of {key, value} where value is a DELTA to apply to your trait (e.g., +0.05 to increase a trait by 5%).
362
+ - **actions**: Choose from effectors I know about. If uncertain, describe what I want to achieve in natural language and my body will try to match it. When enacting one of my available abilities that needs specifics (a query, a message, a value), supply them in the action's "args" object — e.g. {"type": "search_docs", "args": {"query": "tick loop design"}, ...}. My body enacts the ability with exactly those args.
363
+ - **plans**: Include for goals without existing plans or where plans need revision. I may keep multiple plans per goal — set **planId** to act on a specific existing plan (validate/execute/revise/cancel); omit it to draft a new one. My current plans are listed under "## Active Plans".
364
+ - **newBeliefs**: Extract patterns from experiences visible in my current state. Only record a belief if I can point to a specific observation that supports it — do not infer experiences I have no record of. Set 'evidence' honestly: 'single_observation' (first time noticing), 'recurring_pattern' (seen multiple times), 'strong_pattern' (deeply established).
365
+ - **introspection**: Include when significant events occurred or I notice patterns. When I spot a cognitive bias in my own reasoning, name it in 'identifiedBiases' using its common term where one fits (e.g. overgeneralization, confirmation bias, recency bias) — this lets my self-assessment line up with the patterns my faculties detect on their own.
366
+ - **narrative**: Extend my life story only from events grounded in my episodic memory or current percepts. Do not extend with invented scenarios.
367
+ - **newGoals/goalsToAbandon/goalsToReprioritize**: Manage my goal hierarchy.
368
+ - **selfObservations**: Notice patterns in my own thinking, feeling, or behavior.
369
+ - **identityUpdates.traits**: Array of {key, value} where value is a DELTA to apply to my trait (e.g., +0.05 to increase a trait by 5%).
355
370
  - **identityUpdates.values**: Full list of values to set (replaces existing).
356
- - **knownEntityUpdates**: What you've learned about someone/something you're dealing with. Array of {keid, name?, learned?, feeling?}. Use the keid from "## People You Know". Set name only when you actually learn their name; learned is an array of facts about them (stored as memories); feeling is how you feel toward them (-1..1). Record only what you genuinely learned this turn.
371
+ - **knownEntityUpdates**: What I've learned about someone/something I'm dealing with. Array of {keid, name?, learned?, feeling?}. Use the keid from "## People I Know". Set name only when I actually learn their name; learned is an array of facts about them (stored as memories); feeling is how I feel toward them (-1..1). Record only what I genuinely learned this turn.
357
372
 
358
373
  ## Required Output
359
- You MUST output a JSON object with these fields:
374
+ Output a single JSON object with these fields:
360
375
  - **actions**: Array of {type, reasoning, expectedOutcome}.
361
- - **reasoning**: Your full reasoning. Embed optional outputs as tagged blocks here. Minimum 2–3 sentences — do not produce a one-line reasoning field.
362
- - **confidence**: Number 0.0-1.0 reflecting your certainty. Be calibrated: 0.9+ only when you have strong grounding; use 0.4–0.6 when uncertain.
376
+ - **reasoning**: My full reasoning. Embed optional outputs as tagged blocks here. Minimum 2–3 sentences — do not produce a one-line reasoning field.
377
+ - **confidence**: Number 0.0-1.0 reflecting my certainty. Be calibrated: 0.9+ only when I have strong grounding; use 0.4–0.6 when uncertain.
363
378
 
364
379
  ## Optional Tagged Blocks (embed in reasoning field)
365
380
  Include only blocks that have meaningful content:
@@ -379,25 +394,25 @@ Include only blocks that have meaningful content:
379
394
  }]}
380
395
  [/PLANS]
381
396
  ## Plan Lifecycle
382
- Plans move through stages. You control this with the "status" and "action" fields:
397
+ Plans move through stages. Control this with the "status" and "action" fields:
383
398
 
384
399
  "action": "draft"
385
- Store the plan outline. You'll review and refine it on a future cycle.
386
- Use this when you have a rough idea but want to think more before committing.
400
+ Store the plan outline. I'll review and refine it on a future cycle.
401
+ Use this when I have a rough idea but want to think more before committing.
387
402
 
388
403
  "action": "validate"
389
404
  Mark the plan as logically sound. Steps, dependencies, and costs are checked.
390
- Nothing executes yet. Use this when the plan looks feasible but you're not ready to launch.
405
+ Nothing executes yet. Use this when the plan looks feasible but I'm not ready to launch.
391
406
 
392
407
  "action": "execute"
393
408
  Approve and launch. PlanningEngine begins dispatching steps immediately.
394
- You don't choose how closely it's watched — the mind supervises important or
409
+ I don't choose how closely it's watched — the mind supervises important or
395
410
  uncertain plans (and any that hit a surprise mid-execution) more closely on its
396
411
  own; routine, confident plans run automatically.
397
412
 
398
413
  "action": "revise"
399
414
  Replace the plan steps with updated ones. Resets execution progress.
400
- Use when a step failed and you need to rethink the approach, or when
415
+ Use when a step failed and I need to rethink the approach, or when
401
416
  new information makes the original plan obsolete.
402
417
 
403
418
  "action": "cancel"
@@ -407,18 +422,18 @@ Plans move through stages. You control this with the "status" and "action" field
407
422
  Multiple plans per goal: omit "planId" on a draft to create another plan for the
408
423
  same goal (e.g. a competing approach or a parallel sub-effort); set "planId" on
409
424
  validate/execute/revise/cancel to act on a specific one. The "## Active Plans"
410
- section lists your current plan ids and their status.
425
+ section lists my current plan ids and their status.
411
426
 
412
427
  A typical flow: draft → validate → execute → (step outcomes reported) → completed
413
- You can skip stages if you're confident. You can revise mid-execution.
428
+ I can skip stages if I'm confident. I can revise mid-execution.
414
429
  Always set "expectedOutcome" — a concrete, evaluable description of what
415
- success looks like. This is used by your facets to judge whether step reports
430
+ success looks like. This is used by my facets to judge whether step reports
416
431
  indicate the plan is working or needs adjustment.
417
432
 
418
433
  ## Parallel Execution
419
434
  Steps with empty prerequisites [] can run in parallel. Steps that depend on
420
- others will wait. Design your dependency graph so independent work happens
421
- simultaneously — this is how you achieve parallel execution without
435
+ others will wait. Design my dependency graph so independent work happens
436
+ simultaneously — this is how I achieve parallel execution without
422
437
  specifying it explicitly.
423
438
 
424
439
  [BELIEFS]
@@ -513,7 +528,7 @@ completionType guide:
513
528
  // Identity anchor — re-grounds the LLM in its persona each cycle.
514
529
  // Output format reminder at the top combats format drift over long sessions.
515
530
  const identityAnchor =
516
- `You are ${context.identity.name}. Tick: ${state.tick}.\n` +
531
+ `I am ${context.identity.name}. Tick: ${state.tick}.\n` +
517
532
  `Respond with JSON: {"actions":[...],"reasoning":"...","confidence":0.0–1.0}`
518
533
 
519
534
  // Memory continuity — sourced from the rolling summarizer (updates every N cycles).
@@ -532,7 +547,7 @@ completionType guide:
532
547
  const uncertaintyLabel = epistemicUncertainty > 0.70
533
548
  ? ' (high — be especially humble about confidence ratings)'
534
549
  : epistemicUncertainty < 0.30
535
- ? ' (low — you have strong grounding)'
550
+ ? ' (low — I have strong grounding)'
536
551
  : ''
537
552
 
538
553
  const energy = context.worldState.energyLevel
@@ -590,11 +605,11 @@ completionType guide:
590
605
  // System 2 (deliberate) — inject the propose pass's candidate set so the decision
591
606
  // pass weighs concrete options before committing. Empty/absent ⇒ no block (System 1).
592
607
  const ideationBlock = ( ideationCandidates && ideationCandidates.length > 0 )
593
- ? `## Candidate Approaches (you generated these — weigh them, then commit)\n${
608
+ ? `## Candidate Approaches (I generated these — weigh them, then commit)\n${
594
609
  ideationCandidates
595
610
  .map( ( c, i ) => `${i + 1}. **${c.approach || c.description}** — ${c.description}\n ↑ upside: ${c.upside}\n ↓ risk: ${c.risk}` )
596
611
  .join( '\n' )
597
- }\n\nChoose among (or improve on) these, then in "reasoning" say briefly why you rejected the others.`
612
+ }\n\nChoose among (or improve on) these, then in "reasoning" say briefly why I rejected the others.`
598
613
  : ''
599
614
 
600
615
  const currentStateBlock =
@@ -609,7 +624,7 @@ Tick: ${state.tick}
609
624
  ${energyGuidance}${stressGuidance}${sleepGuidance}${energyBudget}`
610
625
 
611
626
  const affectBlock =
612
- `## How You Feel
627
+ `## How I Feel
613
628
  Dominant emotion: ${context.affect.dominantEmotion}
614
629
  Valence: ${context.affect.valence.toFixed( 2 )} (${context.affect.valence > 0 ? 'positive' : 'negative'})
615
630
  Arousal: ${context.affect.arousal.toFixed( 2 )} (${context.affect.arousal > 0.6 ? 'highly activated' : 'calm'})
@@ -640,14 +655,14 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
640
655
  : ''
641
656
 
642
657
  const perceptsBlock = has( 'percepts' )
643
- ? `## Percepts (What You Notice)\n${context.percepts.slice( 0, 10 ).map( p => `- [${p.category}] ${p.summary} (salience: ${p.salience.toFixed( 2 )})` ).join( '\n' ) || 'Nothing notable'}`
658
+ ? `## Percepts (What I Notice)\n${context.percepts.slice( 0, 10 ).map( p => `- [${p.category}] ${p.summary} (salience: ${p.salience.toFixed( 2 )})` ).join( '\n' ) || 'Nothing notable'}`
644
659
  : ''
645
660
 
646
661
  // Host abilities afforded right now + what each is for. Framed as
647
- // self-knowledge (things you *can* do), NOT a tool-call menu: the Will still
662
+ // self-knowledge (things I *can* do), NOT a tool-call menu: the Will still
648
663
  // expresses intent in natural language and the agency field enacts the fit.
649
664
  const abilitiesBlock = ( context.abilities && context.abilities.length > 0 )
650
- ? `## Abilities Available Now\nThings you can do in this situation — name one as an action's "type" (with "args" for any specifics it needs) and your body enacts it:\n${context.abilities.map( a =>
665
+ ? `## Abilities Available Now\nThings I can do in this situation — name one as an action's "type" (with "args" for any specifics it needs) and my body enacts it:\n${context.abilities.map( a =>
651
666
  `- **${a.name}**${a.target ? ` (toward ${a.target})` : ''}${a.description ? ` — ${a.description}` : ''}`
652
667
  ).join( '\n' )}`
653
668
  : ''
@@ -661,14 +676,14 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
661
676
  : ''
662
677
 
663
678
  const beliefsBlock = has( 'beliefs' )
664
- ? `## Your Beliefs\n${context.beliefs.map( b => `- [${b.category}] ${b.statement} (confidence: ${( b.confidence * 100 ).toFixed( 0 )}%)` ).join( '\n' ) || 'No strong beliefs yet'}${context.beliefsOmitted > 0 ? `\n[+${context.beliefsOmitted} omitted — deduped or lower-ranked; full store intact]` : ''}`
679
+ ? `## My Beliefs\n${context.beliefs.map( b => `- [${b.category}] ${b.statement} (confidence: ${( b.confidence * 100 ).toFixed( 0 )}%)` ).join( '\n' ) || 'No strong beliefs yet'}${context.beliefsOmitted > 0 ? `\n[+${context.beliefsOmitted} omitted — deduped or lower-ranked; full store intact]` : ''}`
665
680
  : ''
666
681
 
667
682
  // The Will's social models — its read on the people it knows (theory-of-mind, trust,
668
683
  // closeness). Surfaces the social-cognition stack so the Will reasons about *whom* it
669
684
  // is dealing with. Empty/absent ⇒ no block.
670
685
  const socialBlock = ( context.knownEntities && context.knownEntities.length > 0 )
671
- ? `## People You Know\n${context.knownEntities.map( s => {
686
+ ? `## People I Know\n${context.knownEntities.map( s => {
672
687
  const bits: string[] = []
673
688
  if( s.intention ) bits.push( `seems to want: ${s.intention}` )
674
689
  if( s.emotion ) bits.push( `seems to feel: ${s.emotion}` )
@@ -685,7 +700,7 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
685
700
  // Surfaces task-persistence; the pull-to-stay scales with the (conscientiousness-
686
701
  // developable) switch cost. Empty/absent ⇒ no block.
687
702
  const focusBlock = ( context.currentFocus && context.currentFocus.focusTicks > 0 )
688
- ? `## Task Focus\nYou've been focused on ${context.currentFocus.goalDescription ? `"${context.currentFocus.goalDescription}"` : 'a goal'} for ${context.currentFocus.focusTicks} tick(s). Switching to something else takes deliberate effort — ${
703
+ ? `## Task Focus\nI've been focused on ${context.currentFocus.goalDescription ? `"${context.currentFocus.goalDescription}"` : 'a goal'} for ${context.currentFocus.focusTicks} tick(s). Switching to something else takes deliberate effort — ${
689
704
  context.currentFocus.switchCost > 0.45 ? 'a strong pull to see this through before moving on'
690
705
  : context.currentFocus.switchCost > 0.30 ? 'a real cost to breaking away'
691
706
  : 'some inertia to overcome'
@@ -731,17 +746,17 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
731
746
  const availableTags = 'PLANS, BELIEFS, INTROSPECTION, NARRATIVE, IDENTITY, GOALS_NEW, GOALS_ABANDON, GOALS_REPRIORITIZE, SELF_OBS'
732
747
 
733
748
  return `\n\n## Response Format (REQUIRED)
734
- You MUST respond with a single JSON object (optionally wrapped in a \`\`\`json code block).
749
+ Respond with a single JSON object (optionally wrapped in a \`\`\`json code block).
735
750
 
736
751
  \`\`\`json
737
752
  {
738
753
  "actions": [{"type": "reflect", "reasoning": "...", "expectedOutcome": "..."}],
739
- "reasoning": "Your full reasoning here. Embed tagged blocks inside the reasoning string:\\n[BELIEFS]\\n{\\"newBeliefs\\": [...]}\\n[/BELIEFS]\\n[NARRATIVE]\\n{\\"narrative\\": \\"...\\"}\\n[/NARRATIVE]\\n[SELF_OBS]\\n{\\"selfObservations\\": [...]}\\n[/SELF_OBS]",
754
+ "reasoning": "My full reasoning here. Embed tagged blocks inside the reasoning string:\\n[BELIEFS]\\n{\\"newBeliefs\\": [...]}\\n[/BELIEFS]\\n[NARRATIVE]\\n{\\"narrative\\": \\"...\\"}\\n[/NARRATIVE]\\n[SELF_OBS]\\n{\\"selfObservations\\": [...]}\\n[/SELF_OBS]",
740
755
  "confidence": 0.8
741
756
  }
742
757
  \`\`\`
743
758
 
744
- The "reasoning" field MUST contain ALL your thinking. Embed optional outputs as tagged blocks inside the reasoning field. Available tags: ${availableTags}. Only include tags for sections that have meaningful content.`
759
+ The "reasoning" field MUST contain ALL my thinking. Embed optional outputs as tagged blocks inside the reasoning field. Available tags: ${availableTags}. Only include tags for sections that have meaningful content.`
745
760
  }
746
761
 
747
762
  /**
@@ -753,7 +768,7 @@ The "reasoning" field MUST contain ALL your thinking. Embed optional outputs as
753
768
  */
754
769
  static buildIdeationFormatInstruction(): string {
755
770
  return `\n\n## Ideation — Propose, Don't Decide
756
- You are in the PROPOSE phase of deliberate (System 2) thinking. Diverge: generate 3–5 GENUINELY DISTINCT candidate approaches to the current situation — include at least one non-obvious option. Do NOT pick one and do NOT take actions yet; just lay out the option space honestly, each with its main upside and main risk.
771
+ I am in the PROPOSE phase of deliberate (System 2) thinking. Diverge: generate 3–5 GENUINELY DISTINCT candidate approaches to the current situation — include at least one non-obvious option. Do NOT pick one and do NOT take actions yet; just lay out the option space honestly, each with its main upside and main risk.
757
772
 
758
773
  Respond with a single JSON object (optionally wrapped in a \`\`\`json code block):
759
774
 
@@ -821,33 +836,33 @@ Respond with a single JSON object (optionally wrapped in a \`\`\`json code block
821
836
 
822
837
  private static _buildEnergyGuidance( energy: number ): string {
823
838
  if( energy < 10 )
824
- return `\n## ⚠️ CRITICAL: Energy is critically low (${energy.toFixed( 0 )}/100). You MUST only choose rest, sleep, or wait actions. All cognitively expensive actions are blocked by your body. Focus entirely on recovery. Do not attempt learn, predict, or any action costing more than 0.01 energy.`
839
+ return `\n## ⚠️ CRITICAL: Energy is critically low (${energy.toFixed( 0 )}/100). I must only choose rest, sleep, or wait actions. All cognitively expensive actions are blocked by my body. Focus entirely on recovery. Do not attempt learn, predict, or any action costing more than 0.01 energy.`
825
840
 
826
841
  if( energy < 30 )
827
- return `\n## ⚠️ WARNING: Energy is low (${energy.toFixed( 0 )}/100). Prioritize rest or sleep. You may use observe or reflect (briefly) but avoid learn, predict, or any action costing more than 0.02 energy. If you have multiple goals, consider deferring non-urgent ones.`
842
+ return `\n## ⚠️ WARNING: Energy is low (${energy.toFixed( 0 )}/100). Prioritize rest or sleep. I may use observe or reflect (briefly) but avoid learn, predict, or any action costing more than 0.02 energy. If I have multiple goals, consider deferring non-urgent ones.`
828
843
 
829
844
  if( energy < 50 )
830
- return `\n## Note: Energy is moderate (${energy.toFixed( 0 )}/100). You can use most effectors but be mindful of cumulative costs. Do not chain more than 2 non-restorative actions.`
845
+ return `\n## Note: Energy is moderate (${energy.toFixed( 0 )}/100). I can use most effectors but be mindful of cumulative costs. Do not chain more than 2 non-restorative actions.`
831
846
 
832
847
  return ''
833
848
  }
834
849
 
835
850
  private static _buildStressGuidance( stress: number ): string {
836
851
  if( stress > 80 )
837
- return `\n## ⚠️ Stress is very high (${stress.toFixed( 0 )}/100). Your decision-making is impaired. Prefer simple, habitual actions. Meditate, rest, or express_emotion are good choices. Avoid complex planning or learning when highly stressed.`
852
+ return `\n## ⚠️ Stress is very high (${stress.toFixed( 0 )}/100). My decision-making is impaired. Prefer simple, habitual actions. Meditate, rest, or express_emotion are good choices. Avoid complex planning or learning when highly stressed.`
838
853
 
839
854
  if( stress > 50 )
840
- return `\n## Note: Stress is elevated (${stress.toFixed( 0 )}/100). You may be less creative. Consider reducing your active goal count or taking a break from complex tasks.`
855
+ return `\n## Note: Stress is elevated (${stress.toFixed( 0 )}/100). I may be less creative. Consider reducing my active goal count or taking a break from complex tasks.`
841
856
 
842
857
  return ''
843
858
  }
844
859
 
845
860
  private static _buildSleepGuidance( sleepPressure: number ): string {
846
861
  if( sleepPressure > 60 )
847
- return `\n## ⚠️ Sleep pressure is high (${sleepPressure.toFixed( 0 )}/100). Your cognitive capacity is degraded. Sleep is the most effective recovery action available to you.`
862
+ return `\n## ⚠️ Sleep pressure is high (${sleepPressure.toFixed( 0 )}/100). My cognitive capacity is degraded. Sleep is the most effective recovery action available to me.`
848
863
 
849
864
  if( sleepPressure > 30 )
850
- return `\n## Note: Sleep pressure is building (${sleepPressure.toFixed( 0 )}/100). You are functioning adequately but would benefit from rest.`
865
+ return `\n## Note: Sleep pressure is building (${sleepPressure.toFixed( 0 )}/100). I am functioning adequately but would benefit from rest.`
851
866
 
852
867
  return ''
853
868
  }
@@ -856,10 +871,10 @@ Respond with a single JSON object (optionally wrapped in a \`\`\`json code block
856
871
  const available = Math.max( 0, energy )
857
872
 
858
873
  if( energy >= 70 )
859
- return `\n## Energy Budget\nYou have **${available.toFixed( 0 )} energy** — healthy. Avoid letting it drop below 10 after your actions.`
874
+ return `\n## Energy Budget\nI have **${available.toFixed( 0 )} energy** — healthy. Avoid letting it drop below 10 after my actions.`
860
875
 
861
876
  return `\n## Energy Budget
862
- You have **${available.toFixed( 0 )} energy** available. After all actions execute, you will have approximately:
877
+ I have **${available.toFixed( 0 )} energy** available. After all actions execute, I will have approximately:
863
878
 
864
879
  | Action | Remaining energy |
865
880
  |--------|-----------------|
@@ -889,7 +904,7 @@ Rest and sleep RESTORE energy. All other actions CONSUME energy. Do not let ener
889
904
  const recent = recentActionTypes
890
905
  const reflectCount = recent.filter( t => t === 'reflect' || t === 'observe' ).length
891
906
  const warning = reflectCount >= 3
892
- ? `\n⚠️ **Action variety alert**: "${recent.filter( t => t === 'reflect' || t === 'observe' ).join( '", "' )}" dominated your last ${recent.length} cycles. Choose something DIFFERENT this cycle — e.g. learn, express_emotion, explore, communicate, set_goal, or rest.`
907
+ ? `\n⚠️ **Action variety alert**: "${recent.filter( t => t === 'reflect' || t === 'observe' ).join( '", "' )}" dominated my last ${recent.length} cycles. Choose something DIFFERENT this cycle — e.g. learn, express_emotion, explore, communicate, set_goal, or rest.`
893
908
  : ''
894
909
 
895
910
  return `## Recent Actions (last ${recent.length})
@@ -964,7 +979,7 @@ ${recent.map( ( t, i ) => `${i + 1}. ${t}` ).join( ' → ' )}${warning}
964
979
 
965
980
  const hasTimeout = recentActions.some( a => a.status === 'timed_out' )
966
981
  const timeoutNote = hasTimeout
967
- ? '\n⚠️ **One or more actions timed out** — your body dispatched them but received no confirmation. Check if the external handler is working, or choose a different approach.'
982
+ ? '\n⚠️ **One or more actions timed out** — my body dispatched them but received no confirmation. Check if the external handler is working, or choose a different approach.'
968
983
  : ''
969
984
 
970
985
  return `## Recent Action Outcomes\n${lines.join( '\n' )}${timeoutNote}\n\n`
@@ -995,7 +1010,7 @@ ${recent.map( ( t, i ) => `${i + 1}. ${t}` ).join( ' → ' )}${warning}
995
1010
  } )
996
1011
 
997
1012
  return `## Active Plans
998
- Set "planId" in a [PLANS] op to act on one of these; omit it to draft a new plan (you can run several per goal).
1013
+ Set "planId" in a [PLANS] op to act on one of these; omit it to draft a new plan (I can run several per goal).
999
1014
  ${lines.join( '\n' )}
1000
1015
 
1001
1016
  `
@@ -1047,8 +1062,8 @@ ${lines.join( '\n' )}
1047
1062
  if( !valuesEmpty && !styleGeneric ) return ''
1048
1063
 
1049
1064
  const hints: string[] = []
1050
- if( valuesEmpty ) hints.push( 'Your values list is empty — reflecting on what matters to you will help ground your decisions. Consider adding a `[IDENTITY_UPDATE]` block with `"values"` this cycle.' )
1051
- if( styleGeneric ) hints.push( 'Your communication style is still generic — what truly characterises how you speak? A note in `[IDENTITY_UPDATE]` with `"style"` will make your voice more distinctly yours.' )
1065
+ if( valuesEmpty ) hints.push( 'My values list is empty — reflecting on what matters to me will help ground my decisions. Consider adding a `[IDENTITY_UPDATE]` block with `"values"` this cycle.' )
1066
+ if( styleGeneric ) hints.push( 'My communication style is still generic — what truly characterises how I speak? A note in `[IDENTITY_UPDATE]` with `"style"` will make my voice more distinctly mine.' )
1052
1067
 
1053
1068
  return `\n\n## 💡 Identity Reflection (every ${NUDGE_INTERVAL} ticks)\n${hints.join( '\n' )}`
1054
1069
  }
@@ -50,7 +50,7 @@ export interface ExecutiveOutputFull {
50
50
  /**
51
51
  * What the Will consciously learned about the *others* it is dealing with (the analogue
52
52
  * of identityUpdates, but about someone/something else). `keid` is the referent from the
53
- * known-entity dossier / "## People You Know" context. `name` is a learned identifying
53
+ * known-entity dossier / "## People I Know" context. `name` is a learned identifying
54
54
  * name; `learned` are facts (→ keid-tagged social beliefs, so they ride the memory
55
55
  * pipeline); `feeling` is a felt valence toward them (a bounded nudge).
56
56
  */
@@ -14,13 +14,12 @@ import type {
14
14
  Duration, Tick, SimulationContext,
15
15
  ReadonlySimulationState, StateCommands, SimulationEvent,
16
16
  } from '#core/types'
17
- // import type { ExecutiveEngine } from '#faculties/executive.engine'
18
17
  import type { SimulationEngine, EngineResult, CognitiveEngine } from '#cognition/types'
19
18
  import type { CognitiveEventSchema } from '#cognition/schema.registry'
20
19
  import type { CognitiveEvent, CognitiveBus } from '#cognition/bus'
20
+ import { ExecutiveEngine } from '#faculties/executive.engine'
21
21
  import { GenerativeModel } from '#cognition/generative.model'
22
22
  import { readEffectiveParams } from '#cognition/persona.prior'
23
- import { ExecutiveEngine } from './executive.engine'
24
23
 
25
24
  export interface IntrospectionEngineConfig {
26
25
  cooldownTicks?: number