@mindot/will 0.5.0 → 0.7.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 (136) hide show
  1. package/dist/channels/discord.d.ts +1 -1
  2. package/dist/channels/discord.js.map +1 -1
  3. package/dist/channels/whatsapp.d.ts +1 -1
  4. package/dist/channels/whatsapp.js.map +1 -1
  5. package/dist/cli.js +998 -59
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +998 -59
  9. package/dist/index.js.map +1 -1
  10. package/dist/mcp/effectors.d.ts +1 -1
  11. package/dist/mcp/effectors.js.map +1 -1
  12. package/dist/{will-BDq-TMQr.d.ts → will-DAW0l-lY.d.ts} +90 -10
  13. package/package.json +1 -1
  14. package/src/channels/discord.ts +11 -11
  15. package/src/channels/roster.ts +1 -1
  16. package/src/channels/types.ts +1 -1
  17. package/src/channels/whatsapp.ts +25 -25
  18. package/src/cli.ts +20 -20
  19. package/src/cognition/acp.ts +28 -0
  20. package/src/cognition/agency/consequence.ts +174 -0
  21. package/src/cognition/agency/engines/action.selector.ts +333 -48
  22. package/src/cognition/agency/engines/affordance.synthesizer.ts +35 -16
  23. package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
  24. package/src/cognition/agency/engines/motor.schema.executor.ts +90 -14
  25. package/src/cognition/agency/engines/reafference.engine.ts +152 -23
  26. package/src/cognition/agency/execution.primitives.ts +13 -13
  27. package/src/cognition/agency/proactive.communicator.ts +1 -1
  28. package/src/cognition/agency/reconcile.learning.ts +10 -1
  29. package/src/cognition/agency/revocation.ts +79 -0
  30. package/src/cognition/agency/schemas/repertoire.ts +109 -9
  31. package/src/cognition/agency/selection.scoring.ts +32 -10
  32. package/src/cognition/agency/types.ts +9 -0
  33. package/src/cognition/bus.ts +2 -2
  34. package/src/cognition/completion.inbox.ts +1 -1
  35. package/src/cognition/config.mirror.entities.ts +5 -5
  36. package/src/cognition/event.log.ts +2 -2
  37. package/src/cognition/event.schemas.ts +67 -67
  38. package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
  39. package/src/cognition/faculties/affective.blender.ts +36 -4
  40. package/src/cognition/faculties/attachment.evaluator.ts +6 -6
  41. package/src/cognition/faculties/attention.allocator.ts +63 -8
  42. package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
  43. package/src/cognition/faculties/bias.detector.ts +7 -7
  44. package/src/cognition/faculties/circadian.oscillator.ts +3 -3
  45. package/src/cognition/faculties/confidence.calibrator.ts +7 -7
  46. package/src/cognition/faculties/dream.simulator.ts +5 -5
  47. package/src/cognition/faculties/empathy.simulator.ts +7 -7
  48. package/src/cognition/faculties/energy.regulator.ts +4 -4
  49. package/src/cognition/faculties/episodic.consolidator.ts +6 -6
  50. package/src/cognition/faculties/executive.engine/commands.ts +6 -6
  51. package/src/cognition/faculties/executive.engine/context.ts +42 -42
  52. package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
  53. package/src/cognition/faculties/executive.engine/engine.ts +25 -25
  54. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
  55. package/src/cognition/faculties/executive.engine/facet.ts +18 -18
  56. package/src/cognition/faculties/executive.engine/gating.ts +2 -2
  57. package/src/cognition/faculties/executive.engine/messages.ts +3 -3
  58. package/src/cognition/faculties/executive.engine/parser.ts +11 -11
  59. package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
  60. package/src/cognition/faculties/exteroception.ts +99 -5
  61. package/src/cognition/faculties/forgetting.curve.ts +3 -3
  62. package/src/cognition/faculties/frustration.evaluator.ts +6 -6
  63. package/src/cognition/faculties/goal.manager.ts +15 -15
  64. package/src/cognition/faculties/inhibition.controller.ts +3 -3
  65. package/src/cognition/faculties/interoception.ts +3 -3
  66. package/src/cognition/faculties/introspection.engine.ts +3 -3
  67. package/src/cognition/faculties/known.entity.tracker.ts +15 -15
  68. package/src/cognition/faculties/loss.evaluator.ts +2 -2
  69. package/src/cognition/faculties/moral.evaluator.ts +4 -4
  70. package/src/cognition/faculties/novelty.detector.ts +4 -4
  71. package/src/cognition/faculties/persona.consolidator.ts +12 -12
  72. package/src/cognition/faculties/planning.engine/engine.ts +47 -47
  73. package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
  74. package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
  75. package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
  76. package/src/cognition/faculties/reputation.tracker.ts +6 -6
  77. package/src/cognition/faculties/reward.evaluator.ts +4 -4
  78. package/src/cognition/faculties/self.model.updater.ts +8 -8
  79. package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
  80. package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
  81. package/src/cognition/faculties/social.perception.ts +3 -3
  82. package/src/cognition/faculties/spaced.repetition.ts +2 -2
  83. package/src/cognition/faculties/stress.regulator.ts +27 -4
  84. package/src/cognition/faculties/task.switcher.ts +11 -6
  85. package/src/cognition/faculties/theory.of.mind.ts +6 -6
  86. package/src/cognition/faculties/threat.evaluator.ts +3 -3
  87. package/src/cognition/faculties/working.memory.ts +8 -8
  88. package/src/cognition/generative.model.ts +2 -2
  89. package/src/cognition/memory/vector.adapter.ts +5 -5
  90. package/src/cognition/memory/vector.content.ts +4 -4
  91. package/src/cognition/memory/vector.embedder.ts +9 -9
  92. package/src/cognition/persona.prior.ts +1 -1
  93. package/src/cognition/schema.registry.ts +2 -2
  94. package/src/cognition/senses/audition.engine/engine.ts +2 -2
  95. package/src/cognition/senses/base.sense.engine.ts +1 -1
  96. package/src/cognition/utilities/token.tracker.ts +5 -5
  97. package/src/core/abstracts.ts +1 -1
  98. package/src/core/completion.recorder.ts +2 -2
  99. package/src/core/serialization.ts +3 -3
  100. package/src/core/snapshot.manager.ts +1 -1
  101. package/src/core/state.manager.ts +1 -1
  102. package/src/core/utils.ts +1 -1
  103. package/src/host/boot.ts +30 -30
  104. package/src/host/utterances.ts +1 -1
  105. package/src/llm/index.ts +9 -9
  106. package/src/llm/summarizer.ts +4 -4
  107. package/src/llm/wire.contracts.ts +1 -1
  108. package/src/mcp/effectors.ts +10 -10
  109. package/src/mcp/server.ts +11 -11
  110. package/src/pma/eval.ts +1 -1
  111. package/src/pma/index.ts +21 -21
  112. package/src/runners/outreach.runner.ts +3 -3
  113. package/src/runners/social.runner.ts +1 -1
  114. package/src/runners/thin-shim.runner.ts +1 -1
  115. package/src/sdk/will.ts +5 -5
  116. package/src/serve/server.ts +12 -12
  117. package/src/stem/assembly.audit.ts +3 -3
  118. package/src/stem/guards/identity.coherence.ts +10 -10
  119. package/src/stem/guards/identity.guard.ts +22 -22
  120. package/src/stem/index.ts +26 -11
  121. package/src/stem/mind.ts +12 -12
  122. package/src/stem/policy/arbiter.ts +136 -0
  123. package/src/stem/policy/rule.table.ts +172 -0
  124. package/src/stem/policy/verdict.recorder.ts +0 -0
  125. package/src/stem/tracts/biography.writer.ts +3 -3
  126. package/src/stem/tracts/effector.controller.ts +383 -4
  127. package/src/stem/tracts/health.reporter.ts +1 -1
  128. package/src/stem/tracts/outbox.controller.ts +4 -0
  129. package/src/stem/tracts/outbox.writer.ts +4 -4
  130. package/src/stem/tracts/pma.controller.ts +1 -1
  131. package/src/stem/tracts/replay.controller.ts +10 -10
  132. package/src/stem/tracts/sensory.controller.ts +2 -2
  133. package/src/stem/tracts/session.logger.ts +4 -4
  134. package/src/stem/tracts/transport/socketio.transport.ts +7 -7
  135. package/src/stem/tracts/transport/stream.transport.ts +2 -2
  136. package/src/stem/tracts/transport.controller.ts +15 -15
@@ -68,10 +68,10 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
68
68
  this._tokenTracker = config.tokenTracker ?? null
69
69
  }
70
70
 
71
- async embed( content: unknown, fn: string = 'recall' ): Promise<number[]> {
71
+ async embed( content: unknown, fn: string = 'recall'): Promise<number[]> {
72
72
  let response: Response
73
73
  try {
74
- response = await fetch( `${this._apiUrl}/embeddings`, {
74
+ response = await fetch(`${this._apiUrl}/embeddings`, {
75
75
  method: 'POST',
76
76
  headers: {
77
77
  'Content-Type': 'application/json',
@@ -87,13 +87,13 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
87
87
  }
88
88
  catch( err ){
89
89
  // AbortSignal.timeout rejects with a DOMException named 'TimeoutError'.
90
- if( err instanceof Error && err.name === 'TimeoutError' )
91
- throw new Error( `Embedding request timed out after ${this._timeoutMs}ms` )
90
+ if( err instanceof Error && err.name === 'TimeoutError')
91
+ throw new Error(`Embedding request timed out after ${this._timeoutMs}ms`)
92
92
  throw err
93
93
  }
94
94
 
95
95
  if( !response.ok )
96
- throw new Error( `Embedding failed: ${response.status} ${response.statusText}` )
96
+ throw new Error(`Embedding failed: ${response.status} ${response.statusText}`)
97
97
 
98
98
  const data = await response.json() as {
99
99
  data?: Array<{ embedding?: number[] }>
@@ -104,7 +104,7 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
104
104
  // opaque TypeError on an empty/malformed body (FN16).
105
105
  const embedding = data?.data?.[ 0 ]?.embedding
106
106
  if( !Array.isArray( embedding ) || embedding.length === 0 )
107
- throw new Error( `Embedding response was empty or malformed for model ${this.modelName}` )
107
+ throw new Error(`Embedding response was empty or malformed for model ${this.modelName}`)
108
108
 
109
109
  // Meter embedding token usage (input-only — embeddings have no completion).
110
110
  // Recorded under the 'embedding' category so per-Will dashboards can split
@@ -128,7 +128,7 @@ export class OpenAICompatibleEmbedder implements EmbeddingProvider {
128
128
  return embedding
129
129
  }
130
130
 
131
- async embedBatch( contents: unknown[], fn: string = 'index' ): Promise<number[][]> {
131
+ async embedBatch( contents: unknown[], fn: string = 'index'): Promise<number[][]> {
132
132
  // Bounded fan-out: cap concurrent requests at _maxConcurrency instead of
133
133
  // firing all of them at once (FN16), while preserving input order.
134
134
  const results: number[][] = new Array( contents.length )
@@ -170,7 +170,7 @@ export class MockEmbedder implements EmbeddingProvider {
170
170
  this._seed = seed
171
171
  }
172
172
 
173
- async embed( content: unknown, _fn: string = 'recall' ): Promise<number[]> {
173
+ async embed( content: unknown, _fn: string = 'recall'): Promise<number[]> {
174
174
  const str = typeof content === 'string' ? content : JSON.stringify( content )
175
175
  const hash = this._hashString( str )
176
176
  const embedding: number[] = []
@@ -184,7 +184,7 @@ export class MockEmbedder implements EmbeddingProvider {
184
184
  return embedding
185
185
  }
186
186
 
187
- async embedBatch( contents: unknown[], fn: string = 'index' ): Promise<number[][]> {
187
+ async embedBatch( contents: unknown[], fn: string = 'index'): Promise<number[][]> {
188
188
  return Promise.all( contents.map( c => this.embed( c, fn ) ) )
189
189
  }
190
190
 
@@ -77,7 +77,7 @@ export function readEffectiveParams(
77
77
 
78
78
  const out: Record<string, number> = { ...base }
79
79
  for( const [ key, delta ] of Object.entries( prior ) )
80
- if( typeof out[ key ] === 'number' && typeof delta === 'number' )
80
+ if( typeof out[ key ] === 'number' && typeof delta === 'number')
81
81
  out[ key ] = out[ key ] + delta
82
82
 
83
83
  return out
@@ -137,8 +137,8 @@ globalSchemaRegistry.register({
137
137
  version: 1,
138
138
  validate( payload ){
139
139
  if( !isRecord( payload ) ) return 'clock.tick payload must be an object'
140
- if( typeof payload['tick'] !== 'number' ) return 'clock.tick requires numeric tick'
141
- if( typeof payload['delta'] !== 'number' ) return 'clock.tick requires numeric delta'
140
+ if( typeof payload['tick'] !== 'number') return 'clock.tick requires numeric tick'
141
+ if( typeof payload['delta'] !== 'number') return 'clock.tick requires numeric delta'
142
142
 
143
143
  return null
144
144
  }
@@ -474,7 +474,7 @@ export class AuditionEngine extends BaseSenseEngine {
474
474
  attachmentScore: this._getAttachmentScore?.( entityId ) ?? 0,
475
475
  activeGoalText: this._getActiveGoalText?.() ?? [],
476
476
  })
477
- const salience = this._model.observe( `audition.${entityId}`, langEnergy ).salience
477
+ const salience = this._model.observe(`audition.${entityId}`, langEnergy ).salience
478
478
 
479
479
  // ── Percept ────────────────────────────────────────────────
480
480
  const percept: LanguagePercept = {
@@ -717,7 +717,7 @@ export class AuditionEngine extends BaseSenseEngine {
717
717
  goalsToAbandon: output.goalsToAbandon,
718
718
  newBeliefs: output.newBeliefs,
719
719
  knownEntityUpdates: output.knownEntityUpdates,
720
- requiresMasterAttention: ( output.actions ?? [] ).some( a => a.type === 'escalate' ),
720
+ requiresMasterAttention: ( output.actions ?? [] ).some( a => a.type === 'escalate'),
721
721
  }
722
722
  }
723
723
  }
@@ -112,6 +112,6 @@ export abstract class ShellSenseEngine extends BaseSenseEngine {
112
112
  }
113
113
 
114
114
  protected async _perceive( _input: SensoryInput ): Promise<void> {
115
- logger.warn( `[${this.name}] ingest() called — engine is a shell, not yet implemented.` )
115
+ logger.warn(`[${this.name}] ingest() called — engine is a shell, not yet implemented.`)
116
116
  }
117
117
  }
@@ -96,9 +96,9 @@ const CACHE_WRITE_MULT = 1.25
96
96
  */
97
97
  function normalizeModelKey( model: string ): string {
98
98
  let m = model.toLowerCase().trim()
99
- const slash = m.lastIndexOf( '/' )
99
+ const slash = m.lastIndexOf('/')
100
100
  if( slash >= 0 ) m = m.slice( slash + 1 ) // drop "provider/" prefix
101
- return m.replace( /[-@]\d{6,8}$/, '' ) // drop trailing -YYYYMMDD date stamp
101
+ return m.replace( /[-@]\d{6,8}$/, '') // drop trailing -YYYYMMDD date stamp
102
102
  }
103
103
 
104
104
  // Pre-index the pricing table by normalized model name for O(1), date-insensitive
@@ -106,7 +106,7 @@ function normalizeModelKey( model: string ): string {
106
106
  const PRICING_BY_NORM: Record<string, { input: number; output: number }> = ( () => {
107
107
  const out: Record<string, { input: number; output: number }> = {}
108
108
  for( const [ key, price ] of Object.entries( MODEL_PRICING ) ){
109
- if( key === '__default__' ) continue
109
+ if( key === '__default__') continue
110
110
  out[ normalizeModelKey( key ) ] = price
111
111
  }
112
112
  return out
@@ -305,10 +305,10 @@ export class TokenTracker implements SimulationEngine {
305
305
  if( this._ledgerPath ){
306
306
  try {
307
307
  if( !this._ledgerDirReady ){
308
- mkdirSync( this._ledgerPath.slice( 0, this._ledgerPath.lastIndexOf( '/' ) ), { recursive: true } )
308
+ mkdirSync( this._ledgerPath.slice( 0, this._ledgerPath.lastIndexOf('/') ), { recursive: true } )
309
309
  this._ledgerDirReady = true
310
310
  }
311
- appendFileSync( this._ledgerPath, JSON.stringify( record ) + '\n' )
311
+ appendFileSync( this._ledgerPath, JSON.stringify( record ) + '\n')
312
312
  }
313
313
  catch { /* ledger file is best-effort */ }
314
314
  }
@@ -47,7 +47,7 @@ export class BunStorageAdapter implements StorageAdapter {
47
47
  return Bun.file( path ).text()
48
48
 
49
49
  const { readFile } = await import('node:fs/promises')
50
- return readFile( path, 'utf8' )
50
+ return readFile( path, 'utf8')
51
51
  }
52
52
 
53
53
  async readBytes( path: string ): Promise<Uint8Array> {
@@ -97,11 +97,11 @@ export class RecordedCompletionSource implements LLMCompletionSource {
97
97
  nextCompletion( tick: number, systemPrompt: string, userMessage: string ): LLMCompletionRecord {
98
98
  const queue = this._byTick.get( tick )
99
99
  if( !queue || queue.length === 0 )
100
- throw new Error( `replay: no recorded LLM completion for tick ${tick} (recording is incomplete or call timing diverged)` )
100
+ throw new Error(`replay: no recorded LLM completion for tick ${tick} (recording is incomplete or call timing diverged)`)
101
101
 
102
102
  const idx = queue.findIndex( r => r.systemPrompt === systemPrompt && r.userMessage === userMessage )
103
103
  if( idx === -1 )
104
- throw new Error( `replay: LLM prompt diverged at tick ${tick} — no recorded completion matches this call (upstream non-determinism)` )
104
+ throw new Error(`replay: LLM prompt diverged at tick ${tick} — no recorded completion matches this call (upstream non-determinism)`)
105
105
 
106
106
  return queue.splice( idx, 1 )[ 0 ]!
107
107
  }
@@ -222,7 +222,7 @@ export class DefaultSerializer implements Serializer {
222
222
  if( Array.isArray( value ) )
223
223
  return value.map( v => this._encodeSpecial( v ) )
224
224
 
225
- if( typeof value === 'object' ){
225
+ if( typeof value === 'object'){
226
226
  const out: Record<string, unknown> = {}
227
227
  for( const [ k, v ] of Object.entries( value as Record<string, unknown> ) )
228
228
  out[k] = this._encodeSpecial( v )
@@ -233,11 +233,11 @@ export class DefaultSerializer implements Serializer {
233
233
  }
234
234
 
235
235
  private _decodeSpecial( value: unknown ): unknown {
236
- if( value === null || typeof value !== 'object' ) return value
236
+ if( value === null || typeof value !== 'object') return value
237
237
  if( Array.isArray( value ) ) return value.map( v => this._decodeSpecial( v ) )
238
238
 
239
239
  const tag = ( value as Record<string, unknown> ).__ser
240
- if( typeof tag === 'string' ){
240
+ if( typeof tag === 'string'){
241
241
  const payload = ( value as Record<string, unknown> ).__val
242
242
  switch( tag ){
243
243
  case 'undefined': return undefined
@@ -243,7 +243,7 @@ export class SnapshotManager {
243
243
  return this._serializer.deserialize( JSON.stringify( parsed ) )
244
244
  }
245
245
  catch( err ){
246
- logger.warn( `[SnapshotManager] Could not load latest snapshot:`, err )
246
+ logger.warn(`[SnapshotManager] Could not load latest snapshot:`, err )
247
247
  return undefined
248
248
  }
249
249
  }
@@ -14,7 +14,7 @@ import { deepFreeze } from '#core/utils'
14
14
  */
15
15
  function resolveFreezeState(): boolean {
16
16
  const flag = process.env.WILL_FREEZE_STATE
17
- if( flag !== undefined && flag !== '' )
17
+ if( flag !== undefined && flag !== '')
18
18
  return flag !== '0' && flag.toLowerCase() !== 'false'
19
19
  return process.env.NODE_ENV !== 'production'
20
20
  }
package/src/core/utils.ts CHANGED
@@ -52,7 +52,7 @@ export function createPRNG( seed: number ): SeededPRNG {
52
52
  */
53
53
  export function deepFreeze<T>( value: T ): T {
54
54
  // Only objects/arrays/functions are freezable; primitives are immutable already.
55
- if( value === null || typeof value !== 'object' ) return value
55
+ if( value === null || typeof value !== 'object') return value
56
56
  if( Object.isFrozen( value ) ) return value
57
57
 
58
58
  // Freeze the container first so a cyclic reference back to it hits the
package/src/host/boot.ts CHANGED
@@ -37,12 +37,12 @@ import { anthropicWireHeaders, defaultBaseFor, defaultModelFor } from '#llm/inde
37
37
  */
38
38
  export function routeLogsToStderr(): void {
39
39
  const err = ( level: string ) => ( msg: string, ...rest: unknown[] ) =>
40
- console.error( `[will:${ level }] ${ msg }`, ...rest )
41
- setLogger( { debug: () => {}, info: err( 'info' ), warn: err( 'warn' ), error: err( 'error' ) } )
40
+ console.error(`[will:${ level }] ${ msg }`, ...rest )
41
+ setLogger( { debug: () => {}, info: err('info'), warn: err('warn'), error: err('error') } )
42
42
  }
43
43
 
44
44
  function slug( s: string ): string {
45
- return s.toLowerCase().replace( /[^a-z0-9]+/g, '-' ).replace( /^-+|-+$/g, '' ) || 'will'
45
+ return s.toLowerCase().replace( /[^a-z0-9]+/g, '-').replace( /^-+|-+$/g, '') || 'will'
46
46
  }
47
47
 
48
48
  /** The LLM mode the hosts will boot with: an explicit WILL_LLM, else whichever
@@ -57,7 +57,7 @@ export function resolveLlmMode(): 'mock' | 'anthropic' | 'glm' {
57
57
 
58
58
  /** The key for a live mode — the provider-agnostic override first, then the
59
59
  * provider's own env. */
60
- function resolveLlmKey( mode: 'anthropic' | 'glm' ): string | undefined {
60
+ function resolveLlmKey( mode: 'anthropic' | 'glm'): string | undefined {
61
61
  return process.env.WILL_LLM_API_KEY
62
62
  ?? ( mode === 'glm' ? process.env.ZAI_API_KEY : process.env.ANTHROPIC_API_KEY )
63
63
  }
@@ -79,13 +79,13 @@ function resolveLlmKey( mode: 'anthropic' | 'glm' ): string | undefined {
79
79
  */
80
80
  async function preflightLLM( anatomy: string ): Promise<void> {
81
81
  const mode = resolveLlmMode()
82
- if( mode === 'mock' || anatomy === 'reflex' ) return
82
+ if( mode === 'mock' || anatomy === 'reflex') return
83
83
 
84
84
  const key = resolveLlmKey( mode )
85
85
  if( !key ){
86
86
  const expected = mode === 'glm' ? 'ZAI_API_KEY' : 'ANTHROPIC_API_KEY'
87
- console.error( `[will] WILL_LLM=${ mode } but no ${ expected } / WILL_LLM_API_KEY is set.` )
88
- console.error( '[will] The Will would boot, perceive, and never speak. Set a key, or run keyless with WILL_LLM=mock.' )
87
+ console.error(`[will] WILL_LLM=${ mode } but no ${ expected } / WILL_LLM_API_KEY is set.`)
88
+ console.error('[will] The Will would boot, perceive, and never speak. Set a key, or run keyless with WILL_LLM=mock.')
89
89
  process.exit( 2 )
90
90
  }
91
91
 
@@ -95,9 +95,9 @@ async function preflightLLM( anatomy: string ): Promise<void> {
95
95
  // speaks the same wire at Z.ai's compat endpoint, so one ping serves both.
96
96
  const base = process.env.WILL_LLM_BASE_URL ?? defaultBaseFor( mode )
97
97
  const model = process.env.WILL_LLM_MODEL
98
- ?? ( mode === 'glm' ? defaultModelFor( 'glm' ) : 'claude-haiku-4-5-20251001' )
98
+ ?? ( mode === 'glm' ? defaultModelFor('glm') : 'claude-haiku-4-5-20251001')
99
99
  try {
100
- const res = await fetch( `${ base }/messages`, {
100
+ const res = await fetch(`${ base }/messages`, {
101
101
  method: 'POST',
102
102
  headers: anthropicWireHeaders( mode, key ),
103
103
  body: JSON.stringify( { model, max_tokens: 1, messages: [ { role: 'user', content: 'ping' } ] } ),
@@ -105,20 +105,20 @@ async function preflightLLM( anatomy: string ): Promise<void> {
105
105
  } )
106
106
  if( res.ok ) return
107
107
 
108
- const detail = ( await res.text().catch( () => '' ) ).slice( 0, 300 )
108
+ const detail = ( await res.text().catch( () => '') ).slice( 0, 300 )
109
109
  const fatal = res.status === 400 || res.status === 401 || res.status === 403
110
110
  if( !fatal ){
111
- console.error( `[will] the executive's LLM answered ${ res.status } on a test call — raising the mind anyway (it retries): ${ detail }` )
111
+ console.error(`[will] the executive's LLM answered ${ res.status } on a test call — raising the mind anyway (it retries): ${ detail }`)
112
112
  return
113
113
  }
114
- console.error( `[will] the executive's LLM refused a test call (${ res.status }) — this Will would boot, perceive, and never speak:` )
115
- console.error( ` ${ detail }` )
116
- console.error( '[will] fix the key / credit / model above, or run keyless with WILL_LLM=mock.' )
114
+ console.error(`[will] the executive's LLM refused a test call (${ res.status }) — this Will would boot, perceive, and never speak:`)
115
+ console.error(` ${ detail }`)
116
+ console.error('[will] fix the key / credit / model above, or run keyless with WILL_LLM=mock.')
117
117
  process.exit( 1 )
118
118
  }
119
119
  catch( e ){
120
- console.error( `[will] could not reach the executive's LLM: ${ ( e as Error ).message }` )
121
- console.error( '[will] the Will would boot and stay silent. Check the network / WILL_LLM_BASE_URL, or run keyless with WILL_LLM=mock.' )
120
+ console.error(`[will] could not reach the executive's LLM: ${ ( e as Error ).message }`)
121
+ console.error('[will] the Will would boot and stay silent. Check the network / WILL_LLM_BASE_URL, or run keyless with WILL_LLM=mock.')
122
122
  process.exit( 1 )
123
123
  }
124
124
  }
@@ -138,8 +138,8 @@ export interface BootedWill {
138
138
  /** Raise the mind from env config — wake from the artifact if one exists. */
139
139
  export async function bootWillFromEnv(): Promise<BootedWill> {
140
140
  const name = process.env.WILL_NAME ?? 'Will'
141
- const pmaPath = resolve( process.env.WILL_PMA_PATH ?? `.will/${ slug( name ) }.pma.json` )
142
- const tickMs = parseInt( process.env.WILL_TICK_MS ?? '1000' )
141
+ const pmaPath = resolve( process.env.WILL_PMA_PATH ?? `.will/${ slug( name ) }.pma.json`)
142
+ const tickMs = parseInt( process.env.WILL_TICK_MS ?? '1000')
143
143
  const anatomy = ( process.env.WILL_ANATOMY as CreateWillOptions['anatomy'] ) ?? 'mind'
144
144
 
145
145
  await preflightLLM( anatomy )
@@ -153,23 +153,23 @@ export async function bootWillFromEnv(): Promise<BootedWill> {
153
153
 
154
154
  let will: Will
155
155
  if( existsSync( pmaPath ) ){
156
- const pma = JSON.parse( readFileSync( pmaPath, 'utf8' ) ) as PMASnapshot
156
+ const pma = JSON.parse( readFileSync( pmaPath, 'utf8') ) as PMASnapshot
157
157
  will = await Will.wake( pma, opts )
158
- console.error( `[will] ${ name } woke from ${ pmaPath }` )
158
+ console.error(`[will] ${ name } woke from ${ pmaPath }`)
159
159
  // A woken Will carries its own identity — that is the point of an artifact.
160
160
  // But an operator editing WILL_IDENTITY and seeing nothing change deserves
161
161
  // to know why, rather than concluding the persona layer is broken.
162
162
  if( process.env.WILL_IDENTITY )
163
- console.error( `[will] note: WILL_IDENTITY is ignored — ${ name } woke as itself. Delete ${ pmaPath } to be born fresh from it.` )
163
+ console.error(`[will] note: WILL_IDENTITY is ignored — ${ name } woke as itself. Delete ${ pmaPath } to be born fresh from it.`)
164
164
  }
165
165
  else {
166
166
  will = await Will.create( {
167
167
  ...opts,
168
168
  identity: { prompt: process.env.WILL_IDENTITY ?? `I am ${ name }, a persistent mind.` },
169
169
  } )
170
- console.error( `[will] ${ name } born (no artifact at ${ pmaPath } yet)` )
170
+ console.error(`[will] ${ name } born (no artifact at ${ pmaPath } yet)`)
171
171
  }
172
- will.on( 'error', e => console.error( `[will] error: ${ e.message }` ) )
172
+ will.on('error', e => console.error(`[will] error: ${ e.message }`) )
173
173
 
174
174
  // Onward bridges: MCP servers whose tools become the Will's OWN abilities.
175
175
  // Best-effort — a bad entry warns and is skipped; the mind still boots.
@@ -181,12 +181,12 @@ export async function bootWillFromEnv(): Promise<BootedWill> {
181
181
  try {
182
182
  const { names, close } = await connectMcpEffectors( will, source )
183
183
  cleanups.push( close )
184
- console.error( `[will] ${ name } gained abilities: ${ names.join( ', ' ) }` )
184
+ console.error(`[will] ${ name } gained abilities: ${ names.join(', ') }`)
185
185
  }
186
- catch( e ){ console.error( `[will] MCP bridge failed (skipped): ${ ( e as Error ).message }` ) }
186
+ catch( e ){ console.error(`[will] MCP bridge failed (skipped): ${ ( e as Error ).message }`) }
187
187
  }
188
188
  }
189
- catch( e ){ console.error( `[will] WILL_MCP_SERVERS is not valid JSON — ignoring: ${ ( e as Error ).message }` ) }
189
+ catch( e ){ console.error(`[will] WILL_MCP_SERVERS is not valid JSON — ignoring: ${ ( e as Error ).message }`) }
190
190
  }
191
191
 
192
192
  // Hibernate exactly once on the way out — cleanups (LIFO), distill + stop, persist.
@@ -199,13 +199,13 @@ export async function bootWillFromEnv(): Promise<BootedWill> {
199
199
  const pma = await will.hibernate()
200
200
  mkdirSync( dirname( pmaPath ), { recursive: true } )
201
201
  writeFileSync( pmaPath, JSON.stringify( pma ) )
202
- console.error( `[will] ${ name } hibernated to ${ pmaPath } (${ why })` )
202
+ console.error(`[will] ${ name } hibernated to ${ pmaPath } (${ why })`)
203
203
  }
204
- catch( e ){ console.error( `[will] hibernate failed: ${ ( e as Error ).message }` ) }
204
+ catch( e ){ console.error(`[will] hibernate failed: ${ ( e as Error ).message }`) }
205
205
  process.exit( 0 )
206
206
  }
207
- process.on( 'SIGINT', () => void shutdown( 'SIGINT' ) )
208
- process.on( 'SIGTERM', () => void shutdown( 'SIGTERM' ) )
207
+ process.on('SIGINT', () => void shutdown('SIGINT') )
208
+ process.on('SIGTERM', () => void shutdown('SIGTERM') )
209
209
 
210
210
  return {
211
211
  will, name, pmaPath, tickMs, anatomy,
@@ -20,7 +20,7 @@ export class UtteranceTap {
20
20
 
21
21
  constructor( will: Will ){
22
22
  this._will = will
23
- will.on( 'message', m => {
23
+ will.on('message', m => {
24
24
  this._pending.push( m )
25
25
  if( this._pending.length > BUFFER_CAP ) this._pending.shift()
26
26
  } )
package/src/llm/index.ts CHANGED
@@ -187,7 +187,7 @@ export class LLMDirector {
187
187
  * {"actions":[{"type":"...","reasoning":"...","expectedOutcome":"..."}],...}
188
188
  * Strategy 1 (JSON.parse) handles this directly.
189
189
  */
190
- private _mockResponse( tick: number, userMessage: string = '' ): LLMCallResult {
190
+ private _mockResponse( tick: number, userMessage: string = ''): LLMCallResult {
191
191
  // ── Conversation facet turn ────────────────────────────────
192
192
  // Detect the AuditionEngine facet focus via the SHARED contract (see
193
193
  // llm/wire.contracts.ts) — the render/match pair whose earlier drift
@@ -404,7 +404,7 @@ export class LLMDirector {
404
404
  catch( err ){
405
405
  clearTimeout( timer )
406
406
  if( controller.signal.aborted )
407
- throw new Error( `LLM stream to ${this._provider} timed out after ${this._timeoutMs}ms (no response)` )
407
+ throw new Error(`LLM stream to ${this._provider} timed out after ${this._timeoutMs}ms (no response)`)
408
408
  throw err
409
409
  }
410
410
 
@@ -434,7 +434,7 @@ export class LLMDirector {
434
434
  for( const line of lines ){
435
435
  if( !line.startsWith('data: ') ) continue
436
436
  const raw = line.slice(6).trim()
437
- if( raw === '[DONE]' ) break
437
+ if( raw === '[DONE]') break
438
438
 
439
439
  try {
440
440
  const ev = JSON.parse( raw ) as {
@@ -552,8 +552,8 @@ export class LLMDirector {
552
552
  }
553
553
  catch( err ){
554
554
  // AbortSignal.timeout rejects with a DOMException named 'TimeoutError'.
555
- if( err instanceof Error && err.name === 'TimeoutError' )
556
- throw new Error( `LLM request to ${this._provider} timed out after ${this._timeoutMs}ms` )
555
+ if( err instanceof Error && err.name === 'TimeoutError')
556
+ throw new Error(`LLM request to ${this._provider} timed out after ${this._timeoutMs}ms`)
557
557
  throw err
558
558
  }
559
559
  }
@@ -592,7 +592,7 @@ export class LLMDirector {
592
592
  content: Array<{ type: string; text: string }>
593
593
  usage: { input_tokens: number; output_tokens: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number }
594
594
  },
595
- text = data.content.find( b => b.type === 'text' )?.text ?? ''
595
+ text = data.content.find( b => b.type === 'text')?.text ?? ''
596
596
 
597
597
  return {
598
598
  text,
@@ -673,7 +673,7 @@ export class LLMDirector {
673
673
  }
674
674
 
675
675
  const text = ( data.candidates?.[ 0 ]?.content?.parts ?? [] )
676
- .map( p => p.text ?? '' )
676
+ .map( p => p.text ?? '')
677
677
  .join('')
678
678
 
679
679
  return {
@@ -715,7 +715,7 @@ export class LLMDirector {
715
715
  userMessage,
716
716
  ].join('\n')
717
717
 
718
- const filepath = `${debugDir}/prompt-tick-${String( tick ).padStart( 6, '0' )}.txt`
718
+ const filepath = `${debugDir}/prompt-tick-${String( tick ).padStart( 6, '0')}.txt`
719
719
  writeFileSync( filepath, content )
720
720
 
721
721
  logger.info(`[executive] Debug prompt written → ${filepath} (~${estimatedTokens} tok estimated)`)
@@ -755,7 +755,7 @@ export class LLMDirector {
755
755
  responseText,
756
756
  ].join('\n')
757
757
 
758
- const filepath = `${debugDir}/response-tick-${String( tick ).padStart( 6, '0' )}.txt`
758
+ const filepath = `${debugDir}/response-tick-${String( tick ).padStart( 6, '0')}.txt`
759
759
  writeFileSync( filepath, content )
760
760
  }
761
761
  catch {
@@ -130,7 +130,7 @@ export class ExecutiveSummarizer {
130
130
 
131
131
  private async _run(): Promise<void> {
132
132
  if( !this._llmDirector ){
133
- logger.warn( '[summarizer] skipping — LLMDirector not attached yet' )
133
+ logger.warn('[summarizer] skipping — LLMDirector not attached yet')
134
134
  return
135
135
  }
136
136
 
@@ -139,8 +139,8 @@ export class ExecutiveSummarizer {
139
139
 
140
140
  try {
141
141
  const userMessage = snapshot
142
- .map( ( r, i ) => `[Cycle ${i + 1}]\n${r}` )
143
- .join( '\n\n---\n\n' )
142
+ .map( ( r, i ) => `[Cycle ${i + 1}]\n${r}`)
143
+ .join('\n\n---\n\n')
144
144
 
145
145
  // Use _callCount as the tick value — it's only used for token tracking/logging
146
146
  const result = await this._llmDirector.call(
@@ -161,7 +161,7 @@ export class ExecutiveSummarizer {
161
161
  }
162
162
  }
163
163
  catch( err ){
164
- logger.warn( '[summarizer] failed:', err instanceof Error ? err.message : err )
164
+ logger.warn('[summarizer] failed:', err instanceof Error ? err.message : err )
165
165
  }
166
166
  finally {
167
167
  this._summarizing = false
@@ -36,7 +36,7 @@ export const REPLY_TEXT_CLOSE = `[/${REPLY_TEXT_TAG}]`
36
36
 
37
37
  /** Wrap a reply body in the block markers (the mock's emission shape). */
38
38
  export function wrapReplyText( body: string ): string {
39
- return [ REPLY_TEXT_OPEN, body, REPLY_TEXT_CLOSE ].join( '\n' )
39
+ return [ REPLY_TEXT_OPEN, body, REPLY_TEXT_CLOSE ].join('\n')
40
40
  }
41
41
 
42
42
  // ── Conversation-facet focus — render ↔ match pair ────────────
@@ -67,10 +67,10 @@ export function describeMcpTool( tool: McpToolInfo ): string {
67
67
  const props = tool.inputSchema?.properties ?? {}
68
68
  const required = new Set( tool.inputSchema?.required ?? [] )
69
69
  const argHints = Object.entries( props ).map( ( [ key, p ] ) =>
70
- `${ key }${ required.has( key ) ? '' : '?' }${ p.description ? `: ${ p.description }` : '' }` )
70
+ `${ key }${ required.has( key ) ? '' : '?' }${ p.description ? `: ${ p.description }` : '' }`)
71
71
 
72
- const base = ( tool.description ?? `The ${ tool.name } tool.` ).trim().replace( /\s+/g, ' ' )
73
- const hint = argHints.length > 0 ? ` (args — ${ argHints.join( '; ' ) })` : ''
72
+ const base = ( tool.description ?? `The ${ tool.name } tool.`).trim().replace( /\s+/g, ' ')
73
+ const hint = argHints.length > 0 ? ` (args — ${ argHints.join('; ') })` : ''
74
74
  const full = `${ base }${ hint }`
75
75
  return full.length > MEANING_CAP ? `${ full.slice( 0, MEANING_CAP - 1 ) }…` : full
76
76
  }
@@ -90,21 +90,21 @@ export function buildMcpHandler( client: Client, tool: McpToolInfo ): EffectorHa
90
90
  if( !props || k in props ) filtered[ k ] = v
91
91
 
92
92
  const missing = ( tool.inputSchema?.required ?? [] ).filter(
93
- k => filtered[ k ] === undefined || filtered[ k ] === '' )
93
+ k => filtered[ k ] === undefined || filtered[ k ] === '')
94
94
  if( missing.length > 0 )
95
95
  return {
96
96
  success: false,
97
- description: `${ tool.name } needs ${ missing.join( ', ' ) } — enact it deliberately, supplying them in the action's args.`,
97
+ description: `${ tool.name } needs ${ missing.join(', ') } — enact it deliberately, supplying them in the action's args.`,
98
98
  }
99
99
 
100
100
  try {
101
101
  const res = await client.callTool( { name: tool.name, arguments: filtered } ) as
102
102
  { content?: Array<{ type: string; text?: string }>; isError?: boolean }
103
103
  const text = ( res.content ?? [] )
104
- .filter( c => c.type === 'text' && typeof c.text === 'string' )
104
+ .filter( c => c.type === 'text' && typeof c.text === 'string')
105
105
  .map( c => c.text as string )
106
- .join( '\n' )
107
- .trim() || ( res.isError ? 'The tool reported an error.' : 'Done (no output).' )
106
+ .join('\n')
107
+ .trim() || ( res.isError ? 'The tool reported an error.' : 'Done (no output).')
108
108
  const bounded = text.length > RESULT_DESCRIPTION_CAP ? `${ text.slice( 0, RESULT_DESCRIPTION_CAP - 1 ) }…` : text
109
109
  return { success: !res.isError, description: bounded }
110
110
  }
@@ -115,10 +115,10 @@ export function buildMcpHandler( client: Client, tool: McpToolInfo ): EffectorHa
115
115
  }
116
116
 
117
117
  async function connect( source: McpToolsSource ): Promise<{ client: Client; owned: boolean }> {
118
- if( 'client' in source ) return { client: source.client, owned: false }
118
+ if('client' in source ) return { client: source.client, owned: false }
119
119
 
120
120
  const client = new Client( { name: 'mindot-will', version: '0' } )
121
- if( 'url' in source )
121
+ if('url' in source )
122
122
  await client.connect( new StreamableHTTPClientTransport( new URL( source.url ) ) )
123
123
  else
124
124
  await client.connect( new StdioClientTransport( {
package/src/mcp/server.ts CHANGED
@@ -35,7 +35,7 @@ export interface WillMcpOptions {
35
35
  function serverVersion(): string {
36
36
  // Same relative depth from src/mcp/ and dist/mcp/ — resolves in both.
37
37
  try {
38
- return JSON.parse( readFileSync( new URL( '../../package.json', import.meta.url ), 'utf8' ) ).version ?? '0.0.0'
38
+ return JSON.parse( readFileSync( new URL('../../package.json', import.meta.url ), 'utf8') ).version ?? '0.0.0'
39
39
  }
40
40
  catch { return '0.0.0' }
41
41
  }
@@ -54,7 +54,7 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
54
54
 
55
55
  // ── Tools ─────────────────────────────────────────────────
56
56
 
57
- server.registerTool( 'perceive', {
57
+ server.registerTool('perceive', {
58
58
  title: 'Perceive',
59
59
  description:
60
60
  `Deliver a stimulus (something said or observed) into ${ will.name }'s sensory field. ` +
@@ -62,9 +62,9 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
62
62
  `NOT when it has responded. Its response — if it chooses to give one — arrives as its next ` +
63
63
  `utterance (use the next_utterance tool). Staying silent is a valid choice, not an error.`,
64
64
  inputSchema: {
65
- text: z.string().describe( 'What is said or observed.' ),
65
+ text: z.string().describe('What is said or observed.'),
66
66
  from: z.string().optional().describe( "Who it's from (entity id, default 'user'). Use a stable id per person." ),
67
- speaker: z.string().optional().describe( 'Display name of the speaker.' ),
67
+ speaker: z.string().optional().describe('Display name of the speaker.'),
68
68
  },
69
69
  }, async ( { text, from, speaker } ) => {
70
70
  await will.perceive( { text, ...( from ? { from } : {} ), ...( speaker ? { speaker } : {} ) } )
@@ -77,15 +77,15 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
77
77
  }
78
78
  } )
79
79
 
80
- server.registerTool( 'next_utterance', {
80
+ server.registerTool('next_utterance', {
81
81
  title: 'Next utterance',
82
82
  description:
83
83
  `Await ${ will.name }'s next spontaneous utterance. Returns what it says next, or reports ` +
84
84
  `that it chose silence within the wait window — silence is a real outcome, not a failure. ` +
85
85
  `Call after perceive to hear the response, or on its own to listen for unprompted speech.`,
86
86
  inputSchema: {
87
- within_ms: z.number().optional().describe( 'How long to wait before accepting silence (default 15000, max 120000).' ),
88
- from: z.string().optional().describe( 'Only accept an utterance addressed to this entity id.' ),
87
+ within_ms: z.number().optional().describe('How long to wait before accepting silence (default 15000, max 120000).'),
88
+ from: z.string().optional().describe('Only accept an utterance addressed to this entity id.'),
89
89
  },
90
90
  }, async ( { within_ms, from } ) => {
91
91
  const within = Math.min( Math.max( within_ms ?? 15_000, 100 ), 120_000 )
@@ -101,7 +101,7 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
101
101
  }
102
102
  } )
103
103
 
104
- server.registerTool( 'state', {
104
+ server.registerTool('state', {
105
105
  title: 'Inner state',
106
106
  description:
107
107
  `Read a snapshot of ${ will.name }'s current inner life: tick, body/affect metrics ` +
@@ -112,7 +112,7 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
112
112
  { content: [ { type: 'text', text: JSON.stringify( will.state(), null, 2 ) } ] }
113
113
  ) )
114
114
 
115
- server.registerTool( 'save', {
115
+ server.registerTool('save', {
116
116
  title: 'Save (checkpoint)',
117
117
  description:
118
118
  `Checkpoint ${ will.name } into a portable PMA artifact on disk — non-destructive, it keeps ` +
@@ -130,12 +130,12 @@ export function buildWillMcpServer( will: Will, opts: WillMcpOptions = {} ): Mcp
130
130
 
131
131
  // ── Resources (read-only projections) ─────────────────────
132
132
 
133
- server.registerResource( 'state', 'will://state',
133
+ server.registerResource('state', 'will://state',
134
134
  { title: `${ will.name } — inner state`, description: 'Live snapshot of the mind: metrics, goals, beliefs, narrative.', mimeType: 'application/json' },
135
135
  async uri => ( { contents: [ { uri: uri.href, mimeType: 'application/json', text: JSON.stringify( will.state(), null, 2 ) } ] } ),
136
136
  )
137
137
 
138
- server.registerResource( 'narrative', 'will://narrative',
138
+ server.registerResource('narrative', 'will://narrative',
139
139
  { title: `${ will.name } — self-narrative`, description: 'The story the mind currently tells about itself.', mimeType: 'text/plain' },
140
140
  async uri => ( { contents: [ { uri: uri.href, mimeType: 'text/plain', text: will.state().narrative || '(no narrative formed yet)' } ] } ),
141
141
  )