@mindot/will 0.7.0 → 0.9.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 (88) hide show
  1. package/README.md +87 -22
  2. package/dist/channels/discord.d.ts +67 -6
  3. package/dist/channels/discord.js +112 -6
  4. package/dist/channels/discord.js.map +1 -1
  5. package/dist/channels/whatsapp.d.ts +1 -1
  6. package/dist/channels/whatsapp.js +4 -1
  7. package/dist/channels/whatsapp.js.map +1 -1
  8. package/dist/cli.js +13733 -11057
  9. package/dist/cli.js.map +1 -1
  10. package/dist/index.d.ts +2 -2
  11. package/dist/index.js +3729 -1199
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp/effectors.d.ts +1 -1
  14. package/dist/{will-DAW0l-lY.d.ts → will-DbDj_TEH.d.ts} +1214 -93
  15. package/package.json +1 -1
  16. package/src/channels/discord.ts +189 -11
  17. package/src/channels/types.ts +90 -0
  18. package/src/channels/whatsapp.ts +13 -4
  19. package/src/cli.ts +9 -4
  20. package/src/cognition/agency/consequence.ts +122 -1
  21. package/src/cognition/agency/conversation.aim.ts +260 -0
  22. package/src/cognition/agency/engines/action.selector.ts +85 -3
  23. package/src/cognition/agency/engines/affordance.synthesizer.ts +90 -1
  24. package/src/cognition/agency/engines/motor.schema.executor.ts +152 -10
  25. package/src/cognition/agency/engines/reafference.engine.ts +129 -2
  26. package/src/cognition/agency/proactive.communicator.ts +19 -3
  27. package/src/cognition/agency/reconcile.learning.ts +16 -2
  28. package/src/cognition/agency/restart.ts +66 -0
  29. package/src/cognition/agency/schemas/repertoire.ts +12 -5
  30. package/src/cognition/agency/selection.scoring.ts +33 -0
  31. package/src/cognition/agency/types.ts +35 -0
  32. package/src/cognition/cache/composition.ts +232 -0
  33. package/src/cognition/cache/deliberation.cache.ts +219 -0
  34. package/src/cognition/cache/fingerprint.ts +120 -0
  35. package/src/cognition/cache/types.ts +105 -0
  36. package/src/cognition/config.mirror.entities.ts +109 -1
  37. package/src/cognition/event.schemas.ts +22 -0
  38. package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
  39. package/src/cognition/faculties/episodic.consolidator.ts +59 -3
  40. package/src/cognition/faculties/executive.engine/commands.ts +189 -14
  41. package/src/cognition/faculties/executive.engine/context.ts +67 -13
  42. package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
  43. package/src/cognition/faculties/executive.engine/engine.ts +686 -187
  44. package/src/cognition/faculties/executive.engine/escalation.buffer.ts +162 -44
  45. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +310 -65
  46. package/src/cognition/faculties/executive.engine/facet.ts +89 -26
  47. package/src/cognition/faculties/executive.engine/gating.ts +14 -14
  48. package/src/cognition/faculties/executive.engine/parser.ts +21 -1
  49. package/src/cognition/faculties/executive.engine/prompt.factory.ts +169 -20
  50. package/src/cognition/faculties/executive.engine/types.ts +69 -0
  51. package/src/cognition/faculties/goal.manager.ts +94 -14
  52. package/src/cognition/faculties/known.entity.tracker.ts +267 -28
  53. package/src/cognition/faculties/moral.evaluator.ts +8 -3
  54. package/src/cognition/faculties/persona.consolidator.ts +141 -0
  55. package/src/cognition/faculties/reputation.tracker.ts +66 -2
  56. package/src/cognition/faculties/self.model.updater.ts +19 -12
  57. package/src/cognition/faculties/social.perception.ts +47 -3
  58. package/src/cognition/faculties/threat.evaluator.ts +7 -0
  59. package/src/cognition/faculties/working.memory.ts +10 -20
  60. package/src/cognition/identity.entity.ts +205 -0
  61. package/src/cognition/index.ts +11 -0
  62. package/src/cognition/memory/vector.adapter.ts +12 -3
  63. package/src/cognition/memory/vector.embedder.ts +54 -7
  64. package/src/cognition/persona.prior.ts +6 -0
  65. package/src/cognition/senses/audition.engine/engine.ts +404 -46
  66. package/src/cognition/senses/base.sense.engine.ts +1 -1
  67. package/src/cognition/senses/index.ts +12 -0
  68. package/src/cognition/social.identity.ts +273 -0
  69. package/src/cognition/utilities/token.tracker.ts +246 -98
  70. package/src/core/orchestrator.ts +38 -0
  71. package/src/host/boot.ts +78 -22
  72. package/src/index.ts +35 -0
  73. package/src/llm/index.ts +415 -97
  74. package/src/llm/routing.ts +204 -0
  75. package/src/llm/summarizer.ts +5 -1
  76. package/src/llm/wire.contracts.ts +19 -0
  77. package/src/pma/index.ts +67 -53
  78. package/src/runners/thin-shim.runner.ts +18 -6
  79. package/src/sdk/will.ts +121 -22
  80. package/src/stem/assembly.audit.ts +1 -0
  81. package/src/stem/guards/identity.coherence.ts +17 -6
  82. package/src/stem/index.ts +82 -5
  83. package/src/stem/mind.ts +327 -79
  84. package/src/stem/policy/arbiter.ts +49 -14
  85. package/src/stem/policy/rule.table.ts +2 -2
  86. package/src/stem/tracts/effector.controller.ts +56 -9
  87. package/src/stem/tracts/outbox.writer.ts +40 -2
  88. package/src/cognition/faculties/executive.engine/messages.ts +0 -102
@@ -928,6 +928,22 @@ declare class DefaultOrchestrator implements Orchestrator {
928
928
  * calls _executeTick() which handles pause/stop checks internally.
929
929
  * The orchestrator is the sole driver of ticks.
930
930
  */
931
+ /**
932
+ * A plain SimulationEngine's `react()` is implicitly "finish inside the tick" —
933
+ * only an AsyncEngine is allowed to span ticks, and it does so by LAUNCHING work
934
+ * and landing it later (see async.engine.ts: "react() never awaits LLM calls").
935
+ * Nothing enforces that on everyone else, and the failure is silent and severe:
936
+ * every agency deadline is denominated in TICKS, so an engine that awaits network
937
+ * I/O does not merely run slowly, it rescales time for the whole mind.
938
+ *
939
+ * Measured: one rate-limited embedding call awaited inside EpisodicConsolidator
940
+ * made two consecutive ticks take 64.9s and 63.5s. `AWAIT_TIMEOUT` — 15 ticks,
941
+ * normally ~15s — silently became 15 minutes, so a communicate intent sat
942
+ * 'awaiting' forever and the serial selector never chose anything again. 45
943
+ * executive decisions produced one intent and zero delivered messages, with no
944
+ * error anywhere. This turns that into a line in the log the first time it happens.
945
+ */
946
+ private _warnIfSlow;
931
947
  private _runLoop;
932
948
  private _shutdown;
933
949
  stop(): void;
@@ -1332,7 +1348,6 @@ declare class WorkingMemory implements SimulationEngine, CognitiveEngine {
1332
1348
  private _emitEvents;
1333
1349
  private _items;
1334
1350
  private _modulatedCapacity;
1335
- private _currentFocusId;
1336
1351
  private _activeGoalCount;
1337
1352
  /**
1338
1353
  * Monotonic suffix counter for injected WM item ids. Replaces Math.random()
@@ -1373,8 +1388,10 @@ declare class WorkingMemory implements SimulationEngine, CognitiveEngine {
1373
1388
  */
1374
1389
  private _ingestGoals;
1375
1390
  /**
1376
- * Mark the currently focused entity's WM item as attended this tick.
1377
- * Also checks for attention.focus entities in state as a fallback.
1391
+ * Mark the currently focused entity's WM item as attended this tick, from the
1392
+ * `attention.focus` entities AttentionAllocator writes. (A second, bus-driven
1393
+ * branch used to sit above this one, labelled "preferred"; the event behind it was
1394
+ * never published, so this loop has always been the only path — see #114.)
1378
1395
  */
1379
1396
  private _applyAttention;
1380
1397
  /**
@@ -1446,6 +1463,10 @@ interface GoalState {
1446
1463
  activatedAt: Tick;
1447
1464
  deadline?: Tick;
1448
1465
  tags: string[];
1466
+ /** Why this goal was abandoned, when it was. Set only on status 'abandoned'.
1467
+ * The reason used to be pushed onto `tags`, which threw once the goal had
1468
+ * been rehydrated from a deep-frozen state entity. */
1469
+ abandonedReason?: string;
1449
1470
  /** Snapshot of memory.beliefs_total when this goal was activated.
1450
1471
  * Used to compute epistemic progress: (currentBeliefs - baseline) / threshold. */
1451
1472
  beliefsAtActivation: number;
@@ -1573,6 +1594,19 @@ declare class GoalManager implements SimulationEngine, CognitiveEngine {
1573
1594
  * "learn" ↔ "learning").
1574
1595
  */
1575
1596
  private _nudgeActionGoals;
1597
+ /**
1598
+ * Somebody answered. Advance the action goals that were about reaching them.
1599
+ *
1600
+ * Linked the way every other goal→person link in the system is linked: the
1601
+ * `keid:<id>` tag (selection.scoring's `collectGoalTargets` reads the same one
1602
+ * to lift a reach-out's goal relevance) or `requestingEntityId`, set when a
1603
+ * conversation escalation created the goal. Nothing here guesses from wording.
1604
+ *
1605
+ * Unmatched by design: a goal with no link to this person gets nothing, even if
1606
+ * it is tagged 'communication'. Being answered by one person is not progress on
1607
+ * wanting to talk to another.
1608
+ */
1609
+ private _nudgeAnsweredGoals;
1576
1610
  /** True when a metric completionCondition (e.g. "emotion.boredom < 40") is already met. */
1577
1611
  private _isConditionMet;
1578
1612
  private _evaluateMetricProgress;
@@ -1586,32 +1620,62 @@ declare class GoalManager implements SimulationEngine, CognitiveEngine {
1586
1620
  private _persistGoals;
1587
1621
  }
1588
1622
 
1589
- /**
1590
- * TokenTracker monitors LLM token consumption across all engines.
1591
- *
1592
- * Hooks into the LLM calls to record:
1593
- * - Prompt tokens (input)
1594
- * - Completion tokens (output)
1595
- * - Total tokens
1596
- * - Cost (based on model pricing)
1597
- * - Per-engine breakdowns
1598
- * - Per-agent breakdowns
1599
- *
1600
- * Exposes as metrics so the orchestrator and runner can log costs,
1601
- * and the ParameterOptimizer can factor cost into optimization decisions.
1602
- */
1603
-
1623
+ /** Top-level cost bucket for an LLM call. */
1624
+ type LLMCallCategory = 'executive' | 'summarizer' | 'embedding' | 'identity-guard';
1625
+ /** The actor/subsystem doing the work. */
1626
+ type LLMCallAttribute = 'master' | 'facet' | 'memory' | 'guard';
1627
+ /** The specific cognitive process being paid for. */
1628
+ type LLMCallProcess = 'cog' | 'decision' | 'ideation';
1629
+ /** The specific cognitive function being paid for. */
1630
+ type LLMCallFunction = '-' | 'deliberation' | 'conversation' | 'outreach' | 'planning' | 'supervision' | 'consolidation' | 'recall' | 'index' | 'identity-coherence';
1604
1631
  /** One attributed ledger record (5-axis attribution + tokens + cost). */
1605
1632
  type TokenLedgerRecord = Record<string, unknown>;
1606
1633
  type TokenRecordListener = (record: TokenLedgerRecord) => void;
1607
- /** Resolve the pricing row for any model id (exact, normalized, then default). */
1608
- declare function resolvePricing(model: string): {
1634
+ /** USD per 1M tokens for one model. */
1635
+ interface ModelPrice {
1609
1636
  input: number;
1610
1637
  output: number;
1611
- };
1638
+ }
1639
+ /**
1640
+ * Host-supplied prices, keyed by model id. Matching is exact first, then
1641
+ * normalized (provider prefix, date stamp and context qualifier stripped), so
1642
+ * `claude-sonnet-5` matches `claude-sonnet-5-20260114`.
1643
+ *
1644
+ * Prices belong to the host: they change on a vendor's schedule, differ per
1645
+ * account, and are ~0 for a self-hosted model. The engine ships none.
1646
+ */
1647
+ type PriceTable = Record<string, ModelPrice>;
1648
+ /**
1649
+ * Resolve the price for a model id from the host's table.
1650
+ *
1651
+ * The engine ships no prices at all. A table baked into a release is wrong the
1652
+ * week a vendor changes a rate, differs per account, and is meaningless for a
1653
+ * self-hosted model — and a *partial* table is worse than none, because some
1654
+ * models then report plausible-but-stale numbers while others honestly report
1655
+ * nothing. Prices live with the host, next to the routing policy they inform.
1656
+ *
1657
+ * `null` does NOT mean free — it means *unknown*, and the caller reports zero
1658
+ * cost with `priced: false` so the gap stays visible rather than confidently
1659
+ * wrong. (The removed built-in default priced every unrecognised model at
1660
+ * Sonnet's rate, overstating a budget model's output by ~54×.)
1661
+ */
1662
+ declare function resolvePricing(model: string, hostPrices?: PriceTable): ModelPrice | null;
1612
1663
  interface TokenUsage {
1613
1664
  /** Model identifier (e.g., 'openai/gpt-4o') */
1614
1665
  model: string;
1666
+ /**
1667
+ * The provider that actually served this call.
1668
+ *
1669
+ * Not derivable from `model`: routing is what makes the same model id
1670
+ * reachable from several places — `deepseek-v3` direct, through a gateway, or
1671
+ * self-hosted — at prices that differ by orders of magnitude. Without this a
1672
+ * host billing across a multi-vendor routing table can attribute spend to a
1673
+ * model but never to the vendor it actually paid.
1674
+ *
1675
+ * Optional because a caller recording usage directly (outside the LLM
1676
+ * director) may not know it; absent means unattributed, not "the default".
1677
+ */
1678
+ provider?: string;
1615
1679
  /** Input/prompt tokens consumed */
1616
1680
  promptTokens: number;
1617
1681
  /** Output/completion tokens consumed */
@@ -1622,14 +1686,33 @@ interface TokenUsage {
1622
1686
  cacheReadTokens?: number;
1623
1687
  /** Anthropic prompt-cache write tokens (billed at 1.25× input). Optional. */
1624
1688
  cacheWriteTokens?: number;
1625
- /** Estimated cost in USD */
1689
+ /** Estimated cost in USD. Zero when `priced` is false — unknown, not free. */
1626
1690
  estimatedCostUsd: number;
1627
- /** Top-level cost bucket: 'executive' | 'summarizer' | 'embedding' | 'identity-guard' | … */
1628
- category: string;
1629
- /** Actor/subsystem doing the work: 'master' | 'facet' | 'memory' | 'guard' | … */
1630
- attribute: string;
1631
- /** Cognitive function: 'decision' | 'ideation' | 'conversation' | 'planning' | 'deliberation' | 'outreach' | 'consolidation' | 'recall' | 'index' | 'identity-coherence' | … */
1632
- function: string;
1691
+ /**
1692
+ * Whether a price was found for this model. False ⇒ `estimatedCostUsd` is 0
1693
+ * because nothing priced it, NOT because the call was free. A consumer
1694
+ * summing costs should surface unpriced calls rather than fold them in as
1695
+ * zero.
1696
+ */
1697
+ priced: boolean;
1698
+ /**
1699
+ * How much this call demanded, 0..1 — the cognitive measure the router saw.
1700
+ *
1701
+ * Recorded so routing can be ANSWERED rather than argued. Every call computes
1702
+ * this, routes on it, and until now threw it away — which left questions like
1703
+ * "is deliberation being rated by the tick's mood rather than the stakes of
1704
+ * its own choice?" with no dataset at all.
1705
+ *
1706
+ * Absent means UNMEASURED, never zero. It must stay nullable all the way to
1707
+ * storage: a call that never reported demand and a call that reported 0.0 are
1708
+ * different facts, and collapsing them would put a floor of invented
1709
+ * confidence under exactly the analysis this exists to enable.
1710
+ */
1711
+ demand?: number;
1712
+ category: LLMCallCategory;
1713
+ attribute: LLMCallAttribute;
1714
+ process: LLMCallProcess;
1715
+ function: LLMCallFunction;
1633
1716
  /** Optional specific id or namespace: facet id, entity id, model name. */
1634
1717
  scope?: string;
1635
1718
  /** Human-readable label — auto-composed from the axes when the caller omits it. */
@@ -1642,10 +1725,16 @@ interface TokenUsage {
1642
1725
  latencyMs: number;
1643
1726
  }
1644
1727
  /** What callers pass to {@link TokenTracker.recordUsage} — cost and label are derived. */
1645
- type RecordUsageInput = Omit<TokenUsage, 'estimatedCostUsd' | 'label'> & {
1728
+ type RecordUsageInput = Omit<TokenUsage, 'estimatedCostUsd' | 'label' | 'priced'> & {
1646
1729
  label?: string;
1647
1730
  };
1648
1731
  interface TokenTrackerConfig {
1732
+ /**
1733
+ * Host-supplied model prices (USD per 1M tokens), merged from the per-provider
1734
+ * `prices` maps in `WillLLMConfig.providers`. These win over the built-in
1735
+ * fallback table. Omitted ⇒ fallback only.
1736
+ */
1737
+ prices?: PriceTable;
1649
1738
  /** Whether to emit cost events */
1650
1739
  emitCostEvents?: boolean;
1651
1740
  /** Cost threshold for warning events */
@@ -1664,6 +1753,7 @@ declare class TokenTracker implements SimulationEngine {
1664
1753
  readonly name = "token-tracker";
1665
1754
  private _emitCostEvents;
1666
1755
  private _costWarningThreshold;
1756
+ private _prices;
1667
1757
  private _usageLog;
1668
1758
  private _totalPromptTokens;
1669
1759
  private _totalCompletionTokens;
@@ -1672,6 +1762,10 @@ declare class TokenTracker implements SimulationEngine {
1672
1762
  private _categoryTokens;
1673
1763
  private _functionCosts;
1674
1764
  private _functionTokens;
1765
+ private _processCosts;
1766
+ private _processTokens;
1767
+ private _providerCosts;
1768
+ private _providerTokens;
1675
1769
  private _tickCosts;
1676
1770
  private _maxTickCostSamples;
1677
1771
  private _lastCostWarningTick;
@@ -1716,6 +1810,28 @@ declare class TokenTracker implements SimulationEngine {
1716
1810
  prompt: number;
1717
1811
  completion: number;
1718
1812
  }>;
1813
+ /** Cost broken down by process ('decision' | 'ideation' | 'cog'). */
1814
+ get processBreakdown(): ReadonlyMap<string, number>;
1815
+ /** Token counts (prompt + completion) broken down by process. */
1816
+ get processTokenBreakdown(): ReadonlyMap<string, {
1817
+ prompt: number;
1818
+ completion: number;
1819
+ }>;
1820
+ /**
1821
+ * Cost broken down by provider ('anthropic' | 'glm' | 'moonshot' | …), plus
1822
+ * an `unattributed` bucket for usage recorded without one.
1823
+ *
1824
+ * This is the axis a host reconciles against vendor invoices. Calls whose
1825
+ * model went unpriced contribute 0 here, so compare against
1826
+ * `getUsageLog()`'s `priced` flag before treating a small number as a small
1827
+ * bill.
1828
+ */
1829
+ get providerBreakdown(): ReadonlyMap<string, number>;
1830
+ /** Token counts (prompt + completion) broken down by provider. */
1831
+ get providerTokenBreakdown(): ReadonlyMap<string, {
1832
+ prompt: number;
1833
+ completion: number;
1834
+ }>;
1719
1835
  /** Cost per call average */
1720
1836
  get averageCostPerCall(): number;
1721
1837
  /** Cost per tick average */
@@ -1739,6 +1855,8 @@ declare class TokenTracker implements SimulationEngine {
1739
1855
  * - Mock embedder for testing/deterministic replay
1740
1856
  */
1741
1857
 
1858
+ /** Embedding is only ever a read or a write. */
1859
+ type EmbedFunction = Extract<LLMCallFunction, 'recall' | 'index'>;
1742
1860
  interface EmbeddingProvider {
1743
1861
  readonly modelName: string;
1744
1862
  readonly dimensions: number;
@@ -1761,12 +1879,26 @@ declare class OpenAICompatibleEmbedder implements EmbeddingProvider {
1761
1879
  private _maxConcurrency;
1762
1880
  private _timeoutMs;
1763
1881
  private _tokenTracker;
1882
+ /**
1883
+ * Own gate — the same LLMSemaphore the LLM calls use, on a separate instance so
1884
+ * embeddings and reasoning do not compete for one another's slots. It bounds the
1885
+ * fan-out that produced the 10.7s tail, and `withGate` additionally retries a 429
1886
+ * with backoff, which a bare `embed()` previously surfaced as a hard failure.
1887
+ */
1888
+ private _gate;
1764
1889
  constructor(config: {
1765
1890
  modelName: string;
1766
1891
  dimensions: number;
1767
1892
  apiUrl: string;
1768
1893
  apiKey?: string | null;
1769
- /** Max embedding requests in flight at once for embedBatch(). Default 8. */
1894
+ /**
1895
+ * Max embedding requests in flight at once — across ALL callers, not just one
1896
+ * embedBatch(). Default 4, chosen from measured provider behaviour rather than
1897
+ * taste: gemini-embedding-001 answers a lone request in ~1.1s, but queues hard
1898
+ * under fan-out — at 8 in flight the slowest three took 10.7s (all HTTP 200, no
1899
+ * 429, simply serialized). That tail is what made recall exceed its 5s budget
1900
+ * and return "no recall" while a mind with six live facets was asking.
1901
+ */
1770
1902
  maxConcurrency?: number;
1771
1903
  /** @deprecated use maxConcurrency — kept as its fallback for back-compat. */
1772
1904
  batchSize?: number;
@@ -1775,12 +1907,20 @@ declare class OpenAICompatibleEmbedder implements EmbeddingProvider {
1775
1907
  /**
1776
1908
  * Per-Will token tracker. When provided, each embedding call records its
1777
1909
  * input-token usage under the 'embedding' category so memory-vector spend is
1778
- * visible alongside LLM spend instead of being a silent COGS leak.
1910
+ * visible alongside LLM spend instead of being a silent cost leak.
1779
1911
  */
1780
1912
  tokenTracker?: TokenTracker | null;
1781
1913
  });
1782
- embed(content: unknown, fn?: string): Promise<number[]>;
1783
- embedBatch(contents: unknown[], fn?: string): Promise<number[][]>;
1914
+ /**
1915
+ * Embed one item, gated. Every caller funnels through here — a facet building a
1916
+ * prompt, the master recalling, the consolidator indexing — so the gate is the
1917
+ * only place total in-flight fan-out is bounded. Waiting for a slot is strictly
1918
+ * better than the alternative it replaces: an ungated request that returns after
1919
+ * the recall budget has already expired is a request whose answer is thrown away.
1920
+ */
1921
+ embed(content: unknown, fn?: EmbedFunction): Promise<number[]>;
1922
+ private _embedOnce;
1923
+ embedBatch(contents: unknown[], fn?: EmbedFunction): Promise<number[][]>;
1784
1924
  areEquivalent(embedding1: number[], embedding2: number[], tolerance?: number): boolean;
1785
1925
  }
1786
1926
  /**
@@ -1792,8 +1932,8 @@ declare class MockEmbedder implements EmbeddingProvider {
1792
1932
  readonly dimensions = 128;
1793
1933
  private _seed;
1794
1934
  constructor(seed?: number);
1795
- embed(content: unknown, _fn?: string): Promise<number[]>;
1796
- embedBatch(contents: unknown[], fn?: string): Promise<number[][]>;
1935
+ embed(content: unknown, _fn?: EmbedFunction): Promise<number[]>;
1936
+ embedBatch(contents: unknown[], fn?: EmbedFunction): Promise<number[][]>;
1797
1937
  areEquivalent(embedding1: number[], embedding2: number[], tolerance?: number): boolean;
1798
1938
  private _hashString;
1799
1939
  private _next;
@@ -1945,6 +2085,16 @@ declare class DefaultVectorMemoryAdapter implements VectorMemoryAdapter {
1945
2085
  persist(): Promise<void>;
1946
2086
  load(): Promise<void>;
1947
2087
  private _evictColdest;
2088
+ /**
2089
+ * Throttle, NOT a debounce. The previous version cleared and re-armed the timer on
2090
+ * every index(), so it only ever fired after 5s of complete inactivity — and a mind
2091
+ * consolidating steadily indexes far more often than that, so the write was pushed
2092
+ * back indefinitely and the index never reached disk while it was awake.
2093
+ *
2094
+ * A pending timer is now left alone: the first write after a quiet period sets the
2095
+ * deadline, and everything indexed within the window rides along on it. Persist is
2096
+ * bounded at 5s from the FIRST pending change rather than the last.
2097
+ */
1948
2098
  private _schedulePersist;
1949
2099
  }
1950
2100
 
@@ -2007,6 +2157,13 @@ declare class EpisodicConsolidator implements SimulationEngine, CognitiveEngine
2007
2157
  private _affectDominance;
2008
2158
  private _bus;
2009
2159
  private _vectorMemory;
2160
+ /**
2161
+ * In-flight background indexing. Indexing is deliberately not awaited inside
2162
+ * react() (a rate-limit retry chain would stall the whole tick loop), so this is
2163
+ * the handle for the two callers that genuinely must wait for it: shutdown,
2164
+ * before persisting the index, and tests asserting on it.
2165
+ */
2166
+ private _indexing;
2010
2167
  private _embedder;
2011
2168
  private _autoIndex;
2012
2169
  private readonly _model;
@@ -2044,6 +2201,8 @@ declare class EpisodicConsolidator implements SimulationEngine, CognitiveEngine
2044
2201
  * Other metadata narrowing (sourceType / tags) remains the caller's job on the
2045
2202
  * returned episodes (which carry all metadata).
2046
2203
  */
2204
+ /** Await any background indexing still in flight. Shutdown and tests only. */
2205
+ flushIndexing(): Promise<void>;
2047
2206
  semanticQuery(query: unknown, filters?: {
2048
2207
  minSimilarity?: number;
2049
2208
  limit?: number;
@@ -2567,7 +2726,152 @@ declare class CompletionInbox {
2567
2726
  clear(): number;
2568
2727
  }
2569
2728
 
2570
- type LLMProvider = 'anthropic' | 'glm' | 'deepseek' | 'openai' | 'google';
2729
+ /**
2730
+ * Where a single call should go. Every field except `model` falls back to the
2731
+ * Will's default when omitted.
2732
+ */
2733
+ interface ModelRoute {
2734
+ /**
2735
+ * Omit to keep the Will's default provider and change only the model — the
2736
+ * common "same vendor, different model for this kind of work" route, and what
2737
+ * the per-role model map compiles to (a role has never had a provider of its
2738
+ * own). Name a provider to cross vendors; it must appear in `llm.providers`
2739
+ * or the route falls back to the default.
2740
+ */
2741
+ provider?: LLMProvider;
2742
+ model: string;
2743
+ /** Override the provider's API base (self-hosted / OpenAI-compatible servers). */
2744
+ baseUrl?: string;
2745
+ /** Override the output-token ceiling for this call. */
2746
+ maxOutputTokens?: number;
2747
+ }
2748
+ /**
2749
+ * Chooses a model for a call.
2750
+ *
2751
+ * Returning `null` means "no opinion" — the Will's default model is used. A
2752
+ * router should return `null` rather than guess when it does not recognise a
2753
+ * call: falling back is always safe, and a wrong route is not.
2754
+ */
2755
+ interface ModelRouter {
2756
+ /** Stable identifier, recorded alongside routing telemetry. */
2757
+ readonly name: string;
2758
+ route(meta: LLMCallMeta): ModelRoute | null;
2759
+ }
2760
+ /**
2761
+ * The default. Has no opinion about anything, allocates nothing.
2762
+ *
2763
+ * A Will running this must be byte-identical to one built before the routing
2764
+ * seam existed — that property is asserted by test, and it is what lets this
2765
+ * ship dark.
2766
+ */
2767
+ declare const NULL_ROUTER: ModelRouter;
2768
+ /** True when the router is the no-op default (used to skip the seam entirely). */
2769
+ declare function isNullRouter(router: ModelRouter | null | undefined): boolean;
2770
+ /**
2771
+ * One entry in a {@link TableRouter}'s table. All present conditions must match
2772
+ * (logical AND); an absent condition matches anything.
2773
+ */
2774
+ interface RoutingRule {
2775
+ /**
2776
+ * Match `LLMCallMeta.category` exactly (e.g. 'executive', 'summarizer').
2777
+ *
2778
+ * The axes are typed rather than free strings so a rule that names a bucket
2779
+ * the engine never emits fails to compile instead of silently never matching
2780
+ * — a routing table's worst failure is the rule that looks right and is dead.
2781
+ */
2782
+ category?: LLMCallMeta['category'];
2783
+ /** Match `LLMCallMeta.attribute` exactly (e.g. 'master', 'facet', 'guard'). */
2784
+ attribute?: LLMCallMeta['attribute'];
2785
+ /** Match `LLMCallMeta.process` exactly (e.g. 'decision', 'ideation'). */
2786
+ process?: LLMCallMeta['process'];
2787
+ /** Match `LLMCallMeta.function` exactly (e.g. 'decision', 'consolidation'). */
2788
+ function?: LLMCallMeta['function'];
2789
+ /**
2790
+ * Inclusive lower bound on `LLMCallMeta.demand`. A call with no demand
2791
+ * reported never matches a rule that sets this — absent means unknown, and
2792
+ * unknown must not be treated as zero.
2793
+ */
2794
+ minDemand?: number;
2795
+ /** Exclusive upper bound on `LLMCallMeta.demand`. Same absence rule. */
2796
+ maxDemand?: number;
2797
+ /** Where a matching call goes. */
2798
+ route: ModelRoute;
2799
+ }
2800
+ /**
2801
+ * A worked example of the seam: first matching rule wins, otherwise no opinion.
2802
+ *
2803
+ * This ships so that the interface has a reference implementation and so that
2804
+ * hosts have something to copy — it is deliberately dumb. It is not a routing
2805
+ * strategy, and the engine ships no table of its own: what belongs where is the
2806
+ * host's decision, expressed as configuration.
2807
+ *
2808
+ * Rules are evaluated in order, so put specific rules before general ones.
2809
+ */
2810
+ declare class TableRouter implements ModelRouter {
2811
+ readonly name: string;
2812
+ private readonly _rules;
2813
+ constructor(rules: readonly RoutingRule[], name?: string);
2814
+ route(meta: LLMCallMeta): ModelRoute | null;
2815
+ }
2816
+ /**
2817
+ * Ask each router in turn; the first with an opinion wins.
2818
+ *
2819
+ * This exists because a Will can have two sources of routing at once: the
2820
+ * host's own router, and the one compiled from its per-role model map. Order
2821
+ * expresses precedence — an explicit router is consulted before the role map,
2822
+ * which is the precedence those two mechanisms already had when roles were
2823
+ * served by separate directors.
2824
+ *
2825
+ * A throwing link is skipped, not propagated. The links are independent
2826
+ * decisions, and one broken router must not take a working one down with it —
2827
+ * that would silently demote every role-mapped call to the default model.
2828
+ */
2829
+ declare function chainRouters(...routers: (ModelRouter | null | undefined)[]): ModelRouter;
2830
+
2831
+ /**
2832
+ * The request/response dialect an endpoint speaks. This — not the provider's
2833
+ * name — is what the transport actually branches on.
2834
+ */
2835
+ type LLMWire = 'anthropic' | 'openai' | 'google';
2836
+ /** Providers with built-in defaults. Any other string is equally valid. */
2837
+ type KnownProvider = 'anthropic' | 'glm' | 'openai' | 'google' | 'deepseek' | 'moonshot' | 'qwen' | 'xai' | 'minimax' | 'mistral' | 'ollama' | 'vllm';
2838
+ /**
2839
+ * A provider name. Deliberately open: the field of providers changes monthly,
2840
+ * and a closed union meant a host reaching Kimi or Qwen had to masquerade as
2841
+ * `openai`, which then lied on the completion tape and in cost attribution.
2842
+ *
2843
+ * `(string & {})` keeps editor autocomplete for the known names while accepting
2844
+ * anything. A provider outside {@link KNOWN_PROVIDERS} simply has to declare its
2845
+ * `wire` and `baseUrl` — see `WillLLMConfig.providers`.
2846
+ */
2847
+ type LLMProvider = KnownProvider | (string & {});
2848
+ /**
2849
+ * Built-in wire + base URL per provider. This is *data*, not support: it saves
2850
+ * a host from looking up an endpoint, and nothing more. Any provider absent
2851
+ * from this table works identically once the host declares `wire` + `baseUrl`
2852
+ * on its `llm.providers` entry.
2853
+ *
2854
+ * WHY THIS TABLE SURVIVES WHEN THE PRICE TABLE DID NOT. A stale price is
2855
+ * invisible: it produces a confident wrong number nobody doubts. A stale base
2856
+ * URL fails on the first call, loudly, with the endpoint in the message. They
2857
+ * also move on completely different clocks — vendors reprice quarterly, and
2858
+ * change an API host about once a decade. Convenience is worth it when being
2859
+ * wrong is self-announcing.
2860
+ *
2861
+ * REGIONAL ENDPOINTS. `moonshot`, `qwen` and `minimax` all run separate
2862
+ * mainland-China hosts (`api.moonshot.cn`, `dashscope.aliyuncs.com`,
2863
+ * `api.minimaxi.com`). The international host is the default here; a key issued
2864
+ * on the other one authenticates nowhere, so a host on a China account must set
2865
+ * `baseUrl` explicitly.
2866
+ */
2867
+ declare const KNOWN_PROVIDERS: Record<string, {
2868
+ wire: LLMWire;
2869
+ baseUrl: string;
2870
+ }>;
2871
+ /** Built-in wire for a known provider, or undefined — the host must declare it. */
2872
+ declare function knownWireFor(provider: LLMProvider): LLMWire | undefined;
2873
+ /** Built-in base URL for a known provider, or undefined — the host must declare it. */
2874
+ declare function defaultBaseFor(provider: LLMProvider): string | undefined;
2571
2875
  interface LLMDirectorConfig {
2572
2876
  willId: string;
2573
2877
  model: string;
@@ -2597,6 +2901,38 @@ interface LLMDirectorConfig {
2597
2901
  * replay runs). This replaces the former process-global getTokenTracker().
2598
2902
  */
2599
2903
  tokenTracker?: TokenTracker | null;
2904
+ /**
2905
+ * MODEL_ROUTING W3 — per-call model selection. Absent (or NULL_ROUTER) means
2906
+ * every call uses the default model below, exactly as before the seam existed.
2907
+ * A router that throws, or names a provider with no usable credential, falls
2908
+ * back to the default: a routing problem must never kill a running mind.
2909
+ */
2910
+ router?: ModelRouter | null;
2911
+ /**
2912
+ * Per-provider credentials for routed calls. The top-level `apiKey`/`baseUrl`
2913
+ * remain the default entry; a route to a provider absent from this map falls
2914
+ * back to the default endpoint.
2915
+ */
2916
+ credentials?: Partial<Record<string, ProviderCredential>>;
2917
+ /**
2918
+ * Dialect for the default provider. Required when the provider is not one of
2919
+ * {@link KNOWN_PROVIDERS} — the engine will not guess how to talk to an
2920
+ * endpoint it has never heard of.
2921
+ */
2922
+ wire?: LLMWire;
2923
+ }
2924
+ /**
2925
+ * Everything a single call needs to reach a model. Resolved once per call and
2926
+ * threaded through the provider methods — never stored on the instance, because
2927
+ * the concurrency gate lets several calls be in flight on one director at once
2928
+ * and per-call state on `this` would race between them.
2929
+ */
2930
+ /** What a host supplies so a routed provider can be reached. */
2931
+ interface ProviderCredential {
2932
+ apiKey: string;
2933
+ baseUrl?: string;
2934
+ /** Required for providers outside {@link KNOWN_PROVIDERS}. */
2935
+ wire?: LLMWire;
2600
2936
  }
2601
2937
  interface LLMCallResult {
2602
2938
  text: string;
@@ -2614,17 +2950,56 @@ interface LLMCallResult {
2614
2950
  * here, letting the TokenTracker break spend down per category for transparency.
2615
2951
  */
2616
2952
  interface LLMCallMeta {
2617
- /** Top-level cost bucket: 'executive' | 'summarizer' | 'embedding' | 'identity-guard' | … */
2618
- category: string;
2619
- /** The actor/subsystem doing the work: 'master' | 'facet' | 'memory' | 'guard' | … */
2620
- attribute: string;
2621
- /** The specific cognitive function: 'decision' | 'ideation' | 'conversation' | 'planning' | 'deliberation' | 'outreach' | 'consolidation' | 'recall' | 'index' | 'identity-coherence' | … */
2622
- function: string;
2953
+ /** Top-level cost bucket. */
2954
+ category: LLMCallCategory;
2955
+ /** The actor/subsystem doing the work. */
2956
+ attribute: LLMCallAttribute;
2957
+ /** The specific cognitive function. */
2958
+ process: LLMCallProcess;
2959
+ /** The specific cognitive function. */
2960
+ function: LLMCallFunction;
2623
2961
  /** Optional specific id or namespace: facet id, entity id, model name. */
2624
2962
  scope?: string;
2625
2963
  /** Free-form human-readable label. Auto-composed from the axes when omitted. */
2626
2964
  label?: string;
2965
+ /**
2966
+ * How much this call demands, 0..1 — MODEL_ROUTING W0.
2967
+ *
2968
+ * A *cognitive* measure, never a commercial one: it says how consequential or
2969
+ * uncertain this moment is, never who is paying for it.
2970
+ *
2971
+ * What actually reports it today:
2972
+ * · the master and every facet — including the deliberation facet — forward
2973
+ * `effortScore`, the a-priori effort gate (uncertainty, prior confidence,
2974
+ * novelty, a pending reply, stress load);
2975
+ * · structurally background work (summarising, the identity guard) reports a
2976
+ * low constant, because it is background whether the mind is calm or in
2977
+ * crisis.
2978
+ *
2979
+ * Note what is NOT here: agency's `stakes( winner, bias )` — the consequence
2980
+ * of the specific choice under contest — is computed in
2981
+ * `agency/selection.scoring.ts` and never reaches a call. So a deliberation
2982
+ * facet currently reports the tick's general effort, not the stakes of the
2983
+ * choice it is deliberating. Carrying it is MODEL_ROUTING W1, still open.
2984
+ * This comment previously claimed deliberation passed those stakes; it does
2985
+ * not, and a doc comment that ships to npm is a bad place to be aspirational.
2986
+ *
2987
+ * Absent means UNKNOWN, not zero: a consumer must fall back to its default
2988
+ * rather than treat a missing value as "cheapest possible".
2989
+ *
2990
+ * This field is inert with respect to cognition. It rides along to whoever
2991
+ * resolves the model for a call; no engine may read it back and behave
2992
+ * differently, or the routing layer becomes a hidden input to the mind.
2993
+ */
2994
+ demand?: number;
2627
2995
  }
2996
+ /** Structurally background work — see `LLMCallMeta.demand`. */
2997
+ declare const BACKGROUND_DEMAND = 0.1;
2998
+ /**
2999
+ * Escalation is elevated by construction: the buffer only fires once something
3000
+ * has already failed to resolve on its own.
3001
+ */
3002
+ declare const ESCALATION_DEMAND = 0.7;
2628
3003
  declare class LLMDirector {
2629
3004
  private _willId;
2630
3005
  private _model;
@@ -2636,7 +3011,20 @@ declare class LLMDirector {
2636
3011
  private _baseUrl;
2637
3012
  private _timeoutMs;
2638
3013
  private _tokenTracker;
3014
+ private _router;
3015
+ private _credentials;
3016
+ /** Default endpoint — what every call used before the routing seam existed. */
3017
+ private _defaultEndpoint;
3018
+ /** Routes already warned about (missing credential / bad provider) — log once. */
3019
+ private _routeWarned;
2639
3020
  constructor(config: LLMDirectorConfig);
3021
+ /**
3022
+ * Resolve which model serves this call. Falls back to the default endpoint
3023
+ * whenever the router has no opinion, throws, or names a provider we hold no
3024
+ * credential for — degrade, never crash.
3025
+ */
3026
+ private _resolveEndpoint;
3027
+ private _warnRouteOnce;
2640
3028
  /**
2641
3029
  * Returns a structurally valid executive output with zero API cost.
2642
3030
  * Used when `mock: true` — e.g. for `bw_test_` key holders and the Playground.
@@ -2705,8 +3093,6 @@ declare class LLMDirector {
2705
3093
  /** Cost-attribution tag for this call. Defaults to the master executive. */
2706
3094
  meta?: LLMCallMeta): Promise<LLMCallResult>;
2707
3095
  private _callProvider;
2708
- /** Default API base URL (including version segment) for a provider. */
2709
- private _baseFor;
2710
3096
  /** Resolved API base: explicit override wins, else the provider default. */
2711
3097
  private _resolvedBase;
2712
3098
  /**
@@ -2866,6 +3252,18 @@ interface ExecutiveOutputFull {
2866
3252
  name?: string;
2867
3253
  learned?: string[];
2868
3254
  feeling?: number;
3255
+ /**
3256
+ * "This is the same someone as that" — another keid I now believe is this
3257
+ * same referent, fusing two of my records into one.
3258
+ *
3259
+ * The mind's own verdict on an identity, and it may do what the recognition
3260
+ * heuristic will not: absorb an ESTABLISHED relationship. The heuristic is
3261
+ * right to refuse — fusing two real people who share a name would take one of
3262
+ * them's whole history — but the mind has evidence a name-match does not,
3263
+ * usually because somebody just told it. Without this, the same human
3264
+ * well-established on two channels stayed two people permanently.
3265
+ */
3266
+ sameAs?: string;
2869
3267
  }>;
2870
3268
  newGoals?: Array<{
2871
3269
  description: string;
@@ -2884,6 +3282,8 @@ interface ExecutiveOutputFull {
2884
3282
  reason: string;
2885
3283
  }>;
2886
3284
  selfObservations?: string[];
3285
+ /** Compound actions the mind is naming as single skills (see ProposedSkill). */
3286
+ newSkills?: ProposedSkill[];
2887
3287
  /**
2888
3288
  * Plain-text reply from a conversation facet — populated by parseResponse()
2889
3289
  * from the [REPLY_TEXT]...[/REPLY_TEXT] block.
@@ -2891,6 +3291,11 @@ interface ExecutiveOutputFull {
2891
3291
  * Paragraphs (double-newline separated) map to separate reply bubbles.
2892
3292
  */
2893
3293
  replyText?: string;
3294
+ /**
3295
+ * Set when the facet declared it is NOT speaking this cycle, carrying why.
3296
+ * Present ⇒ nothing is sent, whatever else the response contains.
3297
+ */
3298
+ noMessage?: string;
2894
3299
  /**
2895
3300
  * @deprecated Legacy JSON reply format — no longer emitted by conversation facets.
2896
3301
  * Kept for backward compatibility with any tests/tooling that inspect parsed output.
@@ -2940,6 +3345,24 @@ interface ExecutiveEngineConfig$1 {
2940
3345
  cooldownTicks?: number;
2941
3346
  bus?: CognitiveBus;
2942
3347
  }
3348
+ /**
3349
+ * A compound action the mind names as one thing it does — "when I do A then B,
3350
+ * that is <name>". Registered into the SchemaRepertoire as a composite, after
3351
+ * which it competes as a single affordance and can proceduralize into a habit.
3352
+ *
3353
+ * This is the creation seam for the instrumental→habitual gradient. Before it,
3354
+ * `agency.composite.proposed` was subscribed by ReafferenceEngine — whose handler
3355
+ * is the only caller of `registerComposite()` anywhere — and published by nothing,
3356
+ * so no Will could ever hold a skill beyond the innate floor (#114).
3357
+ */
3358
+ interface ProposedSkill {
3359
+ /** What the mind calls it. Becomes the schema id. */
3360
+ id: string;
3361
+ /** The sub-schemas it is made of, in order. Two or more, or it is not compound. */
3362
+ composedOf: string[];
3363
+ tags?: string[];
3364
+ cost?: number;
3365
+ }
2943
3366
 
2944
3367
  /**
2945
3368
  * PromptFactory — single source of truth for all executive prompts
@@ -2996,7 +3419,7 @@ interface FocusSection {
2996
3419
  * into the facet's LLM calls as `LLMCallMeta.function` so the TokenTracker can
2997
3420
  * break spend down per facet type. Defaults to 'facet' when unset.
2998
3421
  */
2999
- function?: string;
3422
+ function?: LLMCallFunction;
3000
3423
  /**
3001
3424
  * Optional: Custom output format to append instead of the standard executive format.
3002
3425
  * Pass via PromptBuildOptions.outputFormat when building the user message.
@@ -3027,6 +3450,18 @@ interface FocusSection {
3027
3450
  * only sees that person's plans. When unset, those sections show all.
3028
3451
  */
3029
3452
  awarenessEntityId?: string;
3453
+ /**
3454
+ * Optional: WHO this facet is engaged with — the keid and the name the mind has
3455
+ * learned for them. Reported back to the master on every `executive.facet.sync`.
3456
+ *
3457
+ * Without it the master was told, in its own system prompt, that "focused facets
3458
+ * may run simultaneously… their reasoning syncs back to me" while the sync payload
3459
+ * carried only a facetId and a confidence number — so a mind holding two live
3460
+ * conversations could not tell you whose they were. The master is the singular
3461
+ * seat: it has to know who is at the table to reason about them together.
3462
+ */
3463
+ subjectEntityId?: string;
3464
+ subjectName?: string;
3030
3465
  /**
3031
3466
  * Optional: Provided by the creating engine to convert the LLM's parsed output
3032
3467
  * into a domain-specific decision payload.
@@ -3053,6 +3488,18 @@ interface FacetReport {
3053
3488
  contextId?: string;
3054
3489
  /** Optional dynamic instructions to append to the user message */
3055
3490
  instructions?: string;
3491
+ /**
3492
+ * Attend to something else for THIS report only, leaving the facet's standing
3493
+ * focus untouched.
3494
+ *
3495
+ * A facet's focus was a single mutable field, so anything that wanted a live
3496
+ * facet to consider one different thing had to `setFocus()` first — clobbering
3497
+ * whatever the facet was already set up for, and racing with any report already
3498
+ * queued behind it. That is why a self-initiated message to someone the mind was
3499
+ * ALREADY talking to had to be composed by a separate, transient facet that could
3500
+ * not see the live conversation at all.
3501
+ */
3502
+ focus?: FocusSection;
3056
3503
  }
3057
3504
  interface FacetDecision {
3058
3505
  facetId: string;
@@ -3062,6 +3509,16 @@ interface FacetDecision {
3062
3509
  decision: unknown;
3063
3510
  reasoning: string;
3064
3511
  confidence: number;
3512
+ /**
3513
+ * Sim tick this decision was reasoned at.
3514
+ *
3515
+ * The facet has always known it and never passed it on, so a subscriber writing
3516
+ * state in response had no deterministic clock and reached for a process-local
3517
+ * counter instead — which resets on restart and collides. Never wall-clock: this
3518
+ * reaches ids that live in state, and a wall-clock id makes recorded and replayed
3519
+ * runs diverge (R2).
3520
+ */
3521
+ tick: number;
3065
3522
  }
3066
3523
  type FacetEventListener = (decision: FacetDecision) => void;
3067
3524
  interface ExecutiveFacetHandle {
@@ -3095,6 +3552,44 @@ interface ExecutiveFacetHandle {
3095
3552
  onReaped: (handler: () => void) => void;
3096
3553
  }
3097
3554
 
3555
+ /**
3556
+ * DeliberationCache — types and contracts.
3557
+ *
3558
+ * The cache stores past executive outputs keyed by a deterministic
3559
+ * cognitive fingerprint. It is pure, deterministic, and R2-safe:
3560
+ * the same state + same history ⇒ same retrieval + same composition.
3561
+ *
3562
+ * Scope note: Phase 1 caches the ACTIONS block only. The composed output
3563
+ * is a valid `ExecutiveOutputFull` carrying the three required fields
3564
+ * (actions, reasoning, confidence) plus whatever optional blocks the
3565
+ * enabled scopes cover. Everything else stays undefined and the existing
3566
+ * downstream (`buildStateCommands`) treats it as "nothing to do", which is
3567
+ * exactly the intended Phase-1 behaviour.
3568
+ */
3569
+
3570
+ /** Which blocks of the executive output the cache may synthesise. */
3571
+ type CacheScope = 'actions' | 'goals' | 'beliefs';
3572
+ interface DeliberationCacheConfig {
3573
+ /** Maximum patterns to retain. Lowest (competence × recency) evicted when full. */
3574
+ maxPatterns?: number;
3575
+ /** Neighbors retrieved for composition. */
3576
+ k?: number;
3577
+ /** Minimum similarity for a stored pattern to count as a neighbor. */
3578
+ minSimilarity?: number;
3579
+ /** Confidence threshold θ — cache hit requires ρ ≥ θ. Start conservative. */
3580
+ theta?: number;
3581
+ /** Temperature for softmax weights over neighbors. */
3582
+ tau?: number;
3583
+ /** Learning rate (EMA) for competence updates. */
3584
+ eta?: number;
3585
+ /** Competence decay per executive cycle (applied via decay()). */
3586
+ decayPerCycle?: number;
3587
+ /** Verify 1-in-N cache hits against the LLM (0 = never). */
3588
+ verifyEveryNHits?: number;
3589
+ /** Which output blocks to synthesise. Phase 1 default: ['actions']. */
3590
+ scopes?: CacheScope[];
3591
+ }
3592
+
3098
3593
  interface ExecutiveEngineConfig {
3099
3594
  executiveInterval?: number;
3100
3595
  cooldownTicks?: number;
@@ -3108,20 +3603,26 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3108
3603
  private _consumedBufferEntries;
3109
3604
  private _llmDirector;
3110
3605
  private _testMode;
3111
- private _messageQueue;
3112
3606
  private _recentActionTypes;
3113
3607
  private _coherenceVersion;
3114
3608
  private _lastEpistemicUncertainty;
3115
3609
  private _lastExecutiveOutput;
3116
3610
  private _lastExecutiveTick;
3611
+ private _cache;
3612
+ private _cacheRestored;
3613
+ private _pendingVerify;
3614
+ private _lastCacheHit;
3615
+ private _lastCacheConfidence;
3616
+ private _lastCacheNeighborCount;
3117
3617
  private _willId;
3118
- /** Per-Will, per-role model ids (config.model, resolved in mind.ts). */
3119
- private _models;
3618
+ /**
3619
+ * The Will's default model (config.model's `executive` role, resolved in
3620
+ * mind.ts). Every other role reaches its model through the router — see
3621
+ * `compileRoleRouter`.
3622
+ */
3623
+ private _modelId;
3120
3624
  /** Per-Will LLM transport overrides (config.llm) — env fallbacks apply per field. */
3121
3625
  private _llm;
3122
- /** One director per distinct model — same config, different model. Shared
3123
- * tracker/recorder/willId, so ledger attribution and replay hold per role. */
3124
- private _directorCache;
3125
3626
  private _workingMemory;
3126
3627
  private _goalManager;
3127
3628
  private _episodicConsolidator;
@@ -3134,11 +3635,28 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3134
3635
  private _inbox;
3135
3636
  private _tokenTracker;
3136
3637
  private readonly _facetSupervisor;
3137
- private _facetSyncSubscribed;
3638
+ /**
3639
+ * Who each live facet is engaged with, learned from `executive.facet.sync`.
3640
+ * Keyed by facetId; the last sync wins. Rendered into the master's own prompt so
3641
+ * the singular seat can reason across its conversations "as if they were sitting
3642
+ * at the same table" — which it cannot do while it only knows facet numbers.
3643
+ * Stale entries age out on read (see _activeConversations).
3644
+ */
3645
+ private _facetSubjects;
3138
3646
  private readonly _model;
3139
3647
  private readonly _generativeModel;
3140
3648
  private _summarizerRestored;
3141
3649
  private _lastStateRef;
3650
+ /**
3651
+ * The tick currently being processed, refreshed every react() — distinct from
3652
+ * `_lastStateRef` (which tracks the REASONING tick and must not move under
3653
+ * onReasoningComplete) and from `_lastExecutiveTick` (the last cycle that ran).
3654
+ *
3655
+ * Off-tick arrivals — a facet handoff, in particular — need to be stamped with
3656
+ * when they actually happened. Using `_lastExecutiveTick` for that dated them to
3657
+ * the last master cycle, which can be hundreds of ticks behind.
3658
+ */
3659
+ private _currentTick;
3142
3660
  private readonly _deferred;
3143
3661
  private _chunkBroadcaster;
3144
3662
  private readonly _escalations;
@@ -3164,19 +3682,15 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3164
3682
  */
3165
3683
  attachCompletionInbox(inbox: CompletionInbox): void;
3166
3684
  set willId(willId: string);
3167
- /** Per-Will role models (config.model, resolved). Set before the first tick. */
3168
- set models(m: {
3169
- executive: string | null;
3170
- summarizer: string | null;
3171
- deliberation: string | null;
3172
- conversation: string | null;
3173
- });
3174
- get models(): {
3175
- executive: string | null;
3176
- summarizer: string | null;
3177
- deliberation: string | null;
3178
- conversation: string | null;
3179
- };
3685
+ /**
3686
+ * The Will's default model. Set before the first tick.
3687
+ *
3688
+ * This replaced a four-role map (W7): the other roles are routing rules now,
3689
+ * compiled in mind.ts, so the engine holds one model and one router rather
3690
+ * than a model per role plus a router.
3691
+ */
3692
+ set modelId(id: string | null);
3693
+ get modelId(): string | null;
3180
3694
  /** Per-Will LLM transport overrides (config.llm). Set before the first tick. */
3181
3695
  set llm(c: {
3182
3696
  provider?: string;
@@ -3184,9 +3698,10 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3184
3698
  baseUrl?: string;
3185
3699
  maxOutputTokens?: number;
3186
3700
  timeoutMs?: number;
3701
+ credentials?: Partial<Record<string, ProviderCredential>>;
3702
+ router?: ModelRouter | null;
3703
+ wire?: LLMWire;
3187
3704
  } | null);
3188
- /** The executive-role model id (back-compat read). */
3189
- get modelId(): string | null;
3190
3705
  get latestOutput(): ExecutiveOutputFull | null;
3191
3706
  isFresh(currentTick: Tick): boolean;
3192
3707
  /**
@@ -3202,11 +3717,56 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3202
3717
  * and subscribe() to receive facet decisions.
3203
3718
  */
3204
3719
  /** Get-or-create the director for a model id (shared config, per-Will). */
3205
- private _directorFor;
3206
- spawnFacet(role?: 'deliberation' | 'conversation' | 'outreach' | 'supervision'): {
3720
+ /**
3721
+ * The provider, from config or environment never guessed.
3722
+ *
3723
+ * This used to default to 'anthropic', which is how a Will configured for one
3724
+ * vendor could quietly talk to another. An unset provider is a configuration
3725
+ * error, and saying so at construction is far cheaper than a 401 mid-tick.
3726
+ */
3727
+ private _requireProvider;
3728
+ /**
3729
+ * True when this Will cannot make a live call, so provider/model are not
3730
+ * required: mock mode, or a replay re-feeding recorded completions.
3731
+ */
3732
+ private _noLiveCalls;
3733
+ /**
3734
+ * Build this Will's one and only director.
3735
+ *
3736
+ * There used to be a cache of them, keyed by model, because the per-role
3737
+ * model map had no other way to make a role use a different model. Routing
3738
+ * gave it one — the role map now compiles to rules (see `compileRoleRouter`)
3739
+ * and a single director resolves every call's endpoint per call. That is also
3740
+ * strictly more faithful: a facet follows the work it is doing rather than
3741
+ * whatever role it happened to be spawned under.
3742
+ */
3743
+ private _buildDirector;
3744
+ /**
3745
+ * Spawn a facet.
3746
+ *
3747
+ * `role` declares the facet's intent at the call site. It no longer selects a
3748
+ * model: that used to happen here, pinning a facet for life to whatever role
3749
+ * it was spawned under, and it now happens per call from the focus function
3750
+ * the caller sets immediately afterwards (W7). The two always agreed — every
3751
+ * spawn site sets a focus whose `function` matches its role — so the routed
3752
+ * answer is the same one, decided later and from the work itself.
3753
+ */
3754
+ spawnFacet(role?: 'deliberation' | 'conversation' | 'outreach' | 'supervision',
3755
+ /**
3756
+ * What this facet is FOR — see `FacetSpawnDeps.key`. Two spawns with the same
3757
+ * key get the same facet, so callers no longer each invent their own dedup
3758
+ * (and `authorOutreach`, which had none, no longer opens a rival facet on a
3759
+ * person the mind is already talking to).
3760
+ */
3761
+ key?: string): {
3207
3762
  attention: 'available' | 'full';
3208
3763
  handle?: ExecutiveFacetHandle;
3209
3764
  };
3765
+ /**
3766
+ * The facet already attending to `key`, if one is open — without spawning.
3767
+ * See FacetSupervisor.handleFor.
3768
+ */
3769
+ facetFor(key: string): ExecutiveFacetHandle | undefined;
3210
3770
  subscribes(): string[];
3211
3771
  publishes(): CognitiveEventSchema[];
3212
3772
  snapshot(): Record<string, unknown>;
@@ -3218,12 +3778,119 @@ declare class ExecutiveEngine extends AsyncEngine implements CognitiveEngine {
3218
3778
  * its `executive.last_tick` metric reflects whether our prior commands landed.
3219
3779
  */
3220
3780
  react(delta: Duration, tick: Tick, state: ReadonlySimulationState, context: SimulationContext): Promise<EngineResult>;
3781
+ /**
3782
+ * What the mind is attending to because a facet is reasoning about it, as
3783
+ * `attention.demand` entities the AttentionAllocator allocates real capacity
3784
+ * against (`_extractSalienceSignals` reads this type; `costPerFocus` is then
3785
+ * charged against the same 100-unit budget as perceptual foci).
3786
+ *
3787
+ * This closes a loop that was open in one direction only: the allocator's
3788
+ * `freeFraction` scaled the facet budget, but facets never appeared in
3789
+ * `_activeFocus`, so holding three conversations reported exactly as much spare
3790
+ * attention as holding none. The budget was being scaled by a signal blind to the
3791
+ * thing it was bounding.
3792
+ *
3793
+ * `urgency` sits below 1 on purpose: a live conversation is a genuine claim on
3794
+ * attention but must not automatically outrank every percept — the allocator sorts
3795
+ * candidates by salience into `maxFoci` slots, and a facet that always won would
3796
+ * starve perception. Only BUSY facets are charged; an open-but-quiet thread is one
3797
+ * the mind is in, not one it is attending to.
3798
+ */
3799
+ private _facetAttentionDemands;
3221
3800
  protected shouldAct(state: ReadonlySimulationState, tick: Tick, _context: SimulationContext): boolean;
3222
3801
  protected readState(state: ReadonlySimulationState, tick: Tick): ReasoningFootprint;
3223
3802
  protected reasonAsync(footprint: ReasoningFootprint, state: ReadonlySimulationState, context: SimulationContext, stream: IntermediateStream): Promise<unknown>;
3224
3803
  protected onIntermediateResult(step: string, result: unknown, _footprint: ReasoningFootprint, _context: SimulationContext): StateCommands | null;
3225
3804
  protected onReasoningComplete(output: unknown, footprint: ReasoningFootprint, _context: SimulationContext): StateCommands;
3226
- private _ensureFacetSyncSubscription;
3805
+ /**
3806
+ * The people the mind is in conversation with right now, newest first.
3807
+ *
3808
+ * Pruned against the supervisor's live facets on every read: a reaped facet is a
3809
+ * conversation that has ended, and a master that still believes it is mid-thread
3810
+ * with someone reasons about a table that is no longer there.
3811
+ */
3812
+ private _activeConversations;
3813
+ /**
3814
+ * Facet sync — remember WHO each facet is with, and wake the master.
3815
+ *
3816
+ * Reached from `onCognitiveEvent`, NOT from its own `bus.subscribe`. The bus
3817
+ * stores one subscription per engineId (`_subscriptions.set( engineId, … )`),
3818
+ * so a second `subscribe(this.name, …)` silently REPLACES the first — and the
3819
+ * orchestrator registers `subscribe( engine.name, engine.subscribes(), … )`
3820
+ * after `attachBus`, which replaced everything registered here. Two dedicated
3821
+ * handlers used to be installed at this point; the second overwrote the first
3822
+ * and the orchestrator then overwrote that, so neither ever ran. The escalation
3823
+ * leg had been dead in production for its whole life: a facet could escalate,
3824
+ * the audition engine published, and nothing was listening.
3825
+ */
3826
+ /**
3827
+ * Retire undertakings the mind has already honoured, and refuse to restate one
3828
+ * it is already carrying.
3829
+ *
3830
+ * An undertaking percept says, in the first person, "I said I would reach X and
3831
+ * nothing has gone to them yet". That sentence has to stop being true at some
3832
+ * point, and nothing made it stop. Measured on a live Will: SEVEN of them
3833
+ * accumulated in state, every one still asserting nothing had been sent, while
3834
+ * a `conversation.sent` to that person sat right beside them. She read seven
3835
+ * standing unfulfilled promises every cycle and dutifully sent the same message
3836
+ * again, five times in five minutes and once more in the next session — the
3837
+ * percept meant to stop her forgetting a promise was making her unable to
3838
+ * believe she had kept it.
3839
+ *
3840
+ * Discharged by EVIDENCE, not by a timer: a `conversation.sent` to that target,
3841
+ * written no earlier than the undertaking, means the contact happened. That
3842
+ * record is durable and snapshots with the state, so the discharge survives a
3843
+ * restart exactly as the promise does — which the tick-scoped satiation in
3844
+ * `enactionFootprint` deliberately cannot.
3845
+ *
3846
+ * It stays a decision, not an erasure. Retiring the percept removes the standing
3847
+ * claim that the words are unsent; whether to say more to that person is then an
3848
+ * ordinary competition like any other.
3849
+ */
3850
+ private _reconcileUndertakings;
3851
+ private _onFacetSync;
3852
+ /**
3853
+ * A focused part of me surfaced something the singular seat owns — work to plan
3854
+ * (`escalation`) or an intention toward a third party (`undertaking`).
3855
+ *
3856
+ * ONE handler for every facet type. This was `_onAuditionTaskSignal`, listening
3857
+ * on a topic named for one sense engine and typed with one sense engine's nouns
3858
+ * (`entityId`, `threadId`), which meant a planning, supervision or deliberation
3859
+ * facet had no way to hand anything up at all. See EscalationBuffer for the full
3860
+ * rationale; new kinds go in `HandoffBody`, not in a new topic and a new handler
3861
+ * beside this one.
3862
+ *
3863
+ * Master stays out of the reply path entirely:
3864
+ * • The facet has already said (or will say) whatever the person in front of
3865
+ * it needed to hear.
3866
+ * • The master's job is purely cognitive: create a [PLANS] block, update
3867
+ * goals, reflect, or decide whether it still means to make that contact.
3868
+ * Any follow-up communication flows through the agency competition —
3869
+ * NEVER via [REPLY].
3870
+ *
3871
+ * Buffered rather than written directly: state is read-only here, so
3872
+ * `EscalationBuffer.drainToPercepts()` emits it as a StateCommand on the next
3873
+ * master cycle, where Exteroception surfaces it under "## Percepts (What I Notice)".
3874
+ */
3875
+ private _onFacetHandoff;
3876
+ /** Enable the deliberation cache (off by default). Call during mind assembly. */
3877
+ enableCache(config?: DeliberationCacheConfig): void;
3878
+ /** True when the cache is active. */
3879
+ get cacheEnabled(): boolean;
3880
+ /** Telemetry snapshot for harnesses / eval. Null when disabled. */
3881
+ cacheStats(): {
3882
+ size: number;
3883
+ hits: number;
3884
+ misses: number;
3885
+ } | null;
3886
+ /**
3887
+ * Reafference hook — update cache competence from a confirmed action outcome.
3888
+ * Optional, layered on top of the inline verify loop. Reward follows the
3889
+ * research sketch: mean of (action succeeded, stress relief, goal progress).
3890
+ */
3891
+ onActionOutcome(state: ReadonlySimulationState, tick: Tick, success: boolean, stressDelta: number, goalProgressDelta: number): void;
3892
+ private _actionTypesMatch;
3893
+ private _restoreDeliberationCache;
3227
3894
  private _restoreSummarizer;
3228
3895
  }
3229
3896
 
@@ -3837,6 +4504,13 @@ declare class ThreatEvaluator implements SimulationEngine, CognitiveEngine {
3837
4504
  * metadata.hostile === true). Updates _threatFromHostile and re-emits
3838
4505
  * the full threat/emotion metrics so that downstream engines always see
3839
4506
  * a current picture even when no bus event arrives.
4507
+ *
4508
+ * `threat` is a HOST SEAM, not a starved input (#114). No core engine writes
4509
+ * one — appraisal runs entirely off this engine's six bus inputs (energy,
4510
+ * sleep, stress, novelty, metacognition, prediction), all of which are live.
4511
+ * A host embedding a Will in a world with actual hostile agents writes `threat`
4512
+ * entities to make them felt. Empty here means nothing is hostile, not that
4513
+ * nothing is wired.
3840
4514
  */
3841
4515
  react(_delta: Duration, _tick: Tick, state: ReadonlySimulationState, _context: SimulationContext): Promise<EngineResult>;
3842
4516
  private _computeScarcityThreat;
@@ -4696,6 +5370,8 @@ declare class SelfModelUpdater extends AsyncEngine implements CognitiveEngine {
4696
5370
  private _affectObservations;
4697
5371
  private _bus;
4698
5372
  private _semanticIntegrator;
5373
+ /** The reasoning tick's state — onReasoningComplete needs it to merge identity. */
5374
+ private _lastStateRef;
4699
5375
  private readonly _model;
4700
5376
  constructor(config?: SelfModelUpdaterConfig);
4701
5377
  attachBus(bus: CognitiveBus): void;
@@ -5275,6 +5951,19 @@ interface Reputation {
5275
5951
  negativeInteractions: number;
5276
5952
  lastInteractionTick: Tick;
5277
5953
  confidence: number;
5954
+ /**
5955
+ * Everything the mind has OBSERVED about this person, acts and silences alike.
5956
+ *
5957
+ * `interactionCount` counts acts, and only acts — a silence must never inflate
5958
+ * it or the mind remembers conversations that never happened. But confidence is
5959
+ * about how much evidence a read rests on, and a silence is evidence. Counting
5960
+ * only acts meant someone who never answers could never be confidently known as
5961
+ * someone who never answers, which is precisely the read worth holding.
5962
+ *
5963
+ * Rises in lockstep with `interactionCount` on the interaction path, so a mind
5964
+ * that has only ever been spoken to behaves exactly as it did before.
5965
+ */
5966
+ observations: number;
5278
5967
  }
5279
5968
  declare class ReputationTracker implements SimulationEngine, CognitiveEngine {
5280
5969
  readonly name = "reputation-tracker";
@@ -5286,6 +5975,8 @@ declare class ReputationTracker implements SimulationEngine, CognitiveEngine {
5286
5975
  /** True after reputations have been rehydrated from persisted state on first tick. */
5287
5976
  private _restored;
5288
5977
  private _pendingInteractions;
5978
+ /** Whether someone answered when the mind spoke to them (ReafferenceEngine). */
5979
+ private _pendingResponsiveness;
5289
5980
  private _bus;
5290
5981
  private readonly _model;
5291
5982
  constructor(config?: ReputationTrackerConfig);
@@ -5307,6 +5998,36 @@ declare class ReputationTracker implements SimulationEngine, CognitiveEngine {
5307
5998
  private _prune;
5308
5999
  }
5309
6000
 
6001
+ /**
6002
+ * A way this referent has been reachable, and what happened there.
6003
+ *
6004
+ * `kind` is the one fact that decides whether a room is the right place for a
6005
+ * given utterance, and it was being computed at the Discord edge (`isDM`) and
6006
+ * discarded before the mind could see it.
6007
+ *
6008
+ * `lastAnsweredTick` is evidence, not configuration — it arrives free from the
6009
+ * `social.responsiveness` signal. It is what lets the mind prefer the DM because
6010
+ * that is where this person actually answers, rather than because a constant in
6011
+ * the code says DMs rank higher.
6012
+ */
6013
+ interface Handle {
6014
+ /** The transport address — what a channel bridge can actually deliver to. */
6015
+ keid: string;
6016
+ /**
6017
+ * 'dm' — a private thread. 'room' — somewhere others are listening. Left open
6018
+ * for a non-social referent, where the meaningful distinction is a different one.
6019
+ */
6020
+ kind: 'dm' | 'room' | 'unknown';
6021
+ /** The place this handle lives in, once places are dossiers of their own. */
6022
+ place?: string;
6023
+ /** When the mind last SAID something here. */
6024
+ lastUsedTick?: Tick;
6025
+ /** When someone last answered it here — the only evidence that this route works. */
6026
+ lastAnsweredTick?: Tick;
6027
+ /** Free-form, so a host can mark what its own vocabulary cares about. */
6028
+ tags?: string[];
6029
+ }
6030
+
5310
6031
  /**
5311
6032
  * KnownEntityTracker — the cross-modal binder, and owner of the known-entity dossier.
5312
6033
  *
@@ -5361,6 +6082,31 @@ interface KnownEntity {
5361
6082
  lastSeenTick: Tick;
5362
6083
  /** 0–1: how identified/coherent this referent is (a name + repeated encounters raise it). */
5363
6084
  resolutionConfidence: number;
6085
+ /**
6086
+ * The ways this referent has been reachable, with the circumstances.
6087
+ *
6088
+ * Distinct from an ALIAS, and the distinction is load-bearing: an alias is
6089
+ * another NAME for the same referent (a transport user id), a handle is a
6090
+ * PLACE it can be reached (a thread, a room). One person has one identity and
6091
+ * several rooms, and conflating them is what made "which room should I say
6092
+ * this in?" unaskable.
6093
+ */
6094
+ handles: Handle[];
6095
+ /**
6096
+ * Referents this one MIGHT be the same someone as, unresolved.
6097
+ *
6098
+ * A blocked merge used to vanish. `_recognise` will only absorb a THIN handle
6099
+ * into an established relationship — rightly, because fusing two real people
6100
+ * who share a name is the dangerous direction — so once the same human was
6101
+ * well-established on two channels they stayed two people permanently, and
6102
+ * nothing anywhere recorded the near-miss.
6103
+ *
6104
+ * A person does not silently fail here. They NOTICE — "hang on, is this the
6105
+ * same Mara?" — and then resolve it by asking. So the doubt is kept, shown, and
6106
+ * left for the mind to settle. Deliberately not a merge: this is a question,
6107
+ * and the answer is the mind's to give.
6108
+ */
6109
+ suspectedSameAs?: string[];
5364
6110
  }
5365
6111
  declare class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
5366
6112
  readonly name = "known-entity-tracker";
@@ -5376,6 +6122,13 @@ declare class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
5376
6122
  private _pendingEncounters;
5377
6123
  private _pendingConscious;
5378
6124
  private _pendingOutcomes;
6125
+ /**
6126
+ * Aliases minted this tick, awaiting persistence. `_getOrCreate` runs deep inside
6127
+ * the drain loops with no access to the command list, and an alias that lives
6128
+ * only in memory is one the mind forgets on restart — every transport address
6129
+ * would mint a SECOND anchor next boot and the person would fork in two.
6130
+ */
6131
+ private _mintedAliases;
5379
6132
  private _bus;
5380
6133
  private readonly _model;
5381
6134
  constructor(config?: KnownEntityTrackerConfig);
@@ -5392,7 +6145,31 @@ declare class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
5392
6145
  snapshot(): Record<string, unknown>;
5393
6146
  react(_delta: Duration, tick: Tick, state: ReadonlySimulationState, _ctx: SimulationContext): Promise<EngineResult>;
5394
6147
  /** The dossier for a referent, if the Will has one. */
6148
+ /**
6149
+ * The dossier for anything that names this referent — its anchor, or any address
6150
+ * it has been met at.
6151
+ *
6152
+ * Alias-aware because a caller has no business knowing the anchor. This read
6153
+ * `_dossiers.get(keid)` raw, so the moment addresses became aliases of a minted
6154
+ * anchor, every caller holding a transport id got `undefined` — the mind would
6155
+ * have looked up someone it knows perfectly well and found a stranger. The
6156
+ * existing Phase-4 tests caught it, which is exactly what they are for.
6157
+ */
5395
6158
  getDossier(keid: string): KnownEntity | undefined;
6159
+ /**
6160
+ * Absorb `alias` into `canon` — one someone where there were two.
6161
+ *
6162
+ * Shared by the recognition heuristic and by the mind's own `sameAs` verdict,
6163
+ * because "these are the same person" must mean the same thing whichever
6164
+ * concluded it. The only difference is who is ENTITLED to conclude it: the
6165
+ * heuristic will not absorb an established relationship, the mind may, because
6166
+ * it has reasons a name-match does not — usually that somebody just told it.
6167
+ *
6168
+ * Routes MOVE. This used to delete the absorbed dossier and keep only a
6169
+ * redirect, so the mind concluded "same person" and in the same breath threw
6170
+ * away the second way to reach them.
6171
+ */
6172
+ private _fuse;
5396
6173
  /** Resolution confidence: a learned name plus repeated encounters identify a referent. */
5397
6174
  private _resolution;
5398
6175
  /**
@@ -5400,12 +6177,51 @@ declare class KnownEntityTracker implements SimulationEngine, CognitiveEngine {
5400
6177
  * true if any merge happened. Pure + deterministic.
5401
6178
  */
5402
6179
  private _recognise;
6180
+ /**
6181
+ * The dossier for a referent, minting its anchor the first time it is met.
6182
+ *
6183
+ * A transport address arriving here (`discord:1019…`) is not an identity, it is
6184
+ * a way the world happened to name someone. So it becomes an ALIAS of a fresh
6185
+ * `ke:` anchor, and the dossier lives under the anchor. Everything downstream —
6186
+ * reputation, theory-of-mind, attachment, goals, the PMA — keeps working
6187
+ * untouched: it still sees one opaque string per referent, which is simply no
6188
+ * longer a route. The same human met later on another channel resolves to this
6189
+ * same anchor instead of becoming a second person nobody could connect.
6190
+ */
5403
6191
  private _getOrCreate;
5404
6192
  /** Keep the most-familiar dossiers; absence-faded acquaintances fall away (forgetting). */
6193
+ /**
6194
+ * Forget the least-held referents when over capacity.
6195
+ *
6196
+ * Ranked by more than exposure, deliberately. This sorted on `familiarity`
6197
+ * alone, which is MERE EXPOSURE — and now that a referent need not be a person
6198
+ * (a document, a repo, a room), things get far more exposure than people do. A
6199
+ * mind that touched sixty files would have evicted a colleague it speaks to
6200
+ * weekly in favour of a config file it opened a lot, silently, taking that
6201
+ * person's reputation, theory-of-mind model and attachment bond with it.
6202
+ *
6203
+ * So a referent the mind has actually got to know is stickier than one it has
6204
+ * merely seen often: knowing their NAME is the single strongest signal (it is
6205
+ * what distinguishes a someone from a blip), then how resolved the referent is,
6206
+ * then exposure. Nothing here is about being a person — a named, well-resolved
6207
+ * document outranks a glimpsed stranger, which is correct.
6208
+ */
5405
6209
  private _prune;
5406
6210
  private _restoreFromState;
5407
6211
  }
5408
6212
 
6213
+ /**
6214
+ * Referent → a deliverable address, and the room to use when none was chosen.
6215
+ *
6216
+ * Returns null when the referent is already an address (nothing to translate) or
6217
+ * when the mind holds no route at all — in which case the row goes out as-is and
6218
+ * the bridge's own roster fallback still applies, so a message is never silently
6219
+ * dropped for want of a handle.
6220
+ */
6221
+ type OutboxRouting = (targetEntityId: string, chosenThread: string | undefined) => {
6222
+ targetEntityId: string;
6223
+ threadId?: string;
6224
+ } | null;
5409
6225
  /** The caller-supplied fields of an outbox row; the writer stamps id + defaults. */
5410
6226
  interface OutboxRow {
5411
6227
  targetEntityId: string;
@@ -5427,11 +6243,24 @@ declare class OutboxWriter {
5427
6243
  * which made the embedded ids in `conversation.sent` diverge every run).
5428
6244
  */
5429
6245
  private _seq;
6246
+ private _routing;
5430
6247
  constructor(opts?: {
5431
6248
  outbox?: OutboxMessage[];
5432
6249
  willId?: string;
5433
6250
  });
5434
6251
  attachSessionLogger(logger: SessionLogger | null): void;
6252
+ /**
6253
+ * Turn a referent into somewhere the world can actually be spoken to.
6254
+ *
6255
+ * Injected rather than read here, because this writer is deliberately dumb —
6256
+ * it holds no state and must stay replay-safe. Assembly closes over the state
6257
+ * manager (the same shape as `attachMemorySink`).
6258
+ *
6259
+ * This is the ONE seam both send paths cross: ProactiveCommunicator's
6260
+ * `enqueue()` and AuditionEngine's `enqueueReply()`. Translating anywhere else
6261
+ * would mean doing it twice and getting it wrong once.
6262
+ */
6263
+ attachRouting(resolve: OutboxRouting | null): void;
5435
6264
  private _genId;
5436
6265
  /**
5437
6266
  * Push one canonical outbox row and return its generated id. The single point
@@ -5533,6 +6362,18 @@ interface TextMessage {
5533
6362
  content: string;
5534
6363
  /** Display name — used in the facet focus content. */
5535
6364
  speakerName?: string;
6365
+ /**
6366
+ * True when `threadId` is a PRIVATE thread — this someone and the mind, nobody
6367
+ * else listening. The single fact that decides whether a room is the right
6368
+ * place for a given utterance, and the Discord edge has always computed it
6369
+ * (`isDM`) and discarded it before the mind could see it: a follow-up promised
6370
+ * in a DM went out to a public channel, because the roster's "where did I last
6371
+ * see them" is a different question from "where did I promise this".
6372
+ *
6373
+ * Undefined means the channel did not say, which is honestly different from
6374
+ * false — an unknown room is not known to be public.
6375
+ */
6376
+ direct?: boolean;
5536
6377
  }
5537
6378
  interface VoiceChunk {
5538
6379
  kind: 'voice';
@@ -5716,6 +6557,12 @@ declare class AuditionEngine extends BaseSenseEngine {
5716
6557
  * other percept uses. Wired to `stateManager.setEntity` in assembleMind().
5717
6558
  */
5718
6559
  private _memorySink;
6560
+ /**
6561
+ * Sim tick of the most recent facet decision — the only deterministic clock this
6562
+ * off-tick engine has. Stamped from `FacetDecision.tick`, and used to key the
6563
+ * conversation records it writes into state.
6564
+ */
6565
+ private _lastDecisionTick;
5719
6566
  /** Speaker attachment strength accessor (0–1) — weights salience by relationship. */
5720
6567
  private _getAttachmentScore;
5721
6568
  /** Active-goal topic text accessor — for salience topic-overlap. */
@@ -5724,6 +6571,8 @@ declare class AuditionEngine extends BaseSenseEngine {
5724
6571
  private _inflightInbound;
5725
6572
  /** In-flight thread per entity — stamps chunk envelopes with the current threadId. */
5726
6573
  private _inflightThread;
6574
+ /** Targets with an outreach being composed right now — see authorOutreach. */
6575
+ private _outreachInFlight;
5727
6576
  attachExecutiveEngine(exec: ExecutiveEngine): void;
5728
6577
  /**
5729
6578
  * Inject the OutboxWriter so reply bubbles are delivered through the canonical
@@ -5767,7 +6616,7 @@ declare class AuditionEngine extends BaseSenseEngine {
5767
6616
  attachAttachmentScore(fn: (entityId: string) => number): void;
5768
6617
  /** Inject an active-goal topic-text accessor (reads GoalManager) for salience overlap. */
5769
6618
  attachActiveGoalText(fn: () => string[]): void;
5770
- /** Override: audition adds the master-escalation signal to the base percept schema. */
6619
+ /** Override: audition adds the facet→master handoff to the base percept schema. */
5771
6620
  publishes(): CognitiveEventSchema[];
5772
6621
  snapshot(): Record<string, unknown>;
5773
6622
  /**
@@ -5836,6 +6685,52 @@ declare class AuditionEngine extends BaseSenseEngine {
5836
6685
  * (Section 1.2) routes ingest through the tick loop. The entity carries no
5837
6686
  * wall-clock timestamp — `setEntity` stamps createdAt/tick from the sim clock.
5838
6687
  */
6688
+ /**
6689
+ * The inbound as a social signal in state — mirror of `conversation.sent`.
6690
+ *
6691
+ * Shaped for `SocialPerception._scanSocialSignals`, which reads `sourceKeid` for
6692
+ * who acted and `directedAtSelf` for whether it was aimed at us. Valence is left
6693
+ * UNSET on purpose: the words have not been appraised yet, and guessing a number
6694
+ * here would feed reputation and affect a sentiment nobody measured. Absent, the
6695
+ * scanner falls back to its neutral default, so the Will learns *that* someone
6696
+ * engaged (familiarity, recency, reliability) without inventing how it felt.
6697
+ */
6698
+ /**
6699
+ * A durable, deterministic id for a conversation record.
6700
+ *
6701
+ * `<prefix>-<entity>-<tick>-<hash of the words>`. Every part earns its place:
6702
+ * • entity — whose conversation this is;
6703
+ * • tick — WHEN, from the sim clock, which resumes from the snapshot and so
6704
+ * keeps rising across restarts;
6705
+ * • hash — which utterance, so two things said to one person on one tick stay
6706
+ * two records.
6707
+ *
6708
+ * What it replaces was `<prefix>-<entity>-<N>` with N a process-local counter.
6709
+ * It restarted at 1 on every boot, so each session OVERWROTE the previous
6710
+ * session's records of the same person — a mind that had spoken with someone
6711
+ * across four restarts held one session's worth of evidence that it ever had.
6712
+ * Found by diffing a live snapshot against the Discord transcript it came from:
6713
+ * `conv-sent-reply-discord:1019…-1` held that morning's greeting, and every
6714
+ * earlier conversation keyed to the same id was simply gone.
6715
+ *
6716
+ * No wallClock: these ids live in state, and a wall-clock id makes the recorded
6717
+ * and replayed runs diverge (R2).
6718
+ */
6719
+ private _sentKey;
6720
+ private _writeReceived;
6721
+ /**
6722
+ * Record that the mind SPOKE to someone, mirroring `_writeReceived`.
6723
+ *
6724
+ * Only ProactiveCommunicator wrote `conversation.sent`, so a reply — which is
6725
+ * most of what a Will says — left no durable trace of having spoken. Everything
6726
+ * that asks "have I already said something to them?" was therefore blind to
6727
+ * conversation: satiation could not damp repeating a relay delivered as a reply,
6728
+ * and an undertaking discharged inside a conversation stayed forever unkept,
6729
+ * which is exactly how the same message went out again and again.
6730
+ *
6731
+ * Speaking is speaking, whichever path carried it.
6732
+ */
6733
+ private _writeSent;
5839
6734
  private _persistExchangeMemory;
5840
6735
  private _onFacetDecision;
5841
6736
  /**
@@ -6012,6 +6907,30 @@ interface LearnedSkill {
6012
6907
  lastEnactedTick: number;
6013
6908
  }
6014
6909
 
6910
+ /**
6911
+ * WHY this denial is final — the distinction that makes a refusal learnable
6912
+ * rather than a wall to re-probe forever. Each value selects a different
6913
+ * cognitive fate; they are not degrees of one severity.
6914
+ *
6915
+ * • 'class' — the ACTION ITSELF is never permitted. Suppress the
6916
+ * affordance hard, erase any learned envelope, and let go of
6917
+ * a commitment currently deliberating toward it.
6918
+ * • 'parameter' — the action is fine; THESE ARGUMENTS were not (bound
6919
+ * exceeded, wrong target). Narrow the envelope the Will
6920
+ * reaches for; the ability stays.
6921
+ * • 'context' — the refusal was NOT ABOUT THE ACTION at all (tainted
6922
+ * context, unavailable dependency). Touch nothing: no
6923
+ * availability delta, no envelope, no competence.
6924
+ *
6925
+ * POLICY_REAFFERENCE P5 widened this from 'class' | 'instance' after the HELM
6926
+ * joint RFC ("Denials That Teach") identified that an instance-scoped refusal
6927
+ * splits in two, and that the two halves demand opposite responses. These are
6928
+ * OUR names for the distinctions, deliberately not HELM's wire spellings — see
6929
+ * the naming-boundary note in .TODO/POLICY_REAFFERENCE.md. A provider adapter
6930
+ * translates; this interface stays vendor-neutral.
6931
+ */
6932
+ type DenialFinality = 'class' | 'parameter' | 'context';
6933
+
6015
6934
  interface OutcomeObservation {
6016
6935
  schema: string;
6017
6936
  success: boolean;
@@ -6055,11 +6974,17 @@ declare class SchemaRepertoire {
6055
6974
  availabilityOf(schema: string): number;
6056
6975
  /**
6057
6976
  * Fold a policy refusal into the availability layer (NOT competence). A
6058
- * `class` refusal cuts availability hard; an `instance` refusal dents it
6977
+ * `class` refusal cuts availability hard; a `parameter` refusal dents it
6059
6978
  * lightly. Multiplicative so repeated refusals compound toward — but never
6060
6979
  * reach — zero, keeping re-probe alive.
6980
+ *
6981
+ * `context` is EXCLUDED FROM THE SIGNATURE, not handled inside: a refusal
6982
+ * that was not about the action must never reach the availability layer at
6983
+ * all, and making that a type error rather than a convention means a future
6984
+ * caller cannot quietly re-introduce the dent. The routing decision lives in
6985
+ * the ReafferenceEngine's refused branch (P5).
6061
6986
  */
6062
- recordRefusal(schema: string, finality: 'class' | 'instance', tick: number): number;
6987
+ recordRefusal(schema: string, finality: Exclude<DenialFinality, 'context'>, tick: number): number;
6063
6988
  /**
6064
6989
  * Fold one outcome into the schema's learned skill. Returns the updated skill
6065
6990
  * and whether it just crossed the proceduralization threshold this update.
@@ -6112,6 +7037,16 @@ declare class AffordanceSynthesizer implements CognitiveEngine {
6112
7037
  private _schemas;
6113
7038
  private _skills;
6114
7039
  private _repertoire;
7040
+ /**
7041
+ * This tick's live consequence descriptors — the acts the mind has performed
7042
+ * whose outcome has not yet come back. Refreshed once at the top of react()
7043
+ * because `_build` runs per candidate and reading them is a full-entity scan.
7044
+ */
7045
+ private _inFlight;
7046
+ /** Ticks an act stays satiating (engine-config-action-selector.repeatWindowTicks). */
7047
+ private _satiationWindow;
7048
+ /** Tick of the last thing said to each entity — outlives the descriptor sweep. */
7049
+ private _spokenAt;
6115
7050
  private _bus;
6116
7051
  private _defaultCap;
6117
7052
  private _lastFieldSize;
@@ -6245,6 +7180,21 @@ declare class MotorSchemaExecutor implements CognitiveEngine {
6245
7180
  private _author;
6246
7181
  private _grants;
6247
7182
  private _bus;
7183
+ /**
7184
+ * Two-phase outreach authoring. A facet cannot be awaited from inside a tick:
7185
+ * `ExecutiveFacet.report()` only QUEUES in tick-discipline mode and the reasoning
7186
+ * launches from `pump()`, which the ExecutiveEngine calls once per tick — so an
7187
+ * in-tick `await` blocks the very loop that would produce the answer. Observed
7188
+ * live: a 61s freeze of the whole mind inside one tick, then an empty result.
7189
+ * (It passes unit tests because bare facets have no inbox and author inline.)
7190
+ *
7191
+ * So `_deliver` REQUESTS words and returns false (the intent holds 'awaiting'),
7192
+ * the facet answers off-tick, and a later tick delivers. Process-local by design:
7193
+ * an authoring call in flight cannot survive a restart, and the intent would
7194
+ * simply re-request.
7195
+ */
7196
+ private _authoring;
7197
+ private _authored;
6248
7198
  constructor(schemas?: MotorSchema[]);
6249
7199
  attachBus(bus: CognitiveBus): void;
6250
7200
  /** Resolve schemas (incl. learned composites) from the live repertoire first. */
@@ -6284,6 +7234,13 @@ declare class MotorSchemaExecutor implements CognitiveEngine {
6284
7234
  * conversation facet); this is the single enaction → delivery path.
6285
7235
  */
6286
7236
  private _deliver;
7237
+ /**
7238
+ * Ask a facet for the words, off-tick. Fire-and-forget on purpose: awaiting this
7239
+ * from inside `react()` deadlocks the tick loop against the facet pump. Idempotent
7240
+ * per intent — a request already in flight is not duplicated, so the intent may sit
7241
+ * 'awaiting' across many ticks with exactly one LLM call behind it.
7242
+ */
7243
+ private _requestAuthoring;
6287
7244
  private _emitEnacted;
6288
7245
  /**
6289
7246
  * Publish `action.outcome` for EVERY enaction — the shared metacognitive/affective
@@ -6292,6 +7249,32 @@ declare class MotorSchemaExecutor implements CognitiveEngine {
6292
7249
  * RewardEvaluator reads it as a reward signal. `confidence` carries the agency's
6293
7250
  * own forward-model prior so calibration has a real prediction to score.
6294
7251
  */
7252
+ /**
7253
+ * Retire the `ideomotor.intent` that produced this act.
7254
+ *
7255
+ * Nothing deleted these. They were cleared only when the executive next ran and
7256
+ * declined to name the same action again — and the executive runs on an interval,
7257
+ * so between cycles a willed reach-out STOOD in state, was rebuilt into an
7258
+ * affordance every single tick, and competed every single tick. Observed as
7259
+ * dozens of identical lines:
7260
+ *
7261
+ * [selector] willed reach-out → … NOT selected: 0.297 < inspect… 0.340
7262
+ *
7263
+ * losing by four thousandths, over and over, until it won — twice. Fabrice got
7264
+ * the same message byte-for-byte 25 ticks apart, two outbox ids.
7265
+ *
7266
+ * `justEnacted` was built to hold this line and cannot: it is a DECAYING
7267
+ * quantity, capped at `repeatDamping` (0.30), and a standing intent outlasts it
7268
+ * by construction. Damping a permanent pull only ever delays it. So the intention
7269
+ * is discharged by being acted on, which is what an intention is — you meant to
7270
+ * tell someone something, you told them, and it is finished. If the mind still
7271
+ * wants to say more, the next executive cycle forms a new one, now seeing "I said
7272
+ * this 25 ticks ago and have had no answer" in front of it.
7273
+ *
7274
+ * Satiation stays exactly as it was, and still earns its keep: it damps saying
7275
+ * the same thing again for reasons that did NOT come from a standing will.
7276
+ */
7277
+ private _dischargeWill;
6295
7278
  private _emitActionOutcome;
6296
7279
  private _emitDispatch;
6297
7280
  }
@@ -6308,6 +7291,35 @@ declare class ReafferenceEngine implements CognitiveEngine {
6308
7291
  onCognitiveEvent(e: CognitiveEvent): void;
6309
7292
  snapshot(): Record<string, unknown>;
6310
7293
  react(_delta: Duration, tick: Tick, state: ReadonlySimulationState, _context: SimulationContext): Promise<EngineResult>;
7294
+ /**
7295
+ * Latch each open turn's fate onto its own record and announce it once.
7296
+ *
7297
+ * Two rules earn their keep here:
7298
+ *
7299
+ * • MERGE, never replace. `StateManager.setEntity` overwrites the whole entity,
7300
+ * and a `conversation.sent` carries `outboxMessageIds` — the sole key by which
7301
+ * a later delivery ack can find it. Rewriting the record with only the fields
7302
+ * this method cares about would sever that, silently, for every turn that got
7303
+ * an answer.
7304
+ *
7305
+ * • Latch, don't recompute. `answeredAt`/`unansweredAt` persist, so the event
7306
+ * fires on the one tick the fact changed rather than every tick for the rest
7307
+ * of the session — which for an unanswered turn would be thousands of
7308
+ * identical reputation hits against one person for one silence.
7309
+ */
7310
+ private _resolveReplies;
7311
+ /**
7312
+ * Publish how a person responded to being spoken to.
7313
+ *
7314
+ * Deliberately NOT an `interaction.occurred`: that event means "someone did
7315
+ * something toward us" and carries a valence for what they did. A silence is
7316
+ * nobody doing anything, and forcing it through that channel would have the
7317
+ * ReputationTracker book a hostile *act* where there was only an absence — the
7318
+ * mind would come to think it was being rebuffed rather than simply not
7319
+ * answered yet. Separate signal, separate meaning, one consumer decides what
7320
+ * either is worth.
7321
+ */
7322
+ private _emitResponsiveness;
6311
7323
  private _emitProceduralized;
6312
7324
  /**
6313
7325
  * Emit the `action.outcome{planId,stepId}` for an async (host-acked) plan-step
@@ -6339,6 +7351,12 @@ type EngineRegistry = {
6339
7351
  affectiveBlender: AffectiveBlender;
6340
7352
  workingMemory: WorkingMemory;
6341
7353
  episodicConsolidator: EpisodicConsolidator;
7354
+ /**
7355
+ * The vector index, when semantic recall is configured. Exposed so shutdown can
7356
+ * FLUSH it: it lives outside the state snapshot and previously persisted only from
7357
+ * a debounce timer no shutdown path awaited, so it died with the process.
7358
+ */
7359
+ vectorMemory: VectorMemoryAdapter | null;
6342
7360
  semanticIntegrator: SemanticIntegrator;
6343
7361
  spacedRepetition: SpacedRepetition;
6344
7362
  forgettingCurve: ForgettingCurve;
@@ -6645,12 +7663,53 @@ interface WillModelConfig {
6645
7663
  * `apiKey` is held in memory only — it is never mirrored into state entities,
6646
7664
  * session logs, or the PMA.
6647
7665
  */
7666
+ interface WillProviderConfig {
7667
+ /** Credential for this provider. Held in memory only — never state/logs/PMA. */
7668
+ apiKey?: string;
7669
+ /** Base URL override — self-hosted or OpenAI-compatible endpoints. */
7670
+ baseUrl?: string;
7671
+ /**
7672
+ * USD per 1M tokens, keyed by model id. Host-owned on purpose: prices change
7673
+ * on a vendor's schedule, differ per account, and are ~0 self-hosted, so they
7674
+ * cannot be tracked from inside an npm release. These win over the engine's
7675
+ * built-in fallback table.
7676
+ *
7677
+ * Cost is telemetry only — it never enters simulation state — so changing a
7678
+ * price can never change what a mind does or break a replay.
7679
+ */
7680
+ prices?: PriceTable;
7681
+ }
6648
7682
  interface WillLLMConfig {
6649
7683
  provider?: LLMProvider;
6650
7684
  apiKey?: string;
6651
7685
  baseUrl?: string;
6652
7686
  maxOutputTokens?: number;
6653
7687
  timeoutMs?: number;
7688
+ /**
7689
+ * Everything the host knows about each provider — credential, endpoint, and
7690
+ * prices — declared once per provider. The single-provider fields above stay
7691
+ * the simple path; this map is for hosts reaching more than one.
7692
+ */
7693
+ providers?: Partial<Record<LLMProvider, WillProviderConfig>>;
7694
+ /**
7695
+ * Per-call model selection. Omitted (or NULL_ROUTER) means every call uses
7696
+ * `model` above, exactly as before the seam existed.
7697
+ *
7698
+ * The router sees only the call's attribution — what kind of work it is and
7699
+ * how much the moment demands — never who is paying or what anything costs.
7700
+ * Routes name providers from the `providers` map above; a route to a provider
7701
+ * with no credential falls back to the default rather than failing the call.
7702
+ */
7703
+ router?: ModelRouter | null;
7704
+ /**
7705
+ * Concrete LLM model id(s) for this Will — a single id for every role, or a
7706
+ * per-role map. An explicit WILL_LLM_MODEL env pins the thinking roles
7707
+ * (operator single-model deployments); unset roles fall back to `executive`,
7708
+ * then the LLMDirector's built-in default. Product-level labels (pricing
7709
+ * tiers, model families) live host-side and resolve to concrete ids BEFORE
7710
+ * reaching the engine.
7711
+ */
7712
+ model?: string | WillModelConfig;
6654
7713
  }
6655
7714
  interface WillIdentity {
6656
7715
  /**
@@ -6691,17 +7750,26 @@ interface WillConfig {
6691
7750
  profile?: string | null;
6692
7751
  /** Persona definition seeded into the will.identity entity. */
6693
7752
  identity: WillIdentity;
6694
- /** Anatomy — 'mind' (default) or the no-LLM 'reflex' shell. */
6695
- anatomy?: Anatomy;
6696
7753
  /**
6697
- * Concrete LLM model id(s) for this Will a single id for every role, or a
6698
- * per-role map. An explicit WILL_LLM_MODEL env pins the thinking roles
6699
- * (operator single-model deployments); unset roles fall back to `executive`,
6700
- * then the LLMDirector's built-in default. Product-level labels (pricing
6701
- * tiers, model families) live host-side and resolve to concrete ids BEFORE
6702
- * reaching the engine.
7754
+ * This config's `identity` is a PLACEHOLDERthe real one arrives from a PMA
7755
+ * artifact moments later, on the same boot.
7756
+ *
7757
+ * Set by `Will.wake`, which passes `{ prompt: '' }` because a woken mind's
7758
+ * persona belongs to its artifact, not to the caller. Without this flag the
7759
+ * creation-time identity guard inspected that placeholder and warned, on every
7760
+ * single wake, that "identity.values is empty", "identity.style is generic" and
7761
+ * "identity is shallow (strength 0)" — three alarms about a config nobody
7762
+ * intended to use, fired before the real identity had loaded.
7763
+ *
7764
+ * It suppresses only the WARNINGS. Errors still throw (an over-long or
7765
+ * malformed prompt is a hard failure whenever it appears), and the artifact's
7766
+ * OWN identity is fully guarded at the load boundary by PMAController.load,
7767
+ * which is the honest place to ask whether this mind's persona is thin — it is
7768
+ * the only point where the answer is knowable.
6703
7769
  */
6704
- model?: string | WillModelConfig;
7770
+ identityFromArtifact?: boolean;
7771
+ /** Anatomy — 'mind' (default) or the no-LLM 'reflex' shell. */
7772
+ anatomy?: Anatomy;
6705
7773
  /**
6706
7774
  * Per-Will LLM transport overrides (provider, BYO apiKey, baseUrl, output
6707
7775
  * cap, timeout). Unset fields fall back to WILL_LLM_* envs. The apiKey never
@@ -6735,6 +7803,21 @@ interface WillConfig {
6735
7803
  * Plan-enforced floor for executiveInterval — the customer cannot go faster.
6736
7804
  */
6737
7805
  minExecutiveInterval?: number;
7806
+ /**
7807
+ * Enable the DeliberationCache — a learned fast path that composes an executive
7808
+ * output from highly-similar, highly-competent precedent instead of calling the
7809
+ * LLM. Off unless asked for.
7810
+ *
7811
+ * OFF BY DEFAULT ON PURPOSE, and the default is the interesting part: this
7812
+ * changes how a mind THINKS, not how fast it runs. A cache hit means the mind
7813
+ * acted from precedent without deliberating, which is a real thing minds do and
7814
+ * a real thing an operator must opt into for a specific Will — not something a
7815
+ * dependency bump should switch on underneath one that is already living.
7816
+ *
7817
+ * Pass `true` for the built-in conservative settings, or a config object to tune
7818
+ * the retrieval/competence parameters (see cognition/cache/types).
7819
+ */
7820
+ deliberationCache?: boolean | DeliberationCacheConfig;
6738
7821
  /**
6739
7822
  * Goals seeded before the first tick. If omitted or empty, the Will starts
6740
7823
  * goalless — the executive engine will generate context-appropriate goals on its
@@ -7465,7 +8548,7 @@ interface WillSummary {
7465
8548
  createdAt: Date;
7466
8549
  lastTickAt: Date | null;
7467
8550
  anatomy: WillConfig['anatomy'];
7468
- model: WillConfig['model'];
8551
+ model: NonNullable<WillConfig['llm']>['model'];
7469
8552
  }
7470
8553
 
7471
8554
  interface WillInstance {
@@ -7828,6 +8911,8 @@ interface Stimulus {
7828
8911
  speaker?: string;
7829
8912
  /** Conversation/thread id (default = `from`). */
7830
8913
  thread?: string;
8914
+ /** True when `thread` is private — just this someone and the Will. See TextMessage.direct. */
8915
+ direct?: boolean;
7831
8916
  }
7832
8917
  /** A message the Will emitted to someone. */
7833
8918
  interface WillMessage {
@@ -7837,6 +8922,20 @@ interface WillMessage {
7837
8922
  content: string;
7838
8923
  /** Entity id the Will addressed (the speaker you used in say()/tell(), or a bond). */
7839
8924
  to: string;
8925
+ /**
8926
+ * The conversation this belongs to — the `thread` from the `perceive()` that
8927
+ * prompted it. Absent when the Will spoke unprompted, which genuinely has no
8928
+ * thread.
8929
+ *
8930
+ * WHERE, not just to whom. The engine knew this the whole way down —
8931
+ * `OutboxMessage.threadId` carries it — and the projection dropped it here, so
8932
+ * a channel adapter had nothing to answer INTO and had to guess from a roster.
8933
+ * Observed live: a DM arrived on `discord:1532693…`, she answered it correctly
8934
+ * and in seconds, and the reply went to the shared server channel because that
8935
+ * was the last room the roster had seen this person in. From the operator's
8936
+ * side she had simply ignored him.
8937
+ */
8938
+ thread?: string;
7840
8939
  }
7841
8940
  /**
7842
8941
  * A motor act the Will *chose* to enact — a projection of its agency, surfaced
@@ -7941,20 +9040,42 @@ interface CreateWillOptions {
7941
9040
  anatomy?: Anatomy;
7942
9041
  /** Concrete LLM model id, or a per-role map ({ executive, summarizer?,
7943
9042
  * deliberation?, embedding? } — unset thinking roles fall back to executive).
7944
- * Unset → env / provider default. */
9043
+ * Unset → env / provider default.
9044
+ * @deprecated Pass `llmConfig: { model }` instead — model and transport are
9045
+ * one concern. Still honoured; an explicit `llmConfig.model` wins. */
7945
9046
  model?: string | WillModelConfig;
7946
- /** Per-Will LLM transport overrides (provider, BYO apiKey, baseUrl, caps).
9047
+ /** Per-Will LLM config: provider, model(s), BYO apiKey, baseUrl, caps.
7947
9048
  * Unset fields fall back to WILL_LLM_* envs. apiKey stays in memory only.
7948
- * (Named llmConfig because `llm` is the mock/anthropic MODE switch.) */
9049
+ * (Named llmConfig because `llm` is the provider MODE switch.) */
7949
9050
  llmConfig?: WillLLMConfig;
7950
9051
  /**
7951
- * LLM mode. 'mock' (default when no key is present) runs a deterministic
7952
- * canned executive — zero keys, zero cost. 'anthropic' calls Claude (needs
7953
- * ANTHROPIC_API_KEY / WILL_LLM_* env); 'glm' calls Z.ai's GLM over its
7954
- * Anthropic-compatible endpoint (needs ZAI_API_KEY / WILL_LLM_*). Omit to
7955
- * auto-detect from whichever key is set.
9052
+ * LLM mode which provider the executive speaks to.
9053
+ *
9054
+ * 'mock' (the default when no key is present) runs a deterministic canned
9055
+ * executive: zero keys, zero cost. Every other value names a provider and
9056
+ * needs its key, either the provider's own env below or the
9057
+ * provider-agnostic WILL_LLM_API_KEY:
9058
+ *
9059
+ * anthropic ANTHROPIC_API_KEY Claude, native Messages wire
9060
+ * glm ZAI_API_KEY Z.ai GLM, Anthropic-compatible wire
9061
+ * openai OPENAI_API_KEY OpenAI wire
9062
+ * google GOOGLE_API_KEY | GEMINI_API_KEY native Gemini wire
9063
+ * deepseek DEEPSEEK_API_KEY OpenAI wire
9064
+ * moonshot MOONSHOT_API_KEY Kimi — OpenAI wire
9065
+ * qwen DASHSCOPE_API_KEY Alibaba Model Studio — OpenAI wire
9066
+ * xai XAI_API_KEY Grok — OpenAI wire
9067
+ * minimax MINIMAX_API_KEY OpenAI wire
9068
+ * mistral MISTRAL_API_KEY OpenAI wire
9069
+ * ollama · vllm local; no key, set `llm` explicitly
9070
+ *
9071
+ * Any other string works too — it just has to declare its `wire` and
9072
+ * `baseUrl` on `llmConfig.providers`. Naming the vendor rather than
9073
+ * borrowing `openai` because it speaks that wire is what keeps the
9074
+ * completion tape and the cost breakdown honest.
9075
+ *
9076
+ * Omit to auto-detect from whichever key is set.
7956
9077
  */
7957
- llm?: 'mock' | 'anthropic' | 'glm';
9078
+ llm?: 'mock' | LLMProvider;
7958
9079
  /**
7959
9080
  * Abilities the Will can choose to enact. `name → handler`, or
7960
9081
  * `name → { handler, description?, cost?, valence?, preconditions? }` to seed
@@ -8077,4 +9198,4 @@ declare class Will {
8077
9198
  private _emitError;
8078
9199
  }
8079
9200
 
8080
- export { ConfidenceCalibrator as $, type AckResult as A, type AsyncEngineConfig as B, type ConflictReport as C, AttachmentEvaluator as D, type EffectorHandler as E, type AttachmentEvaluatorConfig as F, AttentionAllocator as G, type AttentionAllocatorConfig as H, type InboundEnvelope as I, AuditionEngine as J, AutobiographicalNarrator as K, type AutobiographicalNarratorConfig as L, type BehavioralProbeResult as M, BiasDetector as N, type OutboundEnvelope as O, type BiasDetectorConfig as P, BunStorageAdapter as Q, type RestoreOptions as R, type SimulationContext as S, type TransportStatus as T, type ChunkEnvelope as U, type CircadianConfig as V, Will as W, CircadianOscillator as X, type ClockConfig as Y, type Cognition as Z, type CognitiveHealth as _, type ExternalTransport as a, type LossEvaluatorConfig as a$, type ConfidenceCalibratorConfig as a0, type Coordinates as a1, type CreateWillOptions as a2, DefaultEventBus as a3, DefaultMetricCollector as a4, DefaultOrchestrator as a5, DefaultReplayRecorder as a6, DefaultReplaySession as a7, DefaultScenario as a8, DefaultSerializer as a9, type EventFilter as aA, type EventHandler as aB, type EventPayload as aC, ExecutiveEngine as aD, type ExecutiveEngineConfig$1 as aE, Exteroception as aF, type ExteroceptionConfig as aG, ForgettingCurve as aH, type ForgettingCurveConfig as aI, FrustrationEvaluator as aJ, type FrustrationEvaluatorConfig as aK, GoalManager as aL, type GoalManagerConfig as aM, GustationEngine as aN, type InboundMessageEnvelope as aO, type InboundPerceptEnvelope as aP, InhibitionController as aQ, type InhibitionControllerConfig as aR, Interoception as aS, type InteroceptionConfig as aT, IntrospectionEngine as aU, type IntrospectionEngineConfig as aV, KnownEntityTracker as aW, type KnownEntityTrackerConfig as aX, type LLMCompletionRecord as aY, type LLMCompletionSink as aZ, LossEvaluator as a_, DefaultSimulation as aa, DefaultSimulationClock as ab, DefaultStateManager as ac, DefaultVectorMemoryAdapter as ad, DeliberationEngine as ae, DeltaEncoder as af, type DeltaSnapshot as ag, DreamSimulator as ah, type DreamSimulatorConfig as ai, type Duration as aj, type EffectorDeclaration as ak, type EffectorEntry as al, type EffectorResult as am, type EffectorSpec as an, type EmbeddingProvider as ao, EmpathySimulator as ap, type EmpathySimulatorConfig as aq, EnergyRegulator as ar, type EnergyRegulatorConfig as as, type EngineRegistry as at, type EngineResult as au, type Envelope as av, EpisodicConsolidator as aw, type EpisodicConsolidatorConfig as ax, type EventBus as ay, type EventBusConfig as az, type SeededPRNG as b, type SimulationEngine as b$, type MessageEnvelope as b0, type MetricCollector as b1, type MetricPoint as b2, type MinimalContext as b3, MockEmbedder as b4, MoralEvaluator as b5, type MoralEvaluatorConfig as b6, MotorSchemaExecutor as b7, NoveltyDetector as b8, type NoveltyDetectorConfig as b9, type ReplayMetadata as bA, type ReplayRecord as bB, type ReplayRecorder as bC, type ReplaySession as bD, type ReplyEnvelope as bE, ReputationTracker as bF, type ReputationTrackerConfig as bG, RewardEvaluator as bH, type RewardEvaluatorConfig as bI, type Scenario as bJ, type ScenarioConfig as bK, type ScenarioValidationResult as bL, type SchemaPrecondition as bM, SelfModelUpdater as bN, type SelfModelUpdaterConfig as bO, SemanticIntegrator as bP, type SemanticIntegratorConfig as bQ, type SensoryInput as bR, type SerializationConfig as bS, type SerializationFormat as bT, type SerializedEntity as bU, type SerializedState as bV, type Serializer as bW, type SessionLogEnvelope as bX, type Simulation as bY, type SimulationClock as bZ, type SimulationConfig as b_, OlfactionEngine as ba, OpenAICompatibleEmbedder as bb, type Orchestrator as bc, type OrchestratorConfig as bd, type OutboxMessage as be, type PMABehavioral as bf, type PMABelief as bg, type PMAEmotionalBaseline as bh, PMAEvalHarness as bi, type PMAGoal as bj, type PMAIdentity as bk, type PMAProbe as bl, type PMASnapshot as bm, type PerceptEnvelope as bn, PersonaConsolidator as bo, type PersonaConsolidatorConfig as bp, PlanningEngine as bq, type PlanningEngineConfig as br, ReafferenceEngine as bs, type ReconstructionFidelityReport as bt, type ReconstructionFidelityScores as bu, type RecordUsageInput as bv, type ReplayComparison as bw, type ReplayConfig as bx, type ReplayDifference as by, ReplayManager as bz, type SimulationEntity as c, type SimulationEventBase as c0, type SimulationEventListener as c1, type SleepPressureConfig as c2, SleepPressureRegulator as c3, SocialPerception as c4, type SocialPerceptionConfig as c5, SomatosensationEngine as c6, SpacedRepetition as c7, type SpacedRepetitionConfig as c8, type StateSnapshot as c9, type WillConfig as cA, type WillEffectorAct as cB, type WillInstance as cC, type WillMessage as cD, type WillStateSummary as cE, type WillStatus as cF, WillStem as cG, type WillSummary as cH, WorkingMemory as cI, type WorkingMemoryConfig as cJ, type WorldEntity as cK, type WorldInterface as cL, assembleMind as cM, clearCompletionRecorder as cN, type effectorInvocation as cO, type effectorInvocationEnvelope as cP, getCompletionRecorder as cQ, resolvePricing as cR, setCompletionRecorder as cS, type Stimulus as ca, type StorageAdapter as cb, StressRegulator as cc, type StressRegulatorConfig as cd, TaskSwitcher as ce, type TaskSwitcherConfig as cf, type TextMessage as cg, TheoryOfMind as ch, type TheoryOfMindConfig as ci, ThreatEvaluator as cj, type ThreatEvaluatorConfig as ck, type TickListener as cl, type TokenLedgerRecord as cm, type TokenReportEnvelope as cn, TokenTracker as co, type TokenTrackerConfig as cp, type TokenUsage as cq, type VectorIndex as cr, type VectorMemoryAdapter as cs, type VectorMemoryConfig as ct, type VectorQueryFilter as cu, type VectorQueryResult as cv, type VectorRecord as cw, VisionEngine as cx, type VoiceChunk as cy, type WillAffect as cz, type Timestamp as d, type SimulationEvent as e, type StateManager as f, type Tick as g, type SimulationState as h, type StateCommands as i, type ReasoningFootprint as j, type ReadonlySimulationState as k, type ConflictStrategy as l, type ConflictResolution as m, type AckEnvelope as n, type ActionRequest as o, type ActionResult as p, ActionSelector as q, type ActivityEnvelope as r, type ActivityEvent as s, type ActivityEventHandler as t, AestheticEvaluator as u, type AestheticEvaluatorConfig as v, AffectiveBlender as w, type AffectiveBlenderConfig as x, AffordanceSynthesizer as y, AsyncEngine as z };
9201
+ export { type CognitiveHealth as $, type AckResult as A, type AsyncEngineConfig as B, type ConflictReport as C, AttachmentEvaluator as D, type EffectorHandler as E, type AttachmentEvaluatorConfig as F, AttentionAllocator as G, type AttentionAllocatorConfig as H, type InboundEnvelope as I, AuditionEngine as J, AutobiographicalNarrator as K, type AutobiographicalNarratorConfig as L, BACKGROUND_DEMAND as M, type BehavioralProbeResult as N, type OutboundEnvelope as O, BiasDetector as P, type BiasDetectorConfig as Q, type RestoreOptions as R, type SimulationContext as S, type TransportStatus as T, BunStorageAdapter as U, type ChunkEnvelope as V, Will as W, type CircadianConfig as X, CircadianOscillator as Y, type ClockConfig as Z, type Cognition as _, type ExternalTransport as a, type KnownProvider as a$, ConfidenceCalibrator as a0, type ConfidenceCalibratorConfig as a1, type Coordinates as a2, type CreateWillOptions as a3, DefaultEventBus as a4, DefaultMetricCollector as a5, DefaultOrchestrator as a6, DefaultReplayRecorder as a7, DefaultReplaySession as a8, DefaultScenario as a9, type EventBus as aA, type EventBusConfig as aB, type EventFilter as aC, type EventHandler as aD, type EventPayload as aE, ExecutiveEngine as aF, type ExecutiveEngineConfig$1 as aG, Exteroception as aH, type ExteroceptionConfig as aI, ForgettingCurve as aJ, type ForgettingCurveConfig as aK, FrustrationEvaluator as aL, type FrustrationEvaluatorConfig as aM, GoalManager as aN, type GoalManagerConfig as aO, GustationEngine as aP, type InboundMessageEnvelope as aQ, type InboundPerceptEnvelope as aR, InhibitionController as aS, type InhibitionControllerConfig as aT, Interoception as aU, type InteroceptionConfig as aV, IntrospectionEngine as aW, type IntrospectionEngineConfig as aX, KNOWN_PROVIDERS as aY, KnownEntityTracker as aZ, type KnownEntityTrackerConfig as a_, DefaultSerializer as aa, DefaultSimulation as ab, DefaultSimulationClock as ac, DefaultStateManager as ad, DefaultVectorMemoryAdapter as ae, DeliberationEngine as af, DeltaEncoder as ag, type DeltaSnapshot as ah, DreamSimulator as ai, type DreamSimulatorConfig as aj, type Duration as ak, ESCALATION_DEMAND as al, type EffectorDeclaration as am, type EffectorEntry as an, type EffectorResult as ao, type EffectorSpec as ap, type EmbeddingProvider as aq, EmpathySimulator as ar, type EmpathySimulatorConfig as as, EnergyRegulator as at, type EnergyRegulatorConfig as au, type EngineRegistry as av, type EngineResult as aw, type Envelope as ax, EpisodicConsolidator as ay, type EpisodicConsolidatorConfig as az, type SeededPRNG as b, SelfModelUpdater as b$, type LLMCallMeta as b0, type LLMCompletionRecord as b1, type LLMCompletionSink as b2, type LLMProvider as b3, type LLMWire as b4, LossEvaluator as b5, type LossEvaluatorConfig as b6, type MessageEnvelope as b7, type MetricCollector as b8, type MetricPoint as b9, type PersonaConsolidatorConfig as bA, PlanningEngine as bB, type PlanningEngineConfig as bC, type PriceTable as bD, type ProviderCredential as bE, ReafferenceEngine as bF, type ReconstructionFidelityReport as bG, type ReconstructionFidelityScores as bH, type RecordUsageInput as bI, type ReplayComparison as bJ, type ReplayConfig as bK, type ReplayDifference as bL, ReplayManager as bM, type ReplayMetadata as bN, type ReplayRecord as bO, type ReplayRecorder as bP, type ReplaySession as bQ, type ReplyEnvelope as bR, ReputationTracker as bS, type ReputationTrackerConfig as bT, RewardEvaluator as bU, type RewardEvaluatorConfig as bV, type RoutingRule as bW, type Scenario as bX, type ScenarioConfig as bY, type ScenarioValidationResult as bZ, type SchemaPrecondition as b_, type MinimalContext as ba, MockEmbedder as bb, type ModelPrice as bc, type ModelRoute as bd, type ModelRouter as be, MoralEvaluator as bf, type MoralEvaluatorConfig as bg, MotorSchemaExecutor as bh, NULL_ROUTER as bi, NoveltyDetector as bj, type NoveltyDetectorConfig as bk, OlfactionEngine as bl, OpenAICompatibleEmbedder as bm, type Orchestrator as bn, type OrchestratorConfig as bo, type OutboxMessage as bp, type PMABehavioral as bq, type PMABelief as br, type PMAEmotionalBaseline as bs, PMAEvalHarness as bt, type PMAGoal as bu, type PMAIdentity as bv, type PMAProbe as bw, type PMASnapshot as bx, type PerceptEnvelope as by, PersonaConsolidator as bz, type SimulationEntity as c, assembleMind as c$, type SelfModelUpdaterConfig as c0, SemanticIntegrator as c1, type SemanticIntegratorConfig as c2, type SensoryInput as c3, type SerializationConfig as c4, type SerializationFormat as c5, type SerializedEntity as c6, type SerializedState as c7, type Serializer as c8, type SessionLogEnvelope as c9, type TickListener as cA, type TokenLedgerRecord as cB, type TokenReportEnvelope as cC, TokenTracker as cD, type TokenTrackerConfig as cE, type TokenUsage as cF, type VectorIndex as cG, type VectorMemoryAdapter as cH, type VectorMemoryConfig as cI, type VectorQueryFilter as cJ, type VectorQueryResult as cK, type VectorRecord as cL, VisionEngine as cM, type VoiceChunk as cN, type WillAffect as cO, type WillConfig as cP, type WillEffectorAct as cQ, type WillInstance as cR, type WillMessage as cS, type WillStateSummary as cT, type WillStatus as cU, WillStem as cV, type WillSummary as cW, WorkingMemory as cX, type WorkingMemoryConfig as cY, type WorldEntity as cZ, type WorldInterface as c_, type Simulation as ca, type SimulationClock as cb, type SimulationConfig as cc, type SimulationEngine as cd, type SimulationEventBase as ce, type SimulationEventListener as cf, type SleepPressureConfig as cg, SleepPressureRegulator as ch, SocialPerception as ci, type SocialPerceptionConfig as cj, SomatosensationEngine as ck, SpacedRepetition as cl, type SpacedRepetitionConfig as cm, type StateSnapshot as cn, type Stimulus as co, type StorageAdapter as cp, StressRegulator as cq, type StressRegulatorConfig as cr, TableRouter as cs, TaskSwitcher as ct, type TaskSwitcherConfig as cu, type TextMessage as cv, TheoryOfMind as cw, type TheoryOfMindConfig as cx, ThreatEvaluator as cy, type ThreatEvaluatorConfig as cz, type Timestamp as d, chainRouters as d0, clearCompletionRecorder as d1, defaultBaseFor as d2, type effectorInvocation as d3, type effectorInvocationEnvelope as d4, getCompletionRecorder as d5, isNullRouter as d6, knownWireFor as d7, resolvePricing as d8, setCompletionRecorder as d9, type SimulationEvent as e, type StateManager as f, type Tick as g, type SimulationState as h, type StateCommands as i, type ReasoningFootprint as j, type ReadonlySimulationState as k, type ConflictStrategy as l, type ConflictResolution as m, type AckEnvelope as n, type ActionRequest as o, type ActionResult as p, ActionSelector as q, type ActivityEnvelope as r, type ActivityEvent as s, type ActivityEventHandler as t, AestheticEvaluator as u, type AestheticEvaluatorConfig as v, AffectiveBlender as w, type AffectiveBlenderConfig as x, AffordanceSynthesizer as y, AsyncEngine as z };