@mindot/will 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +32 -12
  2. package/dist/channels/discord.d.ts +2 -11
  3. package/dist/channels/discord.js.map +1 -1
  4. package/dist/channels/whatsapp.d.ts +72 -0
  5. package/dist/channels/whatsapp.js +252 -0
  6. package/dist/channels/whatsapp.js.map +1 -0
  7. package/dist/cli.js +837 -91
  8. package/dist/cli.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.js +620 -79
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp/effectors.d.ts +1 -1
  13. package/dist/mcp/effectors.js.map +1 -1
  14. package/dist/types-E9-HV-SW.d.ts +11 -0
  15. package/dist/{will-D-slky1N.d.ts → will-Bikuk4s2.d.ts} +56 -12
  16. package/package.json +12 -3
  17. package/src/channels/discord.ts +11 -11
  18. package/src/channels/roster.ts +1 -1
  19. package/src/channels/types.ts +1 -1
  20. package/src/channels/whatsapp.ts +318 -0
  21. package/src/cli.ts +46 -23
  22. package/src/cognition/acp.ts +28 -0
  23. package/src/cognition/agency/consequence.ts +174 -0
  24. package/src/cognition/agency/engines/action.selector.ts +301 -48
  25. package/src/cognition/agency/engines/affordance.synthesizer.ts +26 -16
  26. package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
  27. package/src/cognition/agency/engines/motor.schema.executor.ts +86 -14
  28. package/src/cognition/agency/engines/reafference.engine.ts +122 -18
  29. package/src/cognition/agency/execution.primitives.ts +13 -13
  30. package/src/cognition/agency/proactive.communicator.ts +1 -1
  31. package/src/cognition/agency/reconcile.learning.ts +1 -1
  32. package/src/cognition/agency/revocation.ts +79 -0
  33. package/src/cognition/agency/schemas/repertoire.ts +2 -2
  34. package/src/cognition/agency/selection.scoring.ts +25 -9
  35. package/src/cognition/bus.ts +2 -2
  36. package/src/cognition/completion.inbox.ts +1 -1
  37. package/src/cognition/config.mirror.entities.ts +5 -5
  38. package/src/cognition/event.log.ts +2 -2
  39. package/src/cognition/event.schemas.ts +67 -67
  40. package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
  41. package/src/cognition/faculties/affective.blender.ts +36 -4
  42. package/src/cognition/faculties/attachment.evaluator.ts +6 -6
  43. package/src/cognition/faculties/attention.allocator.ts +63 -8
  44. package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
  45. package/src/cognition/faculties/bias.detector.ts +7 -7
  46. package/src/cognition/faculties/circadian.oscillator.ts +3 -3
  47. package/src/cognition/faculties/confidence.calibrator.ts +7 -7
  48. package/src/cognition/faculties/dream.simulator.ts +5 -5
  49. package/src/cognition/faculties/empathy.simulator.ts +7 -7
  50. package/src/cognition/faculties/energy.regulator.ts +4 -4
  51. package/src/cognition/faculties/episodic.consolidator.ts +6 -6
  52. package/src/cognition/faculties/executive.engine/commands.ts +6 -6
  53. package/src/cognition/faculties/executive.engine/context.ts +42 -42
  54. package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
  55. package/src/cognition/faculties/executive.engine/engine.ts +27 -26
  56. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
  57. package/src/cognition/faculties/executive.engine/facet.ts +18 -18
  58. package/src/cognition/faculties/executive.engine/gating.ts +2 -2
  59. package/src/cognition/faculties/executive.engine/messages.ts +3 -3
  60. package/src/cognition/faculties/executive.engine/parser.ts +11 -11
  61. package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
  62. package/src/cognition/faculties/exteroception.ts +99 -5
  63. package/src/cognition/faculties/forgetting.curve.ts +3 -3
  64. package/src/cognition/faculties/frustration.evaluator.ts +6 -6
  65. package/src/cognition/faculties/goal.manager.ts +15 -15
  66. package/src/cognition/faculties/inhibition.controller.ts +3 -3
  67. package/src/cognition/faculties/interoception.ts +3 -3
  68. package/src/cognition/faculties/introspection.engine.ts +3 -3
  69. package/src/cognition/faculties/known.entity.tracker.ts +15 -15
  70. package/src/cognition/faculties/loss.evaluator.ts +2 -2
  71. package/src/cognition/faculties/moral.evaluator.ts +4 -4
  72. package/src/cognition/faculties/novelty.detector.ts +4 -4
  73. package/src/cognition/faculties/persona.consolidator.ts +12 -12
  74. package/src/cognition/faculties/planning.engine/engine.ts +47 -47
  75. package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
  76. package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
  77. package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
  78. package/src/cognition/faculties/reputation.tracker.ts +6 -6
  79. package/src/cognition/faculties/reward.evaluator.ts +4 -4
  80. package/src/cognition/faculties/self.model.updater.ts +8 -8
  81. package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
  82. package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
  83. package/src/cognition/faculties/social.perception.ts +3 -3
  84. package/src/cognition/faculties/spaced.repetition.ts +2 -2
  85. package/src/cognition/faculties/stress.regulator.ts +27 -4
  86. package/src/cognition/faculties/task.switcher.ts +11 -6
  87. package/src/cognition/faculties/theory.of.mind.ts +6 -6
  88. package/src/cognition/faculties/threat.evaluator.ts +3 -3
  89. package/src/cognition/faculties/working.memory.ts +8 -8
  90. package/src/cognition/generative.model.ts +2 -2
  91. package/src/cognition/memory/vector.adapter.ts +5 -5
  92. package/src/cognition/memory/vector.content.ts +4 -4
  93. package/src/cognition/memory/vector.embedder.ts +9 -9
  94. package/src/cognition/persona.prior.ts +1 -1
  95. package/src/cognition/schema.registry.ts +2 -2
  96. package/src/cognition/senses/audition.engine/engine.ts +2 -2
  97. package/src/cognition/senses/base.sense.engine.ts +1 -1
  98. package/src/cognition/utilities/token.tracker.ts +11 -5
  99. package/src/core/abstracts.ts +1 -1
  100. package/src/core/completion.recorder.ts +2 -2
  101. package/src/core/serialization.ts +3 -3
  102. package/src/core/snapshot.manager.ts +1 -1
  103. package/src/core/state.manager.ts +1 -1
  104. package/src/core/utils.ts +1 -1
  105. package/src/host/boot.ts +59 -37
  106. package/src/host/utterances.ts +1 -1
  107. package/src/llm/index.ts +84 -34
  108. package/src/llm/summarizer.ts +4 -4
  109. package/src/llm/wire.contracts.ts +1 -1
  110. package/src/mcp/effectors.ts +10 -10
  111. package/src/mcp/server.ts +11 -11
  112. package/src/pma/eval.ts +1 -1
  113. package/src/pma/index.ts +21 -21
  114. package/src/runners/outreach.runner.ts +3 -3
  115. package/src/runners/social.runner.ts +1 -1
  116. package/src/runners/thin-shim.runner.ts +1 -1
  117. package/src/sdk/will.ts +20 -11
  118. package/src/serve/server.ts +12 -12
  119. package/src/stem/assembly.audit.ts +3 -3
  120. package/src/stem/guards/identity.coherence.ts +13 -12
  121. package/src/stem/guards/identity.guard.ts +22 -22
  122. package/src/stem/index.ts +11 -11
  123. package/src/stem/mind.ts +12 -12
  124. package/src/stem/tracts/biography.writer.ts +3 -3
  125. package/src/stem/tracts/effector.controller.ts +4 -4
  126. package/src/stem/tracts/health.reporter.ts +1 -1
  127. package/src/stem/tracts/outbox.controller.ts +4 -0
  128. package/src/stem/tracts/outbox.writer.ts +4 -4
  129. package/src/stem/tracts/pma.controller.ts +1 -1
  130. package/src/stem/tracts/replay.controller.ts +10 -10
  131. package/src/stem/tracts/sensory.controller.ts +2 -2
  132. package/src/stem/tracts/session.logger.ts +4 -4
  133. package/src/stem/tracts/transport/socketio.transport.ts +7 -7
  134. package/src/stem/tracts/transport/stream.transport.ts +2 -2
  135. package/src/stem/tracts/transport.controller.ts +15 -15
package/dist/cli.js CHANGED
@@ -3862,6 +3862,11 @@ var MODEL_PRICING = {
3862
3862
  // Legacy aliases kept for backward compat
3863
3863
  "anthropic/claude-haiku-4": { input: 1, output: 5 },
3864
3864
  "anthropic/claude-opus-4": { input: 5, output: 25 },
3865
+ // Z.ai (GLM-5 family). `glm-5.2[1m]` is the same model asking for its 1M
3866
+ // context window — same rate, so it gets its own row rather than relying on
3867
+ // the normalizer (a future long-context tier would price differently).
3868
+ "glm/glm-5.2": { input: 1.4, output: 4.4 },
3869
+ "glm/glm-5.2[1m]": { input: 1.4, output: 4.4 },
3865
3870
  // Google
3866
3871
  "google/gemini-2.0-flash": { input: 0.1, output: 0.4 },
3867
3872
  "google/gemini-2.0-pro": { input: 1.25, output: 5 },
@@ -4650,6 +4655,9 @@ var SleepPressureRegulator = class {
4650
4655
  }
4651
4656
  };
4652
4657
 
4658
+ // src/cognition/acp.ts
4659
+ var ACP_SELF_PRECISION = 0.35;
4660
+
4653
4661
  // src/cognition/persona.prior.ts
4654
4662
  var PERSONA_PRIOR_ID = "persona-prior";
4655
4663
  var PERSONA_PRIOR_TYPE = "persona.prior";
@@ -4758,6 +4766,8 @@ function consolidatePrior(current, adjustments, tick, opts = {}) {
4758
4766
  }
4759
4767
 
4760
4768
  // src/cognition/faculties/attention.allocator.ts
4769
+ var ACP_USAGE_UPLIFT = 0.15;
4770
+ var ACP_CONFIDENCE = 0.4;
4761
4771
  var EFFORT_BASELINE2 = 0.7;
4762
4772
  var EFFORT_MIN = 0.4;
4763
4773
  var EFFORT_MAX = 1;
@@ -4779,6 +4789,8 @@ var AttentionAllocator = class {
4779
4789
  _effort = EFFORT_BASELINE2;
4780
4790
  /** A one-shot focus/rest request from the executive, applied next react(). */
4781
4791
  _effortRequest = null;
4792
+ /** ACP-P2: a self-caused precision attenuation is armed; react() restores after one observe. */
4793
+ _acpOneShot = false;
4782
4794
  _bus = null;
4783
4795
  _model = new GenerativeModel();
4784
4796
  constructor(config = {}) {
@@ -4799,8 +4811,12 @@ var AttentionAllocator = class {
4799
4811
  "executive.prediction.formed",
4800
4812
  "attention.regulate",
4801
4813
  // voluntary focus/rest from the executive (Option C)
4802
- "senses.*"
4814
+ "senses.*",
4803
4815
  // all sense percepts — salience feeds attention allocation
4816
+ // ACP-P2: our own enactions — anticipate the attention swing they cause
4817
+ "agency.enacted",
4818
+ "agency.communicate",
4819
+ "agency.invocation"
4804
4820
  ];
4805
4821
  }
4806
4822
  publishes() {
@@ -4827,6 +4843,18 @@ var AttentionAllocator = class {
4827
4843
  this._model.setPrecision("attention.usage", 1 + p.confidence * 0.5);
4828
4844
  break;
4829
4845
  }
4846
+ case "agency.enacted":
4847
+ case "agency.communicate":
4848
+ case "agency.invocation": {
4849
+ const usage = this._model.predict("attention.usage");
4850
+ const free = this._model.predict("attention.free_fraction");
4851
+ this._model.anticipate("attention.usage", Math.min(1, usage + ACP_USAGE_UPLIFT), ACP_CONFIDENCE);
4852
+ this._model.anticipate("attention.free_fraction", Math.max(0, free - ACP_USAGE_UPLIFT), ACP_CONFIDENCE);
4853
+ this._model.setPrecision("attention.usage", ACP_SELF_PRECISION);
4854
+ this._model.setPrecision("attention.free_fraction", ACP_SELF_PRECISION);
4855
+ this._acpOneShot = true;
4856
+ break;
4857
+ }
4830
4858
  default:
4831
4859
  if (e.type.startsWith("senses.")) {
4832
4860
  const percept = e.payload;
@@ -4910,6 +4938,11 @@ var AttentionAllocator = class {
4910
4938
  if (_bus) {
4911
4939
  const usageErr = this._model.observe("attention.usage", attentionUsage);
4912
4940
  const freeErr = this._model.observe("attention.free_fraction", freeFraction);
4941
+ if (this._acpOneShot) {
4942
+ this._model.setPrecision("attention.usage", 1);
4943
+ this._model.setPrecision("attention.free_fraction", 1);
4944
+ this._acpOneShot = false;
4945
+ }
4913
4946
  if (!usageErr.gated || !freeErr.gated)
4914
4947
  _bus.publish({ type: "attention.state.changed", version: 1, sourceEngine: this.name, salience: Math.max(usageErr.salience, freeErr.salience), payload: { usage: attentionUsage, focusCount, capacity: effectiveCapacity, freeFraction } });
4915
4948
  }
@@ -5029,7 +5062,18 @@ var StressRegulator = class {
5029
5062
  }
5030
5063
  // ── Engine interface ─────────────────────────────────────
5031
5064
  subscribes() {
5032
- return ["energy.state.changed", "sleep.state.changed", "novelty.state.changed", "goal.state.changed", "metacognition.state.changed", "executive.prediction.formed"];
5065
+ return [
5066
+ "energy.state.changed",
5067
+ "sleep.state.changed",
5068
+ "novelty.state.changed",
5069
+ "goal.state.changed",
5070
+ "metacognition.state.changed",
5071
+ "executive.prediction.formed",
5072
+ // ACP-P2: our own enactions — the stress swing they cause is expected
5073
+ "agency.enacted",
5074
+ "agency.communicate",
5075
+ "agency.invocation"
5076
+ ];
5033
5077
  }
5034
5078
  publishes() {
5035
5079
  return [];
@@ -5058,8 +5102,17 @@ var StressRegulator = class {
5058
5102
  this._model.setPrecision("stress.load", 1 + p.confidence * 0.5);
5059
5103
  break;
5060
5104
  }
5105
+ case "agency.enacted":
5106
+ case "agency.communicate":
5107
+ case "agency.invocation": {
5108
+ this._model.setPrecision("stress.load", ACP_SELF_PRECISION);
5109
+ this._acpOneShot = true;
5110
+ break;
5111
+ }
5061
5112
  }
5062
5113
  }
5114
+ /** ACP-P2: self-caused stress attenuation armed; react() restores after one observe. */
5115
+ _acpOneShot = false;
5063
5116
  snapshot() {
5064
5117
  return {
5065
5118
  energyLevel: this._energyLevel,
@@ -5152,6 +5205,10 @@ var StressRegulator = class {
5152
5205
  if (_bus) {
5153
5206
  const zoneCode = zone === "low" ? 0 : zone === "optimal" ? 1 : zone === "distress" ? 2 : 3;
5154
5207
  const predErr = this._model.observe("stress.load", newLoad);
5208
+ if (this._acpOneShot) {
5209
+ this._model.setPrecision("stress.load", 1);
5210
+ this._acpOneShot = false;
5211
+ }
5155
5212
  if (!predErr.gated || zone !== previousZone) {
5156
5213
  _bus.publish({ type: "stress.state.changed", version: 1, sourceEngine: this.name, salience: predErr.salience, payload: { load: newLoad, zoneCode, deadlineUrgency: state.metrics.get("deadline.urgency") ?? 0, cognitiveLoad: state.metrics.get("cognitive.load") ?? state.metrics.get("attention.usage") ?? 0 } });
5157
5214
  }
@@ -5374,6 +5431,116 @@ var CircadianOscillator = class {
5374
5431
  }
5375
5432
  };
5376
5433
 
5434
+ // src/cognition/agency/consequence.ts
5435
+ var CONSEQUENCE_TYPE = "agency.consequence";
5436
+ var ATTENUATION = 0.25;
5437
+ var MIN_TEXT_MATCH_LEN = 12;
5438
+ var CONSEQUENCE_TTL_TICKS = 30;
5439
+ function fnv1a(text) {
5440
+ let h = 2166136261;
5441
+ for (let i = 0; i < text.length; i++) {
5442
+ h ^= text.charCodeAt(i);
5443
+ h = Math.imul(h, 16777619) >>> 0;
5444
+ }
5445
+ return h >>> 0;
5446
+ }
5447
+ function paramsKey(value) {
5448
+ if (value === null || typeof value !== "object")
5449
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
5450
+ if (Array.isArray(value))
5451
+ return `[${value.map(paramsKey).join(",")}]`;
5452
+ const obj = value;
5453
+ const keys = Object.keys(obj).sort();
5454
+ return `{${keys.map((k) => `${k}:${paramsKey(obj[k])}`).join(",")}}`;
5455
+ }
5456
+ function consequenceEntity(d) {
5457
+ return {
5458
+ id: `agency-consequence-${d.intentId}`,
5459
+ type: CONSEQUENCE_TYPE,
5460
+ metadata: { ...d }
5461
+ };
5462
+ }
5463
+ function readConsequence(m) {
5464
+ const meta = m ?? {};
5465
+ const intentId = typeof meta["intentId"] === "string" ? meta["intentId"] : void 0;
5466
+ const schema = typeof meta["schema"] === "string" ? meta["schema"] : void 0;
5467
+ const mode = meta["mode"] === "communicate" || meta["mode"] === "external" ? meta["mode"] : void 0;
5468
+ if (!intentId || !schema || !mode) return null;
5469
+ return {
5470
+ intentId,
5471
+ schema,
5472
+ mode,
5473
+ effector: typeof meta["effector"] === "string" ? meta["effector"] : void 0,
5474
+ targetEntityId: typeof meta["targetEntityId"] === "string" ? meta["targetEntityId"] : void 0,
5475
+ textHash: typeof meta["textHash"] === "number" ? meta["textHash"] : void 0,
5476
+ text: typeof meta["text"] === "string" ? meta["text"] : void 0,
5477
+ paramsHash: typeof meta["paramsHash"] === "number" ? meta["paramsHash"] : void 0,
5478
+ expiresAt: typeof meta["expiresAt"] === "number" ? meta["expiresAt"] : 0,
5479
+ tick: typeof meta["tick"] === "number" ? meta["tick"] : 0
5480
+ };
5481
+ }
5482
+ function liveConsequences(entities, tick) {
5483
+ const out = [];
5484
+ for (const [, e] of entities) {
5485
+ if (e.type !== CONSEQUENCE_TYPE) continue;
5486
+ const d = readConsequence(e.metadata);
5487
+ if (d && tick < d.expiresAt) out.push(d);
5488
+ }
5489
+ return out.sort((a, b) => a.intentId < b.intentId ? -1 : a.intentId > b.intentId ? 1 : 0);
5490
+ }
5491
+ function matchConsequenceText(descriptors, candidate) {
5492
+ if (candidate.length === 0) return null;
5493
+ const candidateHash = fnv1a(candidate);
5494
+ for (const d of descriptors) {
5495
+ if (d.textHash !== void 0 && d.textHash === candidateHash) return d;
5496
+ if (d.text !== void 0 && d.text.length >= MIN_TEXT_MATCH_LEN && candidate.includes(d.text)) return d;
5497
+ }
5498
+ return null;
5499
+ }
5500
+ var CORRESPONDENCE_ATTENUATION = 0.5;
5501
+ function matchConsequenceEntity(descriptors, entityId, changeType) {
5502
+ if (changeType !== "modified" || entityId.length === 0) return null;
5503
+ for (const d of descriptors)
5504
+ if (d.mode === "external" && d.targetEntityId === entityId) return d;
5505
+ return null;
5506
+ }
5507
+
5508
+ // src/cognition/agency/revocation.ts
5509
+ var REVOCATION_TYPE = "agency.revocation";
5510
+ var RUPTURE_REVOKE_GATE = 0.7;
5511
+ var REVOCATION_TTL_TICKS = 5;
5512
+ function revocationId(intentId) {
5513
+ return `agency-revocation-${intentId}`;
5514
+ }
5515
+ function revocationEntity(intentId, schema, rupture, tick) {
5516
+ return {
5517
+ id: revocationId(intentId),
5518
+ type: REVOCATION_TYPE,
5519
+ metadata: { intentId, schema, rupture, tick, expiresAt: tick + REVOCATION_TTL_TICKS }
5520
+ };
5521
+ }
5522
+ function revokedIntentIds(entities, tick) {
5523
+ const out = /* @__PURE__ */ new Set();
5524
+ for (const [, e] of entities) {
5525
+ if (e.type !== REVOCATION_TYPE) continue;
5526
+ const m = e.metadata ?? {};
5527
+ const intentId = typeof m["intentId"] === "string" ? m["intentId"] : void 0;
5528
+ const expiresAt = typeof m["expiresAt"] === "number" ? m["expiresAt"] : 0;
5529
+ if (intentId && tick < expiresAt) out.add(intentId);
5530
+ }
5531
+ return out;
5532
+ }
5533
+ function staleRevocationIds(entities, tick) {
5534
+ const stale = [];
5535
+ for (const [id, e] of entities) {
5536
+ if (e.type !== REVOCATION_TYPE) continue;
5537
+ const m = e.metadata ?? {};
5538
+ const expiresAt = typeof m["expiresAt"] === "number" ? m["expiresAt"] : 0;
5539
+ if (tick >= expiresAt) stale.push(id);
5540
+ }
5541
+ return stale;
5542
+ }
5543
+
5377
5544
  // src/cognition/faculties/exteroception.ts
5378
5545
  var internalTypes = /* @__PURE__ */ new Set([
5379
5546
  "percept",
@@ -5399,7 +5566,12 @@ var internalTypes = /* @__PURE__ */ new Set([
5399
5566
  "episodic_memory",
5400
5567
  // Internal state entities written by our own engines — not external events
5401
5568
  "affect.blends",
5402
- "executive.summary"
5569
+ "executive.summary",
5570
+ // The agency's own forward-model records (EXAFFERENCE P1/P2): perceiving our
5571
+ // expected-consequence descriptors would be a self-noise loop.
5572
+ CONSEQUENCE_TYPE,
5573
+ // The agency's own revocation tombstones (EXAFFERENCE P4) — internal bookkeeping.
5574
+ REVOCATION_TYPE
5403
5575
  ]);
5404
5576
  var Exteroception = class {
5405
5577
  name = "exteroception";
@@ -5449,17 +5621,26 @@ var Exteroception = class {
5449
5621
  const events = [], commands = { set: [], delete: [], metrics: [] };
5450
5622
  const rawPercepts = this._scanWorld(state);
5451
5623
  const capped = rawPercepts.slice(0, this._maxPerceptsPerTick);
5624
+ const consequences = liveConsequences(state.entities, tick);
5452
5625
  for (let i = 0; i < capped.length; i++) {
5453
5626
  const rp = capped[i];
5627
+ const textHit = consequences.length > 0 && rp.matchText ? matchConsequenceText(consequences, rp.matchText) : null;
5628
+ const entityHit = !textHit && consequences.length > 0 ? matchConsequenceEntity(consequences, rp.entityId, rp.changeType) : null;
5629
+ const hit = textHit ?? entityHit;
5630
+ const salience = textHit ? rp.salience * ATTENUATION : entityHit ? rp.salience * CORRESPONDENCE_ATTENUATION : rp.salience;
5454
5631
  const perceptEntity = {
5455
5632
  id: `percept-${tick}-${i}`,
5456
5633
  type: "percept",
5457
5634
  metadata: {
5458
5635
  entityId: rp.entityId,
5459
5636
  changeType: rp.changeType,
5460
- salience: rp.salience,
5637
+ salience,
5461
5638
  category: rp.category,
5462
5639
  summary: rp.summary,
5640
+ provenance: hit ? "reafferent" : "exafferent",
5641
+ ...hit ? { sourceIntentId: hit.intentId } : {},
5642
+ // affect→percept seam (registry #5): what this percept FEELS like
5643
+ ...rp.valence !== void 0 ? { valence: rp.valence, valenceSource: rp.valenceSource } : {},
5463
5644
  tick
5464
5645
  }
5465
5646
  };
@@ -5470,7 +5651,7 @@ var Exteroception = class {
5470
5651
  source: this.name,
5471
5652
  payload: {
5472
5653
  entityId: rp.entityId,
5473
- salience: rp.salience,
5654
+ salience,
5474
5655
  category: rp.category,
5475
5656
  summary: rp.summary
5476
5657
  }
@@ -5518,7 +5699,9 @@ var Exteroception = class {
5518
5699
  changeType: "appeared",
5519
5700
  salience: this._computeSalience(entity, "appeared", state.time),
5520
5701
  category: entity.type,
5521
- summary: this._summarizeEntity(entity, "appeared")
5702
+ summary: this._summarizeEntity(entity, "appeared"),
5703
+ matchText: this._matchText(entity),
5704
+ ...this._valenceOf(id, state)
5522
5705
  });
5523
5706
  } else if (entity.updatedAt > previousVersion) {
5524
5707
  percepts.push({
@@ -5526,7 +5709,9 @@ var Exteroception = class {
5526
5709
  changeType: "modified",
5527
5710
  salience: this._computeSalience(entity, "modified", state.time),
5528
5711
  category: entity.type,
5529
- summary: this._summarizeEntity(entity, "modified")
5712
+ summary: this._summarizeEntity(entity, "modified"),
5713
+ matchText: this._matchText(entity),
5714
+ ...this._valenceOf(id, state)
5530
5715
  });
5531
5716
  }
5532
5717
  this._previousEntityVersions.set(id, entity.updatedAt);
@@ -5552,6 +5737,46 @@ var Exteroception = class {
5552
5737
  * Generate a meaningful summary for an entity.
5553
5738
  * Instead of "New percept: percept-54-0", produce something useful.
5554
5739
  */
5740
+ /**
5741
+ * The text the corollary-discharge matcher inspects for this entity — its
5742
+ * content (a message body) over its description over its summary. Where our
5743
+ * own delivered words would surface if the world echoes them back.
5744
+ */
5745
+ /**
5746
+ * The affect→percept valence seam (registry #5). A percept carries how the
5747
+ * mind *feels* about what it is a percept OF, resolved most-specific-first:
5748
+ *
5749
+ * 1. the KnownEntityTracker's dossier for this entity (`ke-<id>.valence`) —
5750
+ * a real per-entity felt valence, the honest signal;
5751
+ * 2. otherwise the ambient `affect.valence` — the felt tone at perception
5752
+ * time. Weaker evidence, so it is tagged `'ambient'` and consumers
5753
+ * weight it down (mood is context, not appraisal of this thing).
5754
+ *
5755
+ * Absent both, no valence is stamped and every consumer keeps its
5756
+ * pre-seam behaviour.
5757
+ */
5758
+ /** Spread-friendly form of `_valenceFor` for percept construction. */
5759
+ _valenceOf(entityId, state) {
5760
+ const v = this._valenceFor(entityId, state);
5761
+ return v ? { valence: v.valence, valenceSource: v.source } : {};
5762
+ }
5763
+ _valenceFor(entityId, state) {
5764
+ const dossier = state.entities.get(`ke-${entityId}`);
5765
+ const felt = dossier?.metadata?.["valence"];
5766
+ if (typeof felt === "number" && Number.isFinite(felt))
5767
+ return { valence: felt, source: "entity" };
5768
+ const ambient = state.metrics.get("affect.valence");
5769
+ if (typeof ambient === "number" && Number.isFinite(ambient))
5770
+ return { valence: ambient, source: "ambient" };
5771
+ return void 0;
5772
+ }
5773
+ _matchText(entity) {
5774
+ const m = entity.metadata;
5775
+ const content = typeof m?.["content"] === "string" ? m["content"] : void 0;
5776
+ const description = typeof m?.["description"] === "string" ? m["description"] : void 0;
5777
+ const summary = typeof m?.["summary"] === "string" ? m["summary"] : void 0;
5778
+ return content ?? description ?? summary;
5779
+ }
5555
5780
  _summarizeEntity(entity, changeType) {
5556
5781
  const name = entity.metadata?.name;
5557
5782
  const description = entity.metadata?.description;
@@ -7536,6 +7761,8 @@ var MoralEvaluator = class {
7536
7761
  };
7537
7762
 
7538
7763
  // src/cognition/faculties/affective.blender.ts
7764
+ var ACP_AROUSAL_UPLIFT = 0.1;
7765
+ var ACP_CONFIDENCE2 = 0.4;
7539
7766
  var DOMINANT_EMOTION_CODES = {
7540
7767
  "neutral": 0,
7541
7768
  "fear": 1,
@@ -7636,6 +7863,8 @@ var AffectiveBlender = class {
7636
7863
  _previousPAD = { valence: 0, arousal: 0.3, dominance: 0.5 };
7637
7864
  _emotionHistory = /* @__PURE__ */ new Map();
7638
7865
  _moodBaseline = 0.5;
7866
+ /** ACP-P2: self-caused arousal attenuation armed; react() restores after one observe. */
7867
+ _acpOneShot = false;
7639
7868
  _energyLevel = 100;
7640
7869
  _comfort = 0.5;
7641
7870
  _sleepPressure = 0;
@@ -7669,8 +7898,12 @@ var AffectiveBlender = class {
7669
7898
  "sleep.state.changed",
7670
7899
  "stress.state.changed",
7671
7900
  "executive.prediction.formed",
7672
- "interaction.occurred"
7901
+ "interaction.occurred",
7673
7902
  // 2.1: social stimuli drive immediate arousal/valence shifts
7903
+ // ACP-P2: our own enactions — the arousal they cause is expected, not surprising
7904
+ "agency.enacted",
7905
+ "agency.communicate",
7906
+ "agency.invocation"
7674
7907
  ];
7675
7908
  }
7676
7909
  publishes() {
@@ -7679,6 +7912,15 @@ var AffectiveBlender = class {
7679
7912
  onCognitiveEvent(e) {
7680
7913
  this._model.observe(e.type, e.salience);
7681
7914
  switch (e.type) {
7915
+ case "agency.enacted":
7916
+ case "agency.communicate":
7917
+ case "agency.invocation": {
7918
+ const arousal = this._model.predict("affect.arousal");
7919
+ this._model.anticipate("affect.arousal", Math.min(1, arousal + ACP_AROUSAL_UPLIFT), ACP_CONFIDENCE2);
7920
+ this._model.setPrecision("affect.arousal", ACP_SELF_PRECISION);
7921
+ this._acpOneShot = true;
7922
+ break;
7923
+ }
7682
7924
  case "circadian.state.changed":
7683
7925
  this._moodBaseline = e.payload["moodBaseline"] ?? this._moodBaseline;
7684
7926
  break;
@@ -7782,8 +8024,14 @@ var AffectiveBlender = class {
7782
8024
  const _bus = this._bus;
7783
8025
  if (_bus)
7784
8026
  _bus.publish({ type: "affect.state.shifted", version: 1, sourceEngine: this.name, salience: Math.min(1, Math.abs(modulatedPAD.valence) + modulatedPAD.arousal * 0.5), payload: { valence: modulatedPAD.valence, arousal: modulatedPAD.arousal, dominantEmotion: dominant } });
7785
- if (_bus)
7786
- _bus.publish({ type: "affect.state.changed", version: 1, sourceEngine: this.name, salience: this._model.observe("affect.arousal", modulatedPAD.arousal).salience, payload: { valence: modulatedPAD.valence, arousal: modulatedPAD.arousal, dominance: modulatedPAD.dominance } });
8027
+ if (_bus) {
8028
+ const arousalErr = this._model.observe("affect.arousal", modulatedPAD.arousal);
8029
+ if (this._acpOneShot) {
8030
+ this._model.setPrecision("affect.arousal", 1);
8031
+ this._acpOneShot = false;
8032
+ }
8033
+ _bus.publish({ type: "affect.state.changed", version: 1, sourceEngine: this.name, salience: arousalErr.salience, payload: { valence: modulatedPAD.valence, arousal: modulatedPAD.arousal, dominance: modulatedPAD.dominance } });
8034
+ }
7787
8035
  return { events: events.length > 0 ? events : void 0, commands };
7788
8036
  }
7789
8037
  // ── PAD computation ──────────────────────────────────────
@@ -13153,6 +13401,38 @@ async function withGate(fn, label, gate = llmGate) {
13153
13401
  }
13154
13402
 
13155
13403
  // src/llm/index.ts
13404
+ var ANTHROPIC_WIRE = /* @__PURE__ */ new Set(["anthropic", "glm"]);
13405
+ function speaksAnthropicWire(provider) {
13406
+ return ANTHROPIC_WIRE.has(provider);
13407
+ }
13408
+ function defaultBaseFor(provider) {
13409
+ switch (provider) {
13410
+ case "anthropic":
13411
+ return "https://api.anthropic.com/v1";
13412
+ // Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
13413
+ // appends `/v1/messages`; this client appends `/messages`, so the version
13414
+ // segment belongs here — verified against the live endpoint.
13415
+ case "glm":
13416
+ return "https://api.z.ai/api/anthropic/v1";
13417
+ case "openai":
13418
+ return "https://api.openai.com/v1";
13419
+ case "deepseek":
13420
+ return "https://api.deepseek.com/v1";
13421
+ case "google":
13422
+ return "https://generativelanguage.googleapis.com/v1beta";
13423
+ }
13424
+ }
13425
+ function defaultModelFor(provider) {
13426
+ return provider === "glm" ? "glm-5.2" : "claude-sonnet-4-5-20250929";
13427
+ }
13428
+ function anthropicWireHeaders(provider, apiKey) {
13429
+ return {
13430
+ "Content-Type": "application/json",
13431
+ "anthropic-version": "2023-06-01",
13432
+ "x-api-key": apiKey,
13433
+ ...provider === "glm" ? { Authorization: `Bearer ${apiKey}` } : {}
13434
+ };
13435
+ }
13156
13436
  var DEFAULT_CALL_META = { category: "executive", attribute: "master", function: "decision" };
13157
13437
  var LLMDirector = class {
13158
13438
  _willId;
@@ -13255,7 +13535,7 @@ var LLMDirector = class {
13255
13535
  this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - start, true);
13256
13536
  return result2;
13257
13537
  }
13258
- const result = this._provider === "anthropic" ? await this._callAnthropicStream(systemPrompt, userMessage, onChunk, temperature) : await (async () => {
13538
+ const result = speaksAnthropicWire(this._provider) ? await this._callAnthropicStream(systemPrompt, userMessage, onChunk, temperature) : await (async () => {
13259
13539
  const r = await this._callProvider(systemPrompt, userMessage, temperature);
13260
13540
  onChunk(r.text);
13261
13541
  return r;
@@ -13335,11 +13615,7 @@ var LLMDirector = class {
13335
13615
  try {
13336
13616
  res = await fetch(`${this._resolvedBase()}/messages`, {
13337
13617
  method: "POST",
13338
- headers: {
13339
- "Content-Type": "application/json",
13340
- "anthropic-version": "2023-06-01",
13341
- "x-api-key": this._apiKey
13342
- },
13618
+ headers: anthropicWireHeaders(this._provider, this._apiKey),
13343
13619
  body: JSON.stringify({
13344
13620
  model: this._model,
13345
13621
  max_tokens: this._maxOutputTokens,
@@ -13414,7 +13690,7 @@ var LLMDirector = class {
13414
13690
  return result2;
13415
13691
  }
13416
13692
  const result = await withGate(
13417
- () => this._provider === "anthropic" ? this._callAnthropicStream(systemPrompt, userMessage, () => {
13693
+ () => speaksAnthropicWire(this._provider) ? this._callAnthropicStream(systemPrompt, userMessage, () => {
13418
13694
  }, temperature) : this._callProvider(systemPrompt, userMessage, temperature),
13419
13695
  "executive/direct"
13420
13696
  );
@@ -13426,6 +13702,8 @@ var LLMDirector = class {
13426
13702
  switch (this._provider) {
13427
13703
  case "anthropic":
13428
13704
  return this._callAnthropic(systemPrompt, userMessage, temperature);
13705
+ case "glm":
13706
+ return this._callAnthropic(systemPrompt, userMessage, temperature);
13429
13707
  case "deepseek":
13430
13708
  return this._callOpenAI(systemPrompt, userMessage, temperature);
13431
13709
  case "openai":
@@ -13438,16 +13716,7 @@ var LLMDirector = class {
13438
13716
  }
13439
13717
  /** Default API base URL (including version segment) for a provider. */
13440
13718
  _baseFor(provider) {
13441
- switch (provider) {
13442
- case "anthropic":
13443
- return "https://api.anthropic.com/v1";
13444
- case "openai":
13445
- return "https://api.openai.com/v1";
13446
- case "deepseek":
13447
- return "https://api.deepseek.com/v1";
13448
- case "google":
13449
- return "https://generativelanguage.googleapis.com/v1beta";
13450
- }
13719
+ return defaultBaseFor(provider);
13451
13720
  }
13452
13721
  /** Resolved API base: explicit override wins, else the provider default. */
13453
13722
  _resolvedBase() {
@@ -13487,15 +13756,11 @@ var LLMDirector = class {
13487
13756
  };
13488
13757
  const res = await this._fetchWithTimeout(`${this._resolvedBase()}/messages`, {
13489
13758
  method: "POST",
13490
- headers: {
13491
- "Content-Type": "application/json",
13492
- "anthropic-version": "2023-06-01",
13493
- "x-api-key": this._apiKey
13494
- },
13759
+ headers: anthropicWireHeaders(this._provider, this._apiKey),
13495
13760
  body: JSON.stringify(body)
13496
13761
  });
13497
13762
  if (!res.ok)
13498
- throw new Error(`Anthropic API ${res.status}: ${(await res.text()).slice(0, 300)}`);
13763
+ throw new Error(`${this._provider} API ${res.status}: ${(await res.text()).slice(0, 300)}`);
13499
13764
  const data = await res.json(), text = data.content.find((b) => b.type === "text")?.text ?? "";
13500
13765
  return {
13501
13766
  text,
@@ -14275,7 +14540,7 @@ var ExecutiveEngine = class extends AsyncEngine {
14275
14540
  this._gatingState.executiveInterval = rtConfig.executiveInterval;
14276
14541
  this._gatingState.cooldownTicks = rtConfig.cooldownTicks;
14277
14542
  if (!this._llmDirector && this._willId) {
14278
- const execModel = this._models.executive ?? process.env.WILL_LLM_MODEL ?? "claude-sonnet-4-5-20250929";
14543
+ const execModel = this._models.executive ?? process.env.WILL_LLM_MODEL ?? defaultModelFor(this._llm?.provider ?? process.env.WILL_LLM_PROVIDER ?? "anthropic");
14279
14544
  this._llmDirector = this._directorFor(execModel);
14280
14545
  this._summarizer?.attachLLMDirector(this._directorFor(this._models.summarizer ?? execModel));
14281
14546
  }
@@ -16184,7 +16449,8 @@ var TaskSwitcher = class {
16184
16449
  const currentGoal = activeGoals.find((g) => g.id === this._currentFocus.goalId);
16185
16450
  const currentPriority = currentGoal?.priority ?? 0;
16186
16451
  const switchAdvantage = topGoal.priority - currentPriority;
16187
- const switchCost = this._baseSwitchCost * (1 + this._currentFocus.focusTicks * 0.01);
16452
+ const stability = state.metrics.get("situation.stability") ?? 1;
16453
+ const switchCost = this._baseSwitchCost * (1 + this._currentFocus.focusTicks * 0.01 * stability);
16188
16454
  const netBenefit = switchAdvantage - switchCost;
16189
16455
  if (netBenefit > this._switchThreshold && this._currentFocus.focusTicks >= this._minFocusTicks) {
16190
16456
  this._currentFocus = {
@@ -19874,9 +20140,7 @@ var AffordanceSynthesizer = class {
19874
20140
  if (e.type === "affordance") del.push(id);
19875
20141
  const floor = schemas.filter((s) => s.binds === "none");
19876
20142
  for (const schema of floor)
19877
- set.push(this._toEntity(
19878
- this._build(schema, tick, state, valence, energyLow, skills, {})
19879
- ));
20143
+ set.push(this._toEntity(this._build(schema, tick, state, valence, energyLow, skills, {})));
19880
20144
  const candidates = [];
19881
20145
  const goalTargets = collectGoalTargets(state);
19882
20146
  const perceptSchema = schemas.find((s) => s.binds === "percept");
@@ -20090,11 +20354,24 @@ var BASE_SWITCH_COST = 0.15;
20090
20354
  var FOCUS_GAIN = 0.01;
20091
20355
  var AWAIT_STALE_TICKS = 15;
20092
20356
  var STALE_DECAY = 0.5;
20357
+ var RUPTURE_SALIENCE_GATE = 0.4;
20358
+ var RUPTURE_WINDOW_TICKS = 2;
20359
+ var STABILITY_RECOVERY = 0.05;
20360
+ var STABILITY_EPSILON = 1e-4;
20361
+ var REVOKE_HINT_WINDOW = 8;
20093
20362
  var ActionSelector = class {
20094
20363
  name = "action-selector";
20095
20364
  _bus = null;
20096
20365
  _lastEntropy = 0;
20097
20366
  _lastDeliberate = false;
20367
+ // EXAFFERENCE P4 follow-up (Channel B): the schema we most recently revoked
20368
+ // under rupture, so the *next* deliberation formed in its wake can own the
20369
+ // interruption in-character ("something changed — I dropped what I was
20370
+ // weighing"). Telemetry-grade instance state, mirroring _lastEntropy.
20371
+ _lastRevoked = null;
20372
+ // ACP §2b: sense-channel percepts buffered off the bus for the next react's
20373
+ // rupture computation (they never become entities). Cross-tick ⇒ FN9.
20374
+ _senseBuffer = [];
20098
20375
  attachBus(bus) {
20099
20376
  this._bus = bus;
20100
20377
  }
@@ -20102,16 +20379,60 @@ var ActionSelector = class {
20102
20379
  return [
20103
20380
  { type: "agency.selection.made", version: 1, validate: () => null },
20104
20381
  { type: "agency.selection.ambiguous", version: 1, validate: () => null },
20105
- { type: "agency.action.preempted", version: 1, validate: () => null }
20382
+ { type: "agency.action.preempted", version: 1, validate: () => null },
20383
+ { type: "agency.situation.rupture", version: 1, validate: () => null },
20384
+ { type: "agency.commitment.revoked", version: 1, validate: () => null }
20106
20385
  ];
20107
20386
  }
20108
20387
  subscribes() {
20109
- return [];
20388
+ return [
20389
+ "senses.*",
20390
+ // Registry #6 (ACP-P3): the model-error term — each carries its
20391
+ // engine's prediction-error salience, ALREADY precision-attenuated for
20392
+ // self-caused swings by the ACP-P2 consumers, so the echo guard
20393
+ // composes by construction: our own action's interoceptive wake
20394
+ // arrives below the rupture gate.
20395
+ "attention.state.changed",
20396
+ "affect.state.changed",
20397
+ "stress.state.changed"
20398
+ ];
20110
20399
  }
20111
- onCognitiveEvent() {
20400
+ /**
20401
+ * Mostly pull-model — but two afferent classes never become entities and
20402
+ * would leave rupture blind: sense-channel percepts
20403
+ * (ACTION_CONDITIONED_PREDICTION §2b) and the model-error state-change
20404
+ * events (registry #6). Buffer both (cross-tick: bus flush at T, consumed
20405
+ * by react at T+1 — FN9-snapshotted); the echo guard is applied at read
20406
+ * time for texts, and at the SOURCE for model errors (ACP-P2 precision).
20407
+ */
20408
+ onCognitiveEvent(e) {
20409
+ const isSense = e.type.startsWith("senses.") && e.type.endsWith(".percept");
20410
+ const isModelError = e.type === "attention.state.changed" || e.type === "affect.state.changed" || e.type === "stress.state.changed";
20411
+ if (!isSense && !isModelError) return;
20412
+ const p = e.payload;
20413
+ this._senseBuffer.push({
20414
+ salience: e.salience,
20415
+ ...isSense && typeof p?.content === "string" ? { text: p.content } : {}
20416
+ });
20112
20417
  }
20113
20418
  snapshot() {
20114
- return { lastEntropy: this._lastEntropy, lastDeliberate: this._lastDeliberate };
20419
+ return {
20420
+ lastEntropy: this._lastEntropy,
20421
+ lastDeliberate: this._lastDeliberate,
20422
+ lastRevoked: this._lastRevoked,
20423
+ senseBuffer: this._senseBuffer
20424
+ };
20425
+ }
20426
+ /** FN9: `_lastRevoked` has behavioral effect (the Channel-B `revokedBy` stamp),
20427
+ * so a restored mind must carry it — a rupture-driven letting-go survives a
20428
+ * snapshot boundary instead of silently losing its narrative thread. */
20429
+ restore(s) {
20430
+ if (typeof s["lastEntropy"] === "number") this._lastEntropy = s["lastEntropy"];
20431
+ if (typeof s["lastDeliberate"] === "boolean") this._lastDeliberate = s["lastDeliberate"];
20432
+ const lr = s["lastRevoked"];
20433
+ this._lastRevoked = lr && typeof lr === "object" && typeof lr.schema === "string" && typeof lr.tick === "number" ? { schema: lr.schema, tick: lr.tick } : null;
20434
+ const sb = s["senseBuffer"];
20435
+ this._senseBuffer = Array.isArray(sb) ? sb.filter((it) => it && typeof it.salience === "number").map((it) => ({ salience: it.salience, ...typeof it.text === "string" ? { text: it.text } : {} })) : [];
20115
20436
  }
20116
20437
  async react(_delta, tick, state, _context) {
20117
20438
  const eligible = [];
@@ -20140,54 +20461,121 @@ var ActionSelector = class {
20140
20461
  let blocking = false;
20141
20462
  let awaiting = null;
20142
20463
  let composite = null;
20464
+ let deliberating = null;
20143
20465
  for (const it of intents) {
20144
- if (it.st === "deliberating") blocking = true;
20145
- else if (it.st === "expanding") composite = { id: it.id, activation: it.activation, schema: it.schema };
20466
+ if (it.st === "deliberating") {
20467
+ blocking = true;
20468
+ deliberating = { id: it.id, schema: it.schema };
20469
+ } else if (it.st === "expanding") composite = { id: it.id, activation: it.activation, schema: it.schema };
20146
20470
  else if (it.st === "awaiting") awaiting = { id: it.id, activation: it.activation, schema: it.schema, target: it.target, dispatchedAt: it.dispatchedAt };
20147
20471
  else if (it.st === "selected") {
20148
20472
  const activeMacroSub = it.parentIntentId !== void 0 && expandingParents.has(it.parentIntentId);
20149
20473
  if (!activeMacroSub) blocking = true;
20150
20474
  }
20151
20475
  }
20152
- const busy = (n) => ({ commands: { metrics: [
20153
- ["agency.field.eligible", n],
20154
- ["agency.selection.busy", 1]
20155
- ] } });
20476
+ const senseEvents = this._senseBuffer;
20477
+ this._senseBuffer = [];
20478
+ const rupture = computeRupture(state, tick, senseEvents);
20479
+ const prevStab = metric2(state, "situation.stability", 1);
20480
+ const nextStabRaw = clamp018(prevStab + STABILITY_RECOVERY * (1 - prevStab) - rupture);
20481
+ const nextStab = 1 - nextStabRaw < STABILITY_EPSILON ? 1 : nextStabRaw;
20482
+ const stabMetrics = rupture > 0 || prevStab < 1 ? [["situation.stability", nextStab]] : [];
20483
+ if (rupture > 0 && this._bus) {
20484
+ try {
20485
+ this._bus.publish({
20486
+ type: "agency.situation.rupture",
20487
+ version: 1,
20488
+ sourceEngine: this.name,
20489
+ salience: rupture,
20490
+ payload: { rupture, stability: nextStab, tick }
20491
+ });
20492
+ } catch (err) {
20493
+ logger.warn(`[selector] rupture publish failed: ${err instanceof Error ? err.message : String(err)}`);
20494
+ }
20495
+ }
20496
+ const busy = (n) => ({
20497
+ commands: {
20498
+ metrics: [
20499
+ ["agency.field.eligible", n],
20500
+ ["agency.selection.busy", 1],
20501
+ ...stabMetrics
20502
+ ]
20503
+ }
20504
+ });
20505
+ if (deliberating && rupture >= RUPTURE_REVOKE_GATE) {
20506
+ if (this._bus) {
20507
+ try {
20508
+ this._bus.publish({
20509
+ type: "agency.commitment.revoked",
20510
+ version: 1,
20511
+ sourceEngine: this.name,
20512
+ salience: 0.85,
20513
+ payload: { from: deliberating.schema, reason: "exafferent-rupture", rupture, tick }
20514
+ });
20515
+ } catch (err) {
20516
+ logger.warn(`[selector] revoked publish failed: ${err instanceof Error ? err.message : String(err)}`);
20517
+ }
20518
+ }
20519
+ this._lastRevoked = { schema: deliberating.schema, tick };
20520
+ return {
20521
+ commands: {
20522
+ set: [revocationEntity(deliberating.id, deliberating.schema, rupture, tick)],
20523
+ metrics: [
20524
+ ["agency.field.eligible", eligible.length],
20525
+ ["agency.selection.busy", 1],
20526
+ ["agency.commitment.revoked", 1],
20527
+ ...stabMetrics
20528
+ ]
20529
+ }
20530
+ };
20531
+ }
20156
20532
  if (blocking) return busy(eligible.length);
20157
20533
  if (eligible.length === 0)
20158
- return { commands: { metrics: [["agency.field.eligible", 0], ["agency.selection.busy", awaiting || composite ? 1 : 0]] } };
20534
+ return {
20535
+ commands: {
20536
+ metrics: [
20537
+ ["agency.field.eligible", 0],
20538
+ ["agency.selection.busy", awaiting || composite ? 1 : 0],
20539
+ ...stabMetrics
20540
+ ]
20541
+ }
20542
+ };
20159
20543
  const bias = buildBias(state);
20160
- const effSwitchCost = effectiveSwitchCost(state);
20544
+ const effSwitchCost = effectiveSwitchCost(state) * (1 - rupture);
20161
20545
  const weights = effectiveWeights(state);
20162
20546
  const scored = eligible.map((a) => ({ affordance: a, activation: scoreAffordance(a, bias, weights) })).sort((x, y) => y.activation - x.activation);
20163
20547
  const winner = scored[0];
20164
20548
  if (!winner) return busy(eligible.length);
20549
+ let compositeTombstone;
20550
+ let compositeFrom;
20165
20551
  if (composite) {
20166
20552
  const different = winner.affordance.schema !== composite.schema;
20167
20553
  const switchCost = effSwitchCost * (1 - stakes(winner.affordance, bias));
20168
- if (different && winner.activation > composite.activation + switchCost) {
20169
- if (this._bus) try {
20554
+ if (!different || winner.activation <= composite.activation + switchCost)
20555
+ return busy(eligible.length);
20556
+ if (this._bus)
20557
+ try {
20170
20558
  this._bus.publish({
20171
20559
  type: "agency.action.preempted",
20172
20560
  version: 1,
20173
20561
  sourceEngine: this.name,
20174
20562
  salience: 0.8,
20175
- payload: { from: composite.schema, to: winner.affordance.schema, activation: winner.activation, tick }
20563
+ payload: {
20564
+ from: composite.schema,
20565
+ to: winner.affordance.schema,
20566
+ activation: winner.activation,
20567
+ tick
20568
+ }
20176
20569
  });
20177
20570
  } catch (err) {
20178
20571
  logger.warn(`[selector] preempt publish failed: ${err instanceof Error ? err.message : String(err)}`);
20179
20572
  }
20180
- return { commands: { delete: [composite.id], metrics: [
20181
- ["agency.field.eligible", eligible.length],
20182
- ["agency.selection.busy", 1],
20183
- ["agency.selection.preempted", 1]
20184
- ] } };
20185
- }
20186
- return busy(eligible.length);
20573
+ compositeTombstone = composite.id;
20574
+ compositeFrom = composite.schema;
20187
20575
  }
20188
20576
  let preemptDelete;
20189
- let preemptedFrom;
20190
- if (awaiting) {
20577
+ let preemptedFrom = compositeFrom;
20578
+ if (awaiting && !compositeTombstone) {
20191
20579
  const sameAction = winner.affordance.schema === awaiting.schema && (winner.affordance.targetEntityId ?? "") === awaiting.target;
20192
20580
  if (sameAction) return busy(eligible.length);
20193
20581
  const staleness = Math.min(1, (tick - awaiting.dispatchedAt) / AWAIT_STALE_TICKS);
@@ -20210,6 +20598,13 @@ var ActionSelector = class {
20210
20598
  const deliberate = margin < marginGate || stk > stakesGate;
20211
20599
  this._lastEntropy = entropy;
20212
20600
  this._lastDeliberate = deliberate;
20601
+ let revokedBy;
20602
+ if (this._lastRevoked) {
20603
+ if (deliberate && tick - this._lastRevoked.tick <= REVOKE_HINT_WINDOW)
20604
+ revokedBy = this._lastRevoked.schema;
20605
+ if (revokedBy || tick - this._lastRevoked.tick > REVOKE_HINT_WINDOW)
20606
+ this._lastRevoked = null;
20607
+ }
20213
20608
  const intent = {
20214
20609
  id: `agency-intent-${tick}`,
20215
20610
  type: "agency.intent",
@@ -20252,6 +20647,9 @@ var ActionSelector = class {
20252
20647
  // interrupted, so the deliberation facet can own the interruption in-character
20253
20648
  // ("I was about to X, but this pulled me away"). Only meaningful while deliberating.
20254
20649
  ...preemptedFrom ? { preemptedFrom } : {},
20650
+ // Channel B: this deliberation forms in the wake of a rupture-driven
20651
+ // revocation — the facet can own "something changed, I let go of X".
20652
+ ...revokedBy ? { revokedBy } : {},
20255
20653
  tick
20256
20654
  }
20257
20655
  };
@@ -20290,7 +20688,9 @@ var ActionSelector = class {
20290
20688
  }
20291
20689
  }
20292
20690
  const commands = {
20293
- set: [intent],
20691
+ // A composite preemption rides along as a tombstone (never a delete — the
20692
+ // executor's in-tick macro-advance would resurrect the parent).
20693
+ set: compositeTombstone ? [intent, revocationEntity(compositeTombstone, compositeFrom ?? "", rupture, tick)] : [intent],
20294
20694
  ...preemptDelete ? { delete: [preemptDelete] } : {},
20295
20695
  metrics: [
20296
20696
  ["agency.field.eligible", eligible.length],
@@ -20299,7 +20699,8 @@ var ActionSelector = class {
20299
20699
  ["agency.selection.margin", margin],
20300
20700
  ["agency.selection.deliberate", deliberate ? 1 : 0],
20301
20701
  ["agency.selection.preempted", preemptedFrom ? 1 : 0],
20302
- ["agency.selection.activation", winner.activation]
20702
+ ["agency.selection.activation", winner.activation],
20703
+ ...stabMetrics
20303
20704
  ]
20304
20705
  };
20305
20706
  return { commands };
@@ -20309,7 +20710,29 @@ function effectiveSwitchCost(state) {
20309
20710
  const params = readEffectiveParams(state, "engine-config-action-selector");
20310
20711
  const base = num2(params["switchCost"], BASE_SWITCH_COST);
20311
20712
  const focusTicks = metric2(state, "task_switch.current_focus_ticks", 0);
20312
- return base * (1 + focusTicks * FOCUS_GAIN);
20713
+ const stability = metric2(state, "situation.stability", 1);
20714
+ return base * (1 + focusTicks * FOCUS_GAIN * stability);
20715
+ }
20716
+ function computeRupture(state, tick, senseEvents = []) {
20717
+ let maxSalience = 0;
20718
+ for (const e of state.entities.values()) {
20719
+ if (e.type !== "percept") continue;
20720
+ const m = e.metadata;
20721
+ if (str2(m?.["provenance"]) !== "exafferent") continue;
20722
+ const pTick = num2(m?.["tick"], -1);
20723
+ if (pTick < 0 || tick - pTick > RUPTURE_WINDOW_TICKS) continue;
20724
+ const s = num2(m?.["salience"], 0);
20725
+ if (s > maxSalience) maxSalience = s;
20726
+ }
20727
+ if (senseEvents.length > 0) {
20728
+ const live = liveConsequences(state.entities, tick);
20729
+ for (const ev of senseEvents) {
20730
+ if (ev.text !== void 0 && live.length > 0 && matchConsequenceText(live, ev.text)) continue;
20731
+ if (ev.salience > maxSalience) maxSalience = ev.salience;
20732
+ }
20733
+ }
20734
+ if (maxSalience <= RUPTURE_SALIENCE_GATE) return 0;
20735
+ return clamp018((maxSalience - RUPTURE_SALIENCE_GATE) / (1 - RUPTURE_SALIENCE_GATE));
20313
20736
  }
20314
20737
  function effectiveWeights(state) {
20315
20738
  const p = readEffectiveParams(state, "engine-config-action-selector");
@@ -20410,19 +20833,28 @@ var DeliberationEngine = class {
20410
20833
  return { deliberations: this._deliberations };
20411
20834
  }
20412
20835
  async react(_delta, tick, state, _context) {
20836
+ const revoked = revokedIntentIds(state.entities, tick);
20837
+ const del = [];
20413
20838
  let target = null;
20414
20839
  for (const [id2, e] of state.entities) {
20415
20840
  if (e.type !== "agency.intent") continue;
20416
20841
  if (str3(e.metadata?.["status"]) !== "deliberating") continue;
20842
+ if (revoked.has(id2)) {
20843
+ del.push(id2, revocationId(id2));
20844
+ continue;
20845
+ }
20417
20846
  const meta2 = e.metadata ?? {};
20418
20847
  if (!target || id2 < target.id) target = { id: id2, meta: meta2 };
20419
20848
  }
20420
- if (!target) return { commands: {} };
20849
+ if (!target) return del.length > 0 ? { commands: { delete: del } } : { commands: {} };
20421
20850
  const { id, meta } = target;
20422
20851
  const provisional = str3(meta["schema"]) ?? "wait";
20423
20852
  const candidates = Array.isArray(meta["candidates"]) ? meta["candidates"] : [];
20424
20853
  if (!this._provider)
20425
- return { commands: { set: [this._commit(id, meta, provisional, candidates, "no-executive")] } };
20854
+ return { commands: {
20855
+ set: [this._commit(id, meta, provisional, candidates, "no-executive")],
20856
+ ...del.length > 0 ? { delete: del } : {}
20857
+ } };
20426
20858
  const chosen = await this._deliberate(state, candidates, provisional, meta);
20427
20859
  this._deliberations++;
20428
20860
  if (this._bus) {
@@ -20440,6 +20872,7 @@ var DeliberationEngine = class {
20440
20872
  return {
20441
20873
  commands: {
20442
20874
  set: [this._commit(id, meta, chosen, candidates, "facet")],
20875
+ ...del.length > 0 ? { delete: del } : {},
20443
20876
  metrics: [["agency.deliberation.count", 1]]
20444
20877
  }
20445
20878
  };
@@ -20497,7 +20930,10 @@ var DeliberationEngine = class {
20497
20930
  _buildFocusContent(state, candidates, meta) {
20498
20931
  const lines = [];
20499
20932
  const preemptedFrom = str3(meta["preemptedFrom"]);
20500
- if (preemptedFrom)
20933
+ const revokedBy = str3(meta["revokedBy"]);
20934
+ if (revokedBy)
20935
+ lines.push(`Something in my situation just shifted and I let go of what I was weighing ("${revokedBy}"). Decide afresh what to do now:`);
20936
+ else if (preemptedFrom)
20501
20937
  lines.push(`I just broke off a pending action ("${preemptedFrom}") because something more pressing pulled at me. Decide what to do now:`);
20502
20938
  else
20503
20939
  lines.push("My automatic action-selection was uncertain. Candidate actions:");
@@ -20670,6 +21106,19 @@ var MotorSchemaExecutor = class {
20670
21106
  const metrics = [];
20671
21107
  const energy = state.metrics.get("energy.level") ?? 100;
20672
21108
  const stress = state.metrics.get("stress.load") ?? 0;
21109
+ for (const [id, e] of state.entities) {
21110
+ if (e.type !== CONSEQUENCE_TYPE) continue;
21111
+ if (tick >= num3(e.metadata?.["expiresAt"], 0)) del.push(id);
21112
+ }
21113
+ del.push(...staleRevocationIds(state.entities, tick));
21114
+ const revoked = revokedIntentIds(state.entities, tick);
21115
+ if (revoked.size > 0)
21116
+ for (const [id, e] of state.entities) {
21117
+ if (e.type !== "agency.intent") continue;
21118
+ const parentId = str5(e.metadata?.["parentIntentId"]);
21119
+ if (revoked.has(id)) del.push(id, revocationId(id));
21120
+ else if (parentId && revoked.has(parentId)) del.push(id);
21121
+ }
20673
21122
  for (const [id, e] of state.entities) {
20674
21123
  if (e.type !== "agency.intent" || str5(e.metadata?.["status"]) !== "awaiting") continue;
20675
21124
  const dispatchedAt = num3(e.metadata?.["dispatchedAt"], tick);
@@ -20696,6 +21145,9 @@ var MotorSchemaExecutor = class {
20696
21145
  const selected = [...state.entities.entries()].filter(([, e]) => e.type === "agency.intent" && str5(e.metadata?.["status"]) === "selected").sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
20697
21146
  let enactedCount = 0;
20698
21147
  for (const [id, e] of selected) {
21148
+ if (revoked.has(id)) continue;
21149
+ const parentOf = str5(e.metadata?.["parentIntentId"]);
21150
+ if (parentOf && revoked.has(parentOf)) continue;
20699
21151
  const intent = readIntent(id, e.metadata);
20700
21152
  const schema = this._resolve(intent.schema);
20701
21153
  if (schema?.kind === "composite" && schema.composedOf && schema.composedOf.length > 0) {
@@ -20744,6 +21196,18 @@ var MotorSchemaExecutor = class {
20744
21196
  predictedValence: predicted.expectedValence
20745
21197
  }
20746
21198
  });
21199
+ const awaitingText = enaction.mode === "communicate" ? str5(intent.parameters["content"]) ?? firstMessage(intent.parameters["messages"]) : void 0;
21200
+ set.push(consequenceEntity({
21201
+ intentId: id,
21202
+ schema: intent.schema,
21203
+ mode: enaction.mode === "communicate" ? "communicate" : "external",
21204
+ ...enaction.mode === "communicate" ? { effector: COMM_SCHEMA_TO_EFFECTOR[intent.schema] ?? intent.schema } : {},
21205
+ ...intent.targetEntityId ? { targetEntityId: intent.targetEntityId } : {},
21206
+ ...awaitingText ? { text: awaitingText, textHash: fnv1a(awaitingText) } : {},
21207
+ paramsHash: fnv1a(paramsKey(intent.parameters)),
21208
+ expiresAt: tick + CONSEQUENCE_TTL_TICKS,
21209
+ tick
21210
+ }));
20747
21211
  this._emitDispatch(intent, enaction.mode, tick);
20748
21212
  metrics.push([enaction.mode === "communicate" ? "agency.communicate.dispatched" : "agency.invocation.dispatched", 1]);
20749
21213
  }
@@ -20910,6 +21374,18 @@ var MotorSchemaExecutor = class {
20910
21374
  };
20911
21375
  set.push(outcomeEntity(tick, intent, out, predicted));
20912
21376
  del.push(id);
21377
+ if (result.success)
21378
+ set.push(consequenceEntity({
21379
+ intentId: id,
21380
+ schema: intent.schema,
21381
+ mode: "communicate",
21382
+ effector,
21383
+ ...intent.targetEntityId ? { targetEntityId: intent.targetEntityId } : {},
21384
+ textHash: fnv1a(bubbles.join("\n")),
21385
+ text: bubbles.join("\n"),
21386
+ expiresAt: tick + CONSEQUENCE_TTL_TICKS,
21387
+ tick
21388
+ }));
20913
21389
  this._emitEnacted(intent, out, predicted, tick);
20914
21390
  this._emitActionOutcome(
20915
21391
  intent,
@@ -21048,6 +21524,10 @@ function errMsg(err) {
21048
21524
 
21049
21525
  // src/cognition/agency/engines/reafference.engine.ts
21050
21526
  var PROC_THRESHOLD2 = 0.6;
21527
+ var SENSORY_SOFT_QUALITY = 0.6;
21528
+ var SENSORY_VALENCE_SPAN_ENTITY = 0.25;
21529
+ var SENSORY_VALENCE_SPAN_AMBIENT = 0.1;
21530
+ var SENSORY_SUCCESS_FLOOR = 0.5;
21051
21531
  var ReafferenceEngine = class {
21052
21532
  name = "reafference";
21053
21533
  _repertoire;
@@ -21097,14 +21577,64 @@ var ReafferenceEngine = class {
21097
21577
  const set = [];
21098
21578
  const del = [];
21099
21579
  const metrics = [];
21100
- let updates = 0;
21101
- let discovered = 0;
21580
+ const outcomes = [];
21581
+ const gradedIntentIds = /* @__PURE__ */ new Set();
21102
21582
  for (const [id, e] of state.entities) {
21103
21583
  if (e.type !== "agency.outcome") continue;
21104
21584
  const m = e.metadata ?? {};
21585
+ outcomes.push({ id, meta: m, fromState: true });
21586
+ const iid = str6(m["intentId"]);
21587
+ if (iid) gradedIntentIds.add(iid);
21588
+ }
21589
+ const awaiting = /* @__PURE__ */ new Map();
21590
+ for (const [id, e] of state.entities) {
21591
+ if (e.type !== "agency.intent" || str6(e.metadata?.["status"]) !== "awaiting") continue;
21592
+ const m = e.metadata ?? {};
21593
+ if (tick - num4(m["dispatchedAt"], tick) >= AWAIT_TIMEOUT) continue;
21594
+ awaiting.set(id, {
21595
+ schema: str6(m["schema"]) ?? "",
21596
+ predictedReward: num4(m["predictedReward"], 0.5),
21597
+ predictedValence: num4(m["predictedValence"], 0)
21598
+ });
21599
+ }
21600
+ let sensory = 0;
21601
+ const sensedIntentIds = /* @__PURE__ */ new Set();
21602
+ for (const [, e] of state.entities) {
21603
+ if (e.type !== "percept") continue;
21604
+ const m = e.metadata ?? {};
21605
+ if (str6(m["provenance"]) !== "reafferent") continue;
21606
+ const iid = str6(m["sourceIntentId"]);
21607
+ if (!iid || gradedIntentIds.has(iid) || sensedIntentIds.has(iid)) continue;
21608
+ const aw = awaiting.get(iid);
21609
+ if (!aw || !aw.schema) continue;
21610
+ sensedIntentIds.add(iid);
21611
+ sensory++;
21612
+ const feltRaw = m["valence"];
21613
+ const felt = typeof feltRaw === "number" && Number.isFinite(feltRaw) ? feltRaw : void 0;
21614
+ const span = str6(m["valenceSource"]) === "entity" ? SENSORY_VALENCE_SPAN_ENTITY : SENSORY_VALENCE_SPAN_AMBIENT;
21615
+ const quality = felt === void 0 ? SENSORY_SOFT_QUALITY : clamp0111(SENSORY_SOFT_QUALITY + Math.max(-1, Math.min(1, felt)) * span);
21616
+ outcomes.push({ id: `agency-outcome-${tick}-${iid}-sensory`, fromState: false, meta: {
21617
+ schema: aw.schema,
21618
+ intentId: iid,
21619
+ success: quality >= SENSORY_SUCCESS_FLOOR,
21620
+ outcomeQuality: quality,
21621
+ valence: felt ?? aw.predictedValence,
21622
+ predictedReward: aw.predictedReward,
21623
+ predictedValence: aw.predictedValence,
21624
+ surprise: clamp0111(Math.abs(aw.predictedReward - quality)),
21625
+ mode: "external",
21626
+ reconciled: true,
21627
+ sensory: true,
21628
+ ...felt !== void 0 ? { sensoryValence: felt, valenceSource: str6(m["valenceSource"]) } : {},
21629
+ tick
21630
+ } });
21631
+ }
21632
+ let updates = 0;
21633
+ let discovered = 0;
21634
+ for (const { id, meta: m, fromState } of outcomes) {
21105
21635
  const schema = str6(m["schema"]);
21106
21636
  if (!schema) {
21107
- del.push(id);
21637
+ if (fromState) del.push(id);
21108
21638
  continue;
21109
21639
  }
21110
21640
  const { skill, proceduralized } = this._repertoire.recordOutcome({
@@ -21116,7 +21646,7 @@ var ReafferenceEngine = class {
21116
21646
  tick
21117
21647
  });
21118
21648
  set.push(skillEntity(skill));
21119
- del.push(id);
21649
+ if (fromState) del.push(id);
21120
21650
  const intentId = str6(m["intentId"]);
21121
21651
  if (intentId) del.push(intentId);
21122
21652
  updates++;
@@ -21144,7 +21674,8 @@ var ReafferenceEngine = class {
21144
21674
  ["agency.learning.updates", updates],
21145
21675
  ["agency.discovered.count", discovered],
21146
21676
  ["agency.skill.count", skills.size],
21147
- ["agency.habitual.count", habitual]
21677
+ ["agency.habitual.count", habitual],
21678
+ ["agency.sensory.confirmed", sensory]
21148
21679
  );
21149
21680
  return { commands: { set, delete: del, metrics } };
21150
21681
  }
@@ -21228,6 +21759,9 @@ function str6(v) {
21228
21759
  function num4(v, fallback) {
21229
21760
  return typeof v === "number" && Number.isFinite(v) ? v : fallback;
21230
21761
  }
21762
+ function clamp0111(n) {
21763
+ return n < 0 ? 0 : n > 1 ? 1 : n;
21764
+ }
21231
21765
 
21232
21766
  // src/cognition/config.mirror.entities.ts
21233
21767
  function buildEngineConfigEntities(config, executiveInterval) {
@@ -22198,12 +22732,13 @@ async function checkIdentityCoherence(input, reviewer) {
22198
22732
  return { ok: !issues.some((i) => i.severity === "error"), ran: true, issues, raw: text };
22199
22733
  }
22200
22734
  async function reviewIdentityCoherence(input, opts = {}) {
22735
+ const provider = process.env.WILL_LLM_PROVIDER ?? "anthropic";
22201
22736
  const director = new LLMDirector({
22202
22737
  willId: opts.willId ?? "identity-coherence",
22203
- model: process.env.WILL_LLM_MODEL ?? "claude-sonnet-4-5-20250929",
22738
+ model: process.env.WILL_LLM_MODEL ?? defaultModelFor(provider),
22204
22739
  maxOutputTokens: 512,
22205
22740
  apiKey: process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? "",
22206
- provider: process.env.WILL_LLM_PROVIDER ?? "anthropic",
22741
+ provider,
22207
22742
  sessionLogger: null,
22208
22743
  baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
22209
22744
  // When a per-Will tracker is supplied, the creation-time review records under
@@ -23958,6 +24493,10 @@ var OutboxController = class {
23958
24493
  summary: delivered ? `My message was delivered successfully.` : `My message failed to reach the recipient.`,
23959
24494
  salience: delivered ? 0.35 : 0.6,
23960
24495
  changeType: delivered ? "delivered" : "failed",
24496
+ // Reafference by construction — this percept describes our own action's
24497
+ // outcome ("ear hears the word"). Tagged, not attenuated: it is the ack
24498
+ // surface, not a content echo (EXAFFERENCE P2).
24499
+ provenance: "reafferent",
23961
24500
  messageId
23962
24501
  }
23963
24502
  });
@@ -24359,7 +24898,7 @@ var InboundQueue = class {
24359
24898
  function reconcileInvocation(intentId, schema, result, tick, predicted = { reward: 0.5, valence: 0 }, provenance = {}) {
24360
24899
  const outcomeQuality = result.outcomeQuality ?? (result.success ? 0.8 : 0.1);
24361
24900
  const valence = result.valence ?? (result.success ? 0.2 : -0.2);
24362
- const surprise = clamp0111(Math.abs(predicted.reward - outcomeQuality));
24901
+ const surprise = clamp0112(Math.abs(predicted.reward - outcomeQuality));
24363
24902
  return {
24364
24903
  id: `agency-outcome-${tick}-${intentId}`,
24365
24904
  type: "agency.outcome",
@@ -24381,7 +24920,7 @@ function reconcileInvocation(intentId, schema, result, tick, predicted = { rewar
24381
24920
  }
24382
24921
  };
24383
24922
  }
24384
- function clamp0111(n) {
24923
+ function clamp0112(n) {
24385
24924
  return n < 0 ? 0 : n > 1 ? 1 : n;
24386
24925
  }
24387
24926
 
@@ -25760,7 +26299,9 @@ var Will = class _Will {
25760
26299
  }
25761
26300
  // ── Internals ──────────────────────────────────────────────
25762
26301
  _buildConfig(id, opts) {
25763
- const useMock = (opts.llm ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : "mock")) === "mock";
26302
+ const mode = opts.llm ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : process.env.ZAI_API_KEY ? "glm" : "mock");
26303
+ const useMock = mode === "mock";
26304
+ const llmConfig = mode === "glm" ? { provider: "glm", ...opts.llmConfig } : opts.llmConfig;
25764
26305
  return {
25765
26306
  id,
25766
26307
  name: opts.name,
@@ -25772,7 +26313,7 @@ var Will = class _Will {
25772
26313
  },
25773
26314
  anatomy: opts.anatomy ?? "mind",
25774
26315
  model: opts.model,
25775
- llm: opts.llmConfig,
26316
+ llm: llmConfig,
25776
26317
  testMode: useMock,
25777
26318
  persistentMemory: opts.persist ?? false,
25778
26319
  snapshotInterval: 100,
@@ -25955,21 +26496,32 @@ function routeLogsToStderr() {
25955
26496
  function slug2(s) {
25956
26497
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
25957
26498
  }
26499
+ function resolveLlmMode() {
26500
+ const explicit = process.env.WILL_LLM;
26501
+ if (explicit) return explicit;
26502
+ if (process.env.ANTHROPIC_API_KEY) return "anthropic";
26503
+ if (process.env.ZAI_API_KEY) return "glm";
26504
+ return "mock";
26505
+ }
26506
+ function resolveLlmKey(mode) {
26507
+ return process.env.WILL_LLM_API_KEY ?? (mode === "glm" ? process.env.ZAI_API_KEY : process.env.ANTHROPIC_API_KEY);
26508
+ }
25958
26509
  async function preflightLLM(anatomy) {
25959
- const mode = process.env.WILL_LLM ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : "mock");
26510
+ const mode = resolveLlmMode();
25960
26511
  if (mode === "mock" || anatomy === "reflex") return;
25961
- const key = process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY;
26512
+ const key = resolveLlmKey(mode);
25962
26513
  if (!key) {
25963
- console.error("[will] WILL_LLM=anthropic but no ANTHROPIC_API_KEY / WILL_LLM_API_KEY is set.");
26514
+ const expected = mode === "glm" ? "ZAI_API_KEY" : "ANTHROPIC_API_KEY";
26515
+ console.error(`[will] WILL_LLM=${mode} but no ${expected} / WILL_LLM_API_KEY is set.`);
25964
26516
  console.error("[will] The Will would boot, perceive, and never speak. Set a key, or run keyless with WILL_LLM=mock.");
25965
26517
  process.exit(2);
25966
26518
  }
25967
- const base = process.env.WILL_LLM_BASE_URL ?? "https://api.anthropic.com/v1";
25968
- const model = process.env.WILL_LLM_MODEL ?? "claude-haiku-4-5-20251001";
26519
+ const base = process.env.WILL_LLM_BASE_URL ?? defaultBaseFor(mode);
26520
+ const model = process.env.WILL_LLM_MODEL ?? (mode === "glm" ? defaultModelFor("glm") : "claude-haiku-4-5-20251001");
25969
26521
  try {
25970
26522
  const res = await fetch(`${base}/messages`, {
25971
26523
  method: "POST",
25972
- headers: { "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": key },
26524
+ headers: anthropicWireHeaders(mode, key),
25973
26525
  body: JSON.stringify({ model, max_tokens: 1, messages: [{ role: "user", content: "ping" }] }),
25974
26526
  signal: AbortSignal.timeout(2e4)
25975
26527
  });
@@ -26474,21 +27026,200 @@ async function createDiscordClient() {
26474
27026
  });
26475
27027
  }
26476
27028
 
27029
+ // src/channels/whatsapp.ts
27030
+ var WHATSAPP_MESSAGE_LIMIT = 65536;
27031
+ var isGroupJid = (jid) => jid.endsWith("@g.us");
27032
+ var bareId = (jid) => jid.split("@")[0].split(":")[0];
27033
+ var dmJidFor = (userId) => `${userId}@s.whatsapp.net`;
27034
+ function textOf(m) {
27035
+ const msg = m.message;
27036
+ return msg?.conversation ?? msg?.extendedTextMessage?.text ?? msg?.imageMessage?.caption ?? msg?.videoMessage?.caption ?? "";
27037
+ }
27038
+ async function connectWhatsApp(will, opts = {}) {
27039
+ const log = opts.log ?? ((m) => console.error(`[will:whatsapp] ${m}`));
27040
+ const roster = new ChannelRoster(opts.rosterPath ?? `.will/${will.id}.whatsapp.json`);
27041
+ const allowed = opts.chats?.length ? new Set(opts.chats) : null;
27042
+ let closed = false;
27043
+ const socket = opts.socket ?? await createWhatsAppSocket({
27044
+ authPath: opts.authPath ?? `.will/${will.id}.wa-auth`,
27045
+ log,
27046
+ stillOpen: () => !closed
27047
+ });
27048
+ let lastActiveChatId = opts.homeChatId ?? null;
27049
+ socket.ev.on("messages.upsert", ({ messages, type }) => {
27050
+ if (type && type !== "notify") return;
27051
+ for (const m of messages) void onMessage(m);
27052
+ });
27053
+ async function onMessage(m) {
27054
+ const jid = m.key.remoteJid;
27055
+ if (!jid || m.key.fromMe || !m.message || m.messageStubType) return;
27056
+ if (jid.endsWith("@broadcast") || jid.endsWith("@newsletter")) return;
27057
+ if (allowed && !allowed.has(jid)) return;
27058
+ const isGroup = isGroupJid(jid);
27059
+ const senderJid = isGroup ? m.key.participant : jid;
27060
+ if (!senderJid) return;
27061
+ const selfId = socket.user ? bareId(socket.user.id) : null;
27062
+ const mentioned = m.message.extendedTextMessage?.contextInfo?.mentionedJid ?? [];
27063
+ const addressed = !isGroup || selfId != null && mentioned.some((j) => bareId(j) === selfId);
27064
+ if (opts.mentionOnly && !addressed) return;
27065
+ const userId = bareId(senderJid);
27066
+ const entityId = `whatsapp:${userId}`;
27067
+ const speaker = m.pushName ?? void 0;
27068
+ roster.record({
27069
+ entityId,
27070
+ userId,
27071
+ ...speaker ? { displayName: speaker } : {},
27072
+ ...isGroup ? { lastChannelId: jid } : { dmChannelId: jid }
27073
+ });
27074
+ if (isGroup) lastActiveChatId = jid;
27075
+ if (addressed) await socket.sendPresenceUpdate?.("composing", jid).catch(() => {
27076
+ });
27077
+ const text = textOf(m);
27078
+ if (!text.trim()) return;
27079
+ await will.perceive({
27080
+ text,
27081
+ from: entityId,
27082
+ thread: `whatsapp:${jid}`,
27083
+ ...speaker ? { speaker } : {}
27084
+ });
27085
+ }
27086
+ will.on("message", (m) => {
27087
+ if (!closed) void deliver(m);
27088
+ });
27089
+ async function deliver(m) {
27090
+ const peer = m.to ? roster.resolve(m.to) : void 0;
27091
+ const chunks = chunkText(m.content, WHATSAPP_MESSAGE_LIMIT);
27092
+ const derivedDm = m.to?.startsWith("whatsapp:") ? dmJidFor(m.to.slice("whatsapp:".length)) : void 0;
27093
+ const targets = [peer?.lastChannelId, peer?.dmChannelId, derivedDm, opts.homeChatId ?? void 0, lastActiveChatId ?? void 0];
27094
+ for (const jid of targets) {
27095
+ if (!jid) continue;
27096
+ try {
27097
+ for (const chunk of chunks) await socket.sendMessage(jid, { text: chunk });
27098
+ return;
27099
+ } catch {
27100
+ }
27101
+ }
27102
+ log(`no route for utterance to '${m.to}' \u2014 dropped (${m.content.length} chars)`);
27103
+ }
27104
+ return {
27105
+ kind: "whatsapp",
27106
+ async start() {
27107
+ log(`${will.name} is present on WhatsApp${socket.user ? ` as ${bareId(socket.user.id)}` : ""}`);
27108
+ },
27109
+ async close() {
27110
+ if (closed) return;
27111
+ closed = true;
27112
+ roster.flush();
27113
+ try {
27114
+ socket.end?.();
27115
+ } catch {
27116
+ }
27117
+ }
27118
+ };
27119
+ }
27120
+ async function createWhatsAppSocket(o) {
27121
+ let baileys;
27122
+ try {
27123
+ baileys = await import('baileys');
27124
+ } catch {
27125
+ throw new Error("baileys is not installed (it is an optionalDependency) \u2014 run `bun add baileys` / `npm i baileys` and retry.");
27126
+ }
27127
+ const { default: makeWASocket, useMultiFileAuthState, DisconnectReason } = baileys;
27128
+ const noop = () => {
27129
+ };
27130
+ const logger2 = {
27131
+ level: "silent",
27132
+ child() {
27133
+ return logger2;
27134
+ },
27135
+ trace: noop,
27136
+ debug: noop,
27137
+ info: noop,
27138
+ warn: noop,
27139
+ error: noop,
27140
+ fatal: noop
27141
+ };
27142
+ const subscribers = [];
27143
+ let inner = null;
27144
+ const facade = {
27145
+ get user() {
27146
+ return inner?.user ? { id: inner.user.id, name: inner.user.name ?? void 0 } : null;
27147
+ },
27148
+ ev: { on: (_e, fn) => {
27149
+ subscribers.push(fn);
27150
+ } },
27151
+ sendMessage: (jid, content) => {
27152
+ if (!inner) return Promise.reject(new Error("whatsapp socket not connected"));
27153
+ return inner.sendMessage(jid, content);
27154
+ },
27155
+ sendPresenceUpdate: (state2, jid) => inner?.sendPresenceUpdate(state2, jid) ?? Promise.resolve(),
27156
+ end: () => inner?.end(void 0)
27157
+ };
27158
+ const { state, saveCreds } = await useMultiFileAuthState(o.authPath);
27159
+ await new Promise((resolveOpen, rejectOpen) => {
27160
+ let opened = false;
27161
+ function connect2() {
27162
+ const sock = makeWASocket({ auth: state, logger: logger2 });
27163
+ inner = sock;
27164
+ sock.ev.on("creds.update", saveCreds);
27165
+ sock.ev.on("messages.upsert", (u) => {
27166
+ for (const fn of subscribers) fn(u);
27167
+ });
27168
+ sock.ev.on("connection.update", (update) => {
27169
+ const { connection, lastDisconnect, qr } = update;
27170
+ if (qr) void printQr(qr, o.log);
27171
+ if (connection === "open" && !opened) {
27172
+ opened = true;
27173
+ resolveOpen();
27174
+ }
27175
+ if (connection === "close") {
27176
+ const code = lastDisconnect?.error?.output?.statusCode;
27177
+ if (code === DisconnectReason.loggedOut) {
27178
+ const err = new Error("WhatsApp unlinked this device (logged out) \u2014 delete the auth dir and pair again.");
27179
+ o.log(err.message);
27180
+ if (!opened) rejectOpen(err);
27181
+ return;
27182
+ }
27183
+ if (o.stillOpen()) {
27184
+ o.log(`connection closed (status ${code ?? "?"}) \u2014 reconnecting\u2026`);
27185
+ setTimeout(connect2, 3e3);
27186
+ }
27187
+ }
27188
+ });
27189
+ }
27190
+ connect2();
27191
+ });
27192
+ return facade;
27193
+ }
27194
+ async function printQr(qr, log) {
27195
+ log("pair this device: WhatsApp \u2192 Settings \u2192 Linked devices \u2192 Link a device");
27196
+ try {
27197
+ const qrt = await import('qrcode-terminal');
27198
+ (qrt.default ?? qrt).generate(qr, { small: true });
27199
+ } catch {
27200
+ log(`qrcode-terminal not installed \u2014 raw pairing code:
27201
+ ${qr}`);
27202
+ }
27203
+ }
27204
+
26477
27205
  // src/cli.ts
26478
27206
  routeLogsToStderr();
26479
- var USAGE = `usage: will <mcp | serve | discord>
27207
+ var USAGE = `usage: will <mcp | serve | discord | whatsapp>
26480
27208
 
26481
- mcp host a persistent mind over MCP stdio (Claude Desktop / Claude Code)
26482
- serve host a persistent mind over HTTP (any language; WILL_PORT, default 7777)
26483
- discord put a persistent mind in a Discord server (DISCORD_BOT_TOKEN; optional
26484
- WILL_DISCORD_CHANNELS, WILL_DISCORD_MENTION_ONLY, WILL_DISCORD_HOME_CHANNEL)
27209
+ mcp host a persistent mind over MCP stdio (Claude Desktop / Claude Code)
27210
+ serve host a persistent mind over HTTP (any language; WILL_PORT, default 7777)
27211
+ discord put a persistent mind in a Discord server (DISCORD_BOT_TOKEN; optional
27212
+ WILL_DISCORD_CHANNELS, WILL_DISCORD_MENTION_ONLY, WILL_DISCORD_HOME_CHANNEL)
27213
+ whatsapp put a persistent mind on WhatsApp \u2014 QR-pairs as a linked device; no token.
27214
+ UNOFFICIAL protocol (ban risk; use a spare number \u2014 docs/channels/whatsapp.md).
27215
+ Optional WILL_WHATSAPP_CHATS, WILL_WHATSAPP_MENTION_ONLY, WILL_WHATSAPP_HOME_CHAT
26485
27216
 
26486
27217
  Shared env: WILL_NAME, WILL_IDENTITY, WILL_TIER, WILL_LLM, WILL_TICK_MS,
26487
27218
  WILL_SEED, WILL_PMA_PATH, WILL_MCP_SERVERS. The mind persists across runs via
26488
27219
  its PMA artifact.`;
26489
27220
  async function main() {
26490
27221
  const sub = process.argv[2];
26491
- if (sub !== "mcp" && sub !== "serve" && sub !== "discord") {
27222
+ if (sub !== "mcp" && sub !== "serve" && sub !== "discord" && sub !== "whatsapp") {
26492
27223
  console.error(sub ? `unknown subcommand: ${sub}
26493
27224
 
26494
27225
  ${USAGE}` : USAGE);
@@ -26520,6 +27251,21 @@ ${USAGE}` : USAGE);
26520
27251
  console.error(`[will] ${name} is present on Discord (tick ${tickMs}ms, anatomy ${anatomy}) \u2014 it speaks when it decides to.`);
26521
27252
  return;
26522
27253
  }
27254
+ if (sub === "whatsapp") {
27255
+ const csv = (v) => v?.split(",").map((s) => s.trim()).filter(Boolean);
27256
+ const stem = pmaPath.replace(/(\.pma)?\.json$/, "");
27257
+ const bridge = await connectWhatsApp(will, {
27258
+ chats: csv(process.env.WILL_WHATSAPP_CHATS),
27259
+ mentionOnly: /^(1|true|yes)$/i.test(process.env.WILL_WHATSAPP_MENTION_ONLY ?? ""),
27260
+ homeChatId: process.env.WILL_WHATSAPP_HOME_CHAT,
27261
+ authPath: stem + ".wa-auth",
27262
+ rosterPath: stem + ".whatsapp.json"
27263
+ });
27264
+ onCleanup(() => bridge.close());
27265
+ await bridge.start();
27266
+ console.error(`[will] ${name} is present on WhatsApp (tick ${tickMs}ms, anatomy ${anatomy}) \u2014 it speaks when it decides to.`);
27267
+ return;
27268
+ }
26523
27269
  const port = parseInt(process.env.WILL_PORT ?? "7777");
26524
27270
  const host = process.env.WILL_HOST ?? "127.0.0.1";
26525
27271
  const server = buildWillHttpServer(will, { pmaPath });