@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.
Files changed (135) hide show
  1. package/README.md +32 -12
  2. package/dist/channels/discord.d.ts +2 -11
  3. package/dist/channels/discord.js.map +1 -1
  4. package/dist/channels/whatsapp.d.ts +72 -0
  5. package/dist/channels/whatsapp.js +252 -0
  6. package/dist/channels/whatsapp.js.map +1 -0
  7. package/dist/cli.js +837 -91
  8. package/dist/cli.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +620 -79
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp/effectors.d.ts +1 -1
  13. package/dist/mcp/effectors.js.map +1 -1
  14. package/dist/types-E9-HV-SW.d.ts +11 -0
  15. package/dist/{will-D-slky1N.d.ts → will-Bikuk4s2.d.ts} +56 -12
  16. package/package.json +12 -3
  17. package/src/channels/discord.ts +11 -11
  18. package/src/channels/roster.ts +1 -1
  19. package/src/channels/types.ts +1 -1
  20. package/src/channels/whatsapp.ts +318 -0
  21. package/src/cli.ts +46 -23
  22. package/src/cognition/acp.ts +28 -0
  23. package/src/cognition/agency/consequence.ts +174 -0
  24. package/src/cognition/agency/engines/action.selector.ts +301 -48
  25. package/src/cognition/agency/engines/affordance.synthesizer.ts +26 -16
  26. package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
  27. package/src/cognition/agency/engines/motor.schema.executor.ts +86 -14
  28. package/src/cognition/agency/engines/reafference.engine.ts +122 -18
  29. package/src/cognition/agency/execution.primitives.ts +13 -13
  30. package/src/cognition/agency/proactive.communicator.ts +1 -1
  31. package/src/cognition/agency/reconcile.learning.ts +1 -1
  32. package/src/cognition/agency/revocation.ts +79 -0
  33. package/src/cognition/agency/schemas/repertoire.ts +2 -2
  34. package/src/cognition/agency/selection.scoring.ts +25 -9
  35. package/src/cognition/bus.ts +2 -2
  36. package/src/cognition/completion.inbox.ts +1 -1
  37. package/src/cognition/config.mirror.entities.ts +5 -5
  38. package/src/cognition/event.log.ts +2 -2
  39. package/src/cognition/event.schemas.ts +67 -67
  40. package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
  41. package/src/cognition/faculties/affective.blender.ts +36 -4
  42. package/src/cognition/faculties/attachment.evaluator.ts +6 -6
  43. package/src/cognition/faculties/attention.allocator.ts +63 -8
  44. package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
  45. package/src/cognition/faculties/bias.detector.ts +7 -7
  46. package/src/cognition/faculties/circadian.oscillator.ts +3 -3
  47. package/src/cognition/faculties/confidence.calibrator.ts +7 -7
  48. package/src/cognition/faculties/dream.simulator.ts +5 -5
  49. package/src/cognition/faculties/empathy.simulator.ts +7 -7
  50. package/src/cognition/faculties/energy.regulator.ts +4 -4
  51. package/src/cognition/faculties/episodic.consolidator.ts +6 -6
  52. package/src/cognition/faculties/executive.engine/commands.ts +6 -6
  53. package/src/cognition/faculties/executive.engine/context.ts +42 -42
  54. package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
  55. package/src/cognition/faculties/executive.engine/engine.ts +27 -26
  56. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
  57. package/src/cognition/faculties/executive.engine/facet.ts +18 -18
  58. package/src/cognition/faculties/executive.engine/gating.ts +2 -2
  59. package/src/cognition/faculties/executive.engine/messages.ts +3 -3
  60. package/src/cognition/faculties/executive.engine/parser.ts +11 -11
  61. package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
  62. package/src/cognition/faculties/exteroception.ts +99 -5
  63. package/src/cognition/faculties/forgetting.curve.ts +3 -3
  64. package/src/cognition/faculties/frustration.evaluator.ts +6 -6
  65. package/src/cognition/faculties/goal.manager.ts +15 -15
  66. package/src/cognition/faculties/inhibition.controller.ts +3 -3
  67. package/src/cognition/faculties/interoception.ts +3 -3
  68. package/src/cognition/faculties/introspection.engine.ts +3 -3
  69. package/src/cognition/faculties/known.entity.tracker.ts +15 -15
  70. package/src/cognition/faculties/loss.evaluator.ts +2 -2
  71. package/src/cognition/faculties/moral.evaluator.ts +4 -4
  72. package/src/cognition/faculties/novelty.detector.ts +4 -4
  73. package/src/cognition/faculties/persona.consolidator.ts +12 -12
  74. package/src/cognition/faculties/planning.engine/engine.ts +47 -47
  75. package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
  76. package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
  77. package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
  78. package/src/cognition/faculties/reputation.tracker.ts +6 -6
  79. package/src/cognition/faculties/reward.evaluator.ts +4 -4
  80. package/src/cognition/faculties/self.model.updater.ts +8 -8
  81. package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
  82. package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
  83. package/src/cognition/faculties/social.perception.ts +3 -3
  84. package/src/cognition/faculties/spaced.repetition.ts +2 -2
  85. package/src/cognition/faculties/stress.regulator.ts +27 -4
  86. package/src/cognition/faculties/task.switcher.ts +11 -6
  87. package/src/cognition/faculties/theory.of.mind.ts +6 -6
  88. package/src/cognition/faculties/threat.evaluator.ts +3 -3
  89. package/src/cognition/faculties/working.memory.ts +8 -8
  90. package/src/cognition/generative.model.ts +2 -2
  91. package/src/cognition/memory/vector.adapter.ts +5 -5
  92. package/src/cognition/memory/vector.content.ts +4 -4
  93. package/src/cognition/memory/vector.embedder.ts +9 -9
  94. package/src/cognition/persona.prior.ts +1 -1
  95. package/src/cognition/schema.registry.ts +2 -2
  96. package/src/cognition/senses/audition.engine/engine.ts +2 -2
  97. package/src/cognition/senses/base.sense.engine.ts +1 -1
  98. package/src/cognition/utilities/token.tracker.ts +11 -5
  99. package/src/core/abstracts.ts +1 -1
  100. package/src/core/completion.recorder.ts +2 -2
  101. package/src/core/serialization.ts +3 -3
  102. package/src/core/snapshot.manager.ts +1 -1
  103. package/src/core/state.manager.ts +1 -1
  104. package/src/core/utils.ts +1 -1
  105. package/src/host/boot.ts +59 -37
  106. package/src/host/utterances.ts +1 -1
  107. package/src/llm/index.ts +84 -34
  108. package/src/llm/summarizer.ts +4 -4
  109. package/src/llm/wire.contracts.ts +1 -1
  110. package/src/mcp/effectors.ts +10 -10
  111. package/src/mcp/server.ts +11 -11
  112. package/src/pma/eval.ts +1 -1
  113. package/src/pma/index.ts +21 -21
  114. package/src/runners/outreach.runner.ts +3 -3
  115. package/src/runners/social.runner.ts +1 -1
  116. package/src/runners/thin-shim.runner.ts +1 -1
  117. package/src/sdk/will.ts +20 -11
  118. package/src/serve/server.ts +12 -12
  119. package/src/stem/assembly.audit.ts +3 -3
  120. package/src/stem/guards/identity.coherence.ts +13 -12
  121. package/src/stem/guards/identity.guard.ts +22 -22
  122. package/src/stem/index.ts +11 -11
  123. package/src/stem/mind.ts +12 -12
  124. package/src/stem/tracts/biography.writer.ts +3 -3
  125. package/src/stem/tracts/effector.controller.ts +4 -4
  126. package/src/stem/tracts/health.reporter.ts +1 -1
  127. package/src/stem/tracts/outbox.controller.ts +4 -0
  128. package/src/stem/tracts/outbox.writer.ts +4 -4
  129. package/src/stem/tracts/pma.controller.ts +1 -1
  130. package/src/stem/tracts/replay.controller.ts +10 -10
  131. package/src/stem/tracts/sensory.controller.ts +2 -2
  132. package/src/stem/tracts/session.logger.ts +4 -4
  133. package/src/stem/tracts/transport/socketio.transport.ts +7 -7
  134. package/src/stem/tracts/transport/stream.transport.ts +2 -2
  135. package/src/stem/tracts/transport.controller.ts +15 -15
package/src/llm/index.ts CHANGED
@@ -12,7 +12,68 @@ import type { LLMCompletionRecord } from '#core/completion.recorder'
12
12
  import { withGate } from '#llm/gate'
13
13
  import { matchConversationFocus, wrapReplyText } from '#llm/wire.contracts'
14
14
 
15
- export type LLMProvider = 'anthropic' | 'deepseek' | 'openai' | 'google'
15
+ export type LLMProvider = 'anthropic' | 'glm' | 'deepseek' | 'openai' | 'google'
16
+
17
+ /**
18
+ * Providers that speak the Anthropic Messages wire.
19
+ *
20
+ * Z.ai ships a real Anthropic-compatible endpoint for GLM — it is what Claude
21
+ * Code itself targets — so GLM rides this path rather than the OpenAI scaffold.
22
+ * That buys it everything the path already has: token streaming, the first-byte
23
+ * deadline, prompt-cache breakpoints, and the structured-output contract. GLM is
24
+ * therefore a second *production* provider, not a fifth scaffold.
25
+ */
26
+ const ANTHROPIC_WIRE = new Set<LLMProvider>( [ 'anthropic', 'glm' ] )
27
+
28
+ /** Does this provider accept Anthropic-shaped requests? */
29
+ export function speaksAnthropicWire( provider: LLMProvider ): boolean {
30
+ return ANTHROPIC_WIRE.has( provider )
31
+ }
32
+
33
+ /** Official API base URL (including version segment) for a provider. */
34
+ export function defaultBaseFor( provider: LLMProvider ): string {
35
+ switch( provider ){
36
+ case 'anthropic': return 'https://api.anthropic.com/v1'
37
+ // Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
38
+ // appends `/v1/messages`; this client appends `/messages`, so the version
39
+ // segment belongs here — verified against the live endpoint.
40
+ case 'glm': return 'https://api.z.ai/api/anthropic/v1'
41
+ case 'openai': return 'https://api.openai.com/v1'
42
+ case 'deepseek': return 'https://api.deepseek.com/v1'
43
+ case 'google': return 'https://generativelanguage.googleapis.com/v1beta'
44
+ }
45
+ }
46
+
47
+ /**
48
+ * The model the executive recruits when none is pinned.
49
+ *
50
+ * Provider-specific because the default is *sent* — a GLM Will with no
51
+ * `WILL_LLM_MODEL` would otherwise ask Z.ai for a Claude id and get a 404 it
52
+ * could do nothing with. The scaffolded providers (openai/deepseek/google) keep
53
+ * today's value: they need an explicit `WILL_LLM_MODEL` to work at all, and
54
+ * inventing ids for them here would look like support that does not exist.
55
+ */
56
+ export function defaultModelFor( provider: LLMProvider ): string {
57
+ return provider === 'glm' ? 'glm-5.2' : 'claude-sonnet-4-5-20250929'
58
+ }
59
+
60
+ /**
61
+ * Auth + version headers for the Anthropic wire.
62
+ *
63
+ * Anthropic authenticates with `x-api-key`. Z.ai's compat endpoint accepts
64
+ * either that or the `Authorization: Bearer` its own docs describe (both were
65
+ * probed against the live endpoint; each is read and validated). GLM sends both
66
+ * — same secret, same host — so the mind keeps working whichever one Z.ai
67
+ * eventually settles on.
68
+ */
69
+ export function anthropicWireHeaders( provider: LLMProvider, apiKey: string ): Record<string, string> {
70
+ return {
71
+ 'Content-Type': 'application/json',
72
+ 'anthropic-version': '2023-06-01',
73
+ 'x-api-key': apiKey,
74
+ ...( provider === 'glm' ? { Authorization: `Bearer ${ apiKey }` } : {} ),
75
+ }
76
+ }
16
77
  export interface LLMDirectorConfig {
17
78
  willId: string
18
79
  model: string
@@ -126,7 +187,7 @@ export class LLMDirector {
126
187
  * {"actions":[{"type":"...","reasoning":"...","expectedOutcome":"..."}],...}
127
188
  * Strategy 1 (JSON.parse) handles this directly.
128
189
  */
129
- private _mockResponse( tick: number, userMessage: string = '' ): LLMCallResult {
190
+ private _mockResponse( tick: number, userMessage: string = ''): LLMCallResult {
130
191
  // ── Conversation facet turn ────────────────────────────────
131
192
  // Detect the AuditionEngine facet focus via the SHARED contract (see
132
193
  // llm/wire.contracts.ts) — the render/match pair whose earlier drift
@@ -216,7 +277,7 @@ export class LLMDirector {
216
277
  return result
217
278
  }
218
279
 
219
- const result = this._provider === 'anthropic'
280
+ const result = speaksAnthropicWire( this._provider )
220
281
  ? await this._callAnthropicStream( systemPrompt, userMessage, onChunk, temperature )
221
282
  : await ( async () => {
222
283
  // Other providers: fall back to regular call, emit whole response as one chunk
@@ -328,11 +389,7 @@ export class LLMDirector {
328
389
  try {
329
390
  res = await fetch(`${this._resolvedBase()}/messages`, {
330
391
  method: 'POST',
331
- headers: {
332
- 'Content-Type': 'application/json',
333
- 'anthropic-version': '2023-06-01',
334
- 'x-api-key': this._apiKey,
335
- },
392
+ headers: anthropicWireHeaders( this._provider, this._apiKey ),
336
393
  body: JSON.stringify({
337
394
  model: this._model,
338
395
  max_tokens: this._maxOutputTokens,
@@ -347,7 +404,7 @@ export class LLMDirector {
347
404
  catch( err ){
348
405
  clearTimeout( timer )
349
406
  if( controller.signal.aborted )
350
- 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)`)
351
408
  throw err
352
409
  }
353
410
 
@@ -377,7 +434,7 @@ export class LLMDirector {
377
434
  for( const line of lines ){
378
435
  if( !line.startsWith('data: ') ) continue
379
436
  const raw = line.slice(6).trim()
380
- if( raw === '[DONE]' ) break
437
+ if( raw === '[DONE]') break
381
438
 
382
439
  try {
383
440
  const ev = JSON.parse( raw ) as {
@@ -438,13 +495,14 @@ export class LLMDirector {
438
495
  return result
439
496
  }
440
497
 
441
- // Anthropic routes through the streaming path so the deadline is first-byte
442
- // (TTFT), not whole-request: a long-but-healthy executive completion (often
443
- // 20–40s on Sonnet) no longer trips the timeout mid-generation. onChunk is a
444
- // no-op here — call() returns the full accumulated text; live token chunks go
445
- // through callStream(). Other providers keep the whole-request deadline.
498
+ // The Anthropic-wire providers route through the streaming path so the
499
+ // deadline is first-byte (TTFT), not whole-request: a long-but-healthy
500
+ // executive completion (often 20–40s on Sonnet) no longer trips the timeout
501
+ // mid-generation. onChunk is a no-op here — call() returns the full
502
+ // accumulated text; live token chunks go through callStream(). Other
503
+ // providers keep the whole-request deadline.
446
504
  const result = await withGate(
447
- () => this._provider === 'anthropic'
505
+ () => speaksAnthropicWire( this._provider )
448
506
  ? this._callAnthropicStream( systemPrompt, userMessage, () => {}, temperature )
449
507
  : this._callProvider( systemPrompt, userMessage, temperature ),
450
508
  'executive/direct',
@@ -465,6 +523,7 @@ export class LLMDirector {
465
523
  ): Promise<LLMCallResult> {
466
524
  switch( this._provider ){
467
525
  case 'anthropic': return this._callAnthropic( systemPrompt, userMessage, temperature )
526
+ case 'glm': return this._callAnthropic( systemPrompt, userMessage, temperature )
468
527
  case 'deepseek': return this._callOpenAI( systemPrompt, userMessage, temperature )
469
528
  case 'openai': return this._callOpenAI( systemPrompt, userMessage, temperature )
470
529
  case 'google': return this._callGoogle( systemPrompt, userMessage, temperature )
@@ -474,12 +533,7 @@ export class LLMDirector {
474
533
 
475
534
  /** Default API base URL (including version segment) for a provider. */
476
535
  private _baseFor( provider: LLMProvider ): string {
477
- switch( provider ){
478
- case 'anthropic': return 'https://api.anthropic.com/v1'
479
- case 'openai': return 'https://api.openai.com/v1'
480
- case 'deepseek': return 'https://api.deepseek.com/v1'
481
- case 'google': return 'https://generativelanguage.googleapis.com/v1beta'
482
- }
536
+ return defaultBaseFor( provider )
483
537
  }
484
538
 
485
539
  /** Resolved API base: explicit override wins, else the provider default. */
@@ -498,8 +552,8 @@ export class LLMDirector {
498
552
  }
499
553
  catch( err ){
500
554
  // AbortSignal.timeout rejects with a DOMException named 'TimeoutError'.
501
- if( err instanceof Error && err.name === 'TimeoutError' )
502
- 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`)
503
557
  throw err
504
558
  }
505
559
  }
@@ -526,23 +580,19 @@ export class LLMDirector {
526
580
 
527
581
  const res = await this._fetchWithTimeout(`${this._resolvedBase()}/messages`, {
528
582
  method: 'POST',
529
- headers: {
530
- 'Content-Type': 'application/json',
531
- 'anthropic-version': '2023-06-01',
532
- 'x-api-key': this._apiKey
533
- },
583
+ headers: anthropicWireHeaders( this._provider, this._apiKey ),
534
584
  body: JSON.stringify( body )
535
585
  })
536
586
 
537
587
  if( !res.ok )
538
- throw new Error(`Anthropic API ${res.status}: ${( await res.text() ).slice(0, 300)}`)
588
+ throw new Error(`${this._provider} API ${res.status}: ${( await res.text() ).slice(0, 300)}`)
539
589
 
540
590
  const
541
591
  data = await res.json() as {
542
592
  content: Array<{ type: string; text: string }>
543
593
  usage: { input_tokens: number; output_tokens: number; cache_read_input_tokens?: number; cache_creation_input_tokens?: number }
544
594
  },
545
- text = data.content.find( b => b.type === 'text' )?.text ?? ''
595
+ text = data.content.find( b => b.type === 'text')?.text ?? ''
546
596
 
547
597
  return {
548
598
  text,
@@ -623,7 +673,7 @@ export class LLMDirector {
623
673
  }
624
674
 
625
675
  const text = ( data.candidates?.[ 0 ]?.content?.parts ?? [] )
626
- .map( p => p.text ?? '' )
676
+ .map( p => p.text ?? '')
627
677
  .join('')
628
678
 
629
679
  return {
@@ -665,7 +715,7 @@ export class LLMDirector {
665
715
  userMessage,
666
716
  ].join('\n')
667
717
 
668
- const filepath = `${debugDir}/prompt-tick-${String( tick ).padStart( 6, '0' )}.txt`
718
+ const filepath = `${debugDir}/prompt-tick-${String( tick ).padStart( 6, '0')}.txt`
669
719
  writeFileSync( filepath, content )
670
720
 
671
721
  logger.info(`[executive] Debug prompt written → ${filepath} (~${estimatedTokens} tok estimated)`)
@@ -705,7 +755,7 @@ export class LLMDirector {
705
755
  responseText,
706
756
  ].join('\n')
707
757
 
708
- const filepath = `${debugDir}/response-tick-${String( tick ).padStart( 6, '0' )}.txt`
758
+ const filepath = `${debugDir}/response-tick-${String( tick ).padStart( 6, '0')}.txt`
709
759
  writeFileSync( filepath, content )
710
760
  }
711
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
  )
package/src/pma/eval.ts CHANGED
@@ -368,7 +368,7 @@ export class PMAEvalHarness {
368
368
  let loadedTraits: Record<string, number> = {}
369
369
 
370
370
  for( const entity of state.entities.values() ){
371
- if( entity.type === 'will.identity' ){
371
+ if( entity.type === 'will.identity'){
372
372
  loadedTraits = ( entity.metadata?.['traits'] as Record<string, number> ) ?? {}
373
373
  break
374
374
  }
package/src/pma/index.ts CHANGED
@@ -345,7 +345,7 @@ export class PMADistiller {
345
345
  */
346
346
  private _extractPersona( state: SimulationState ): PMASnapshot['persona'] {
347
347
  const priorMeta = state.entities.get( PERSONA_PRIOR_ID )?.metadata as { priors?: Record<string, Record<string, number>> } | undefined
348
- const calibMeta = state.entities.get( 'calibration-state' )?.metadata as { domainBias?: Record<string, number> } | undefined
348
+ const calibMeta = state.entities.get('calibration-state')?.metadata as { domainBias?: Record<string, number> } | undefined
349
349
 
350
350
  const configPriors = priorMeta?.priors ?? {}
351
351
  const calibrationBias = calibMeta?.domainBias ?? {}
@@ -358,7 +358,7 @@ export class PMADistiller {
358
358
 
359
359
  private _extractIdentity( state: SimulationState ): PMAIdentity {
360
360
  for( const entity of state.entities.values() ){
361
- if( entity.type === 'will.identity' ){
361
+ if( entity.type === 'will.identity'){
362
362
  const m = entity.metadata ?? {}
363
363
  return {
364
364
  prompt: ( m['prompt'] as string ) ?? '',
@@ -390,9 +390,9 @@ export class PMADistiller {
390
390
  // If socialOrientation not explicitly set, infer from top actions
391
391
  if( !enhanced.socialOrientation && behavioral.fingerprint.topActions.length > 0 ){
392
392
  const topAction = behavioral.fingerprint.topActions[0] ?? ''
393
- if( topAction.includes( 'social' ) || topAction.includes( 'talk' ) || topAction.includes( 'text' ) ){
393
+ if( topAction.includes('social') || topAction.includes('talk') || topAction.includes('text') ){
394
394
  enhanced.socialOrientation = 'gregarious'
395
- } else if( topAction.includes( 'reflect' ) || topAction.includes( 'meditate' ) ){
395
+ } else if( topAction.includes('reflect') || topAction.includes('meditate') ){
396
396
  enhanced.socialOrientation = 'reserved'
397
397
  } else {
398
398
  enhanced.socialOrientation = 'ambivert'
@@ -418,7 +418,7 @@ export class PMADistiller {
418
418
  const raw: PMABelief[] = []
419
419
 
420
420
  for( const entity of state.entities.values() ){
421
- if( entity.type !== 'belief' ) continue
421
+ if( entity.type !== 'belief') continue
422
422
 
423
423
  const m = entity.metadata ?? {}
424
424
  raw.push({
@@ -446,11 +446,11 @@ export class PMADistiller {
446
446
  const goals: PMAGoal[] = []
447
447
 
448
448
  for( const entity of state.entities.values() ){
449
- if( entity.type !== 'goal' ) continue
449
+ if( entity.type !== 'goal') continue
450
450
 
451
451
  const m = entity.metadata ?? {}
452
452
  const status = ( m['status'] as string ) ?? 'active'
453
- if( status !== 'active' && status !== 'in_progress' ) continue
453
+ if( status !== 'active' && status !== 'in_progress') continue
454
454
 
455
455
  goals.push({
456
456
  id: entity.id,
@@ -459,7 +459,7 @@ export class PMADistiller {
459
459
  progress: ( m['progress'] as number ) ?? 0,
460
460
  status,
461
461
  tags: ( m['tags'] as string[] ) ?? [],
462
- completionType: ( m['completionType'] as 'metric' | 'action' | 'epistemic' ) ?? 'epistemic',
462
+ completionType: ( m['completionType'] as 'metric' | 'action' | 'epistemic') ?? 'epistemic',
463
463
  completionCondition: ( m['completionCondition'] as string ) ?? undefined,
464
464
  })
465
465
  }
@@ -475,7 +475,7 @@ export class PMADistiller {
475
475
  const digestTick = new Map<string, number>()
476
476
 
477
477
  for( const entity of state.entities.values() ){
478
- if( entity.type === 'attachment.bond' ){
478
+ if( entity.type === 'attachment.bond'){
479
479
  const m = entity.metadata ?? {}
480
480
  const keid = m['keid'] as string | undefined
481
481
  if( !keid ) continue
@@ -492,7 +492,7 @@ export class PMADistiller {
492
492
  stubs.set( keid, stub )
493
493
  }
494
494
 
495
- else if( entity.type === 'reputation' ){
495
+ else if( entity.type === 'reputation'){
496
496
  const m = entity.metadata ?? {}
497
497
  const keid = m['keid'] as string | undefined
498
498
  if( !keid ) continue
@@ -512,7 +512,7 @@ export class PMADistiller {
512
512
  stubs.set( keid, stub )
513
513
  }
514
514
 
515
- else if( entity.type === 'theory_of_mind' ){
515
+ else if( entity.type === 'theory_of_mind'){
516
516
  const m = entity.metadata ?? {}
517
517
  const keid = m['keid'] as string | undefined
518
518
  if( !keid ) continue
@@ -526,15 +526,15 @@ export class PMADistiller {
526
526
  stubs.set( keid, stub )
527
527
  }
528
528
 
529
- else if( entity.type === 'known-entity' ){
529
+ else if( entity.type === 'known-entity'){
530
530
  const m = entity.metadata ?? {}
531
531
  const keid = m['keid'] as string | undefined
532
532
  if( !keid ) continue
533
533
 
534
534
  const stub = stubs.get( keid ) ?? { keid }
535
- if( !stub.agentName && typeof m['name'] === 'string' ) stub.agentName = m['name'] as string
535
+ if( !stub.agentName && typeof m['name'] === 'string') stub.agentName = m['name'] as string
536
536
  stub.dossier = {
537
- kind: ( m['kind'] as 'sentient' | 'thing' ) ?? 'sentient',
537
+ kind: ( m['kind'] as 'sentient' | 'thing') ?? 'sentient',
538
538
  name: ( m['name'] as string | undefined ),
539
539
  familiarity: ( m['familiarity'] as number ) ?? 0,
540
540
  valence: ( m['valence'] as number ) ?? 0,
@@ -550,7 +550,7 @@ export class PMADistiller {
550
550
  // AuditionEngine memory sink + ProactiveCommunicator). The most recent
551
551
  // exchange summary wins.
552
552
  else if( entity.type === 'episodic_memory'
553
- && entity.metadata?.['sourceType'] === 'conversation.exchange' ){
553
+ && entity.metadata?.['sourceType'] === 'conversation.exchange'){
554
554
  const content = ( entity.metadata['content'] as Record<string, unknown> | undefined ) ?? {}
555
555
  const keid = content['entityId'] as string | undefined
556
556
  if( !keid ) continue
@@ -599,7 +599,7 @@ export class PMADistiller {
599
599
  reactivity: 0.5,
600
600
  }
601
601
 
602
- const filePath = join( dataDir, 'wills', willId, 'profiles', 'emotional_biography.jsonl' )
602
+ const filePath = join( dataDir, 'wills', willId, 'profiles', 'emotional_biography.jsonl')
603
603
  if( !existsSync( filePath ) ) return { baseline: defaultBaseline, sessionsRead: 0 }
604
604
 
605
605
  const summaries = _readLastNSummaries( filePath, 'session_summary', 5 )
@@ -673,7 +673,7 @@ export class PMADistiller {
673
673
  impulsivity: 0.3,
674
674
  }
675
675
 
676
- const filePath = join( dataDir, 'wills', willId, 'profiles', 'behavioral.jsonl' )
676
+ const filePath = join( dataDir, 'wills', willId, 'profiles', 'behavioral.jsonl')
677
677
  if( !existsSync( filePath ) ) return { fingerprint: defaultFingerprint, sessionsRead: 0 }
678
678
 
679
679
  const summaries = _readLastNSummaries( filePath, 'session_summary', 5 )
@@ -860,7 +860,7 @@ export class PMALoader {
860
860
 
861
861
  // ── 3. Goals ──────────────────────────────────────────────
862
862
  for( const g of pma.goals ){
863
- if( g.status !== 'active' && g.status !== 'in_progress' ) continue
863
+ if( g.status !== 'active' && g.status !== 'in_progress') continue
864
864
 
865
865
  cognition.goalManager.addGoal(
866
866
  g.description,
@@ -882,8 +882,8 @@ export class PMALoader {
882
882
  pma.emotionalBaseline.arousalProfile === 'calm' ? 0.30 :
883
883
  0.45
884
884
 
885
- sm.setMetric( 'affect.valence', valence )
886
- sm.setMetric( 'affect.arousal', arousal )
885
+ sm.setMetric('affect.valence', valence )
886
+ sm.setMetric('affect.arousal', arousal )
887
887
 
888
888
  // ── 5. Temperament (influences emotional set-point) ───────
889
889
  const temperamentValence = pma.emotionalBaseline.temperamentValence
@@ -1068,7 +1068,7 @@ function _readLastNSummaries(
1068
1068
  n: number
1069
1069
  ): Array<Record<string, unknown>> {
1070
1070
  try {
1071
- return readFileSync( filePath, 'utf8' )
1071
+ return readFileSync( filePath, 'utf8')
1072
1072
  .split('\n')
1073
1073
  .filter( l => l.trim().length > 0 )
1074
1074
  .map( l => { try { return JSON.parse(l) as Record<string, unknown> } catch { return null } })
@@ -111,8 +111,8 @@ async function main(): Promise<void> {
111
111
  lastBlocked = g('agency.communicate.blocked')
112
112
 
113
113
  if( tick % 10 === 0 ){
114
- const goals = [ ...snap.entities.values() ].filter( e => e.type === 'goal' && e.metadata?.status === 'active' ).length
115
- const sent = [ ...snap.entities.values() ].filter( e => e.type === 'conversation.sent' ).length
114
+ const goals = [ ...snap.entities.values() ].filter( e => e.type === 'goal' && e.metadata?.status === 'active').length
115
+ const sent = [ ...snap.entities.values() ].filter( e => e.type === 'conversation.sent').length
116
116
  console.log(
117
117
  `[t${tick}] v=${g('affect.valence').toFixed(2)} arousal=${g('affect.arousal').toFixed(2)} ` +
118
118
  `frust=${g('emotion.frustration').toFixed(2)} energy=${g('energy.level').toFixed(0)} ` +
@@ -130,7 +130,7 @@ async function main(): Promise<void> {
130
130
  const startedAt = Date.now()
131
131
  while( true ){
132
132
  const s = m.listWills().find( w => w.id === WILL_ID )
133
- if( !s || s.status === 'archived' ) break
133
+ if( !s || s.status === 'archived') break
134
134
  if( s.tickCount >= END_TICK ) break
135
135
  if( sawReactive && sawProactive ){ console.log('\n[scenario] both paths observed — wrapping up'); break }
136
136
  if( Date.now() - startedAt > SAFETY_MS ){ console.log('\n[scenario] safety cap hit'); break }
@@ -134,7 +134,7 @@ async function main(): Promise<void> {
134
134
 
135
135
  // Beliefs the Will formed ABOUT Dr. Chen (social cognition evidence)
136
136
  const chenBeliefs = ( ( pma as { beliefs?: Array<{ statement?: string; content?: string }> } ).beliefs ?? [] )
137
- .map( b => b.statement ?? b.content ?? '' )
137
+ .map( b => b.statement ?? b.content ?? '')
138
138
  .filter( s => /chen|she|her|researcher|observ/i.test( s ) )
139
139
  .slice( 0, 6 )
140
140
  if( chenBeliefs.length ){
@@ -26,7 +26,7 @@ const TICK_MS = parseInt( process.env.WILL_TICK_MS ?? '1000')
26
26
  const MAX_TICKS = parseInt( process.env.WILL_MAX_TICKS ?? '0' )
27
27
  const LOG_INTERVAL = parseInt( process.env.WILL_LOG_INTERVAL ?? '10' )
28
28
  const RANDOM_SEED = parseInt( process.env.WILL_SEED ?? String( Date.now() ) )
29
- const ANATOMY = ( process.env.WILL_ANATOMY ?? 'mind' ) as WillConfig['anatomy']
29
+ const ANATOMY = ( process.env.WILL_ANATOMY ?? 'mind') as WillConfig['anatomy']
30
30
  const EXECUTIVE_INT = process.env.WILL_EXECUTIVE_INTERVAL
31
31
  ? parseInt( process.env.WILL_EXECUTIVE_INTERVAL )
32
32
  : undefined