@mindot/will 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/channels/discord.d.ts +1 -1
  2. package/dist/channels/discord.js.map +1 -1
  3. package/dist/channels/whatsapp.d.ts +1 -1
  4. package/dist/channels/whatsapp.js.map +1 -1
  5. package/dist/cli.js +998 -59
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.js +998 -59
  9. package/dist/index.js.map +1 -1
  10. package/dist/mcp/effectors.d.ts +1 -1
  11. package/dist/mcp/effectors.js.map +1 -1
  12. package/dist/{will-BDq-TMQr.d.ts → will-DAW0l-lY.d.ts} +90 -10
  13. package/package.json +1 -1
  14. package/src/channels/discord.ts +11 -11
  15. package/src/channels/roster.ts +1 -1
  16. package/src/channels/types.ts +1 -1
  17. package/src/channels/whatsapp.ts +25 -25
  18. package/src/cli.ts +20 -20
  19. package/src/cognition/acp.ts +28 -0
  20. package/src/cognition/agency/consequence.ts +174 -0
  21. package/src/cognition/agency/engines/action.selector.ts +333 -48
  22. package/src/cognition/agency/engines/affordance.synthesizer.ts +35 -16
  23. package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
  24. package/src/cognition/agency/engines/motor.schema.executor.ts +90 -14
  25. package/src/cognition/agency/engines/reafference.engine.ts +152 -23
  26. package/src/cognition/agency/execution.primitives.ts +13 -13
  27. package/src/cognition/agency/proactive.communicator.ts +1 -1
  28. package/src/cognition/agency/reconcile.learning.ts +10 -1
  29. package/src/cognition/agency/revocation.ts +79 -0
  30. package/src/cognition/agency/schemas/repertoire.ts +109 -9
  31. package/src/cognition/agency/selection.scoring.ts +32 -10
  32. package/src/cognition/agency/types.ts +9 -0
  33. package/src/cognition/bus.ts +2 -2
  34. package/src/cognition/completion.inbox.ts +1 -1
  35. package/src/cognition/config.mirror.entities.ts +5 -5
  36. package/src/cognition/event.log.ts +2 -2
  37. package/src/cognition/event.schemas.ts +67 -67
  38. package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
  39. package/src/cognition/faculties/affective.blender.ts +36 -4
  40. package/src/cognition/faculties/attachment.evaluator.ts +6 -6
  41. package/src/cognition/faculties/attention.allocator.ts +63 -8
  42. package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
  43. package/src/cognition/faculties/bias.detector.ts +7 -7
  44. package/src/cognition/faculties/circadian.oscillator.ts +3 -3
  45. package/src/cognition/faculties/confidence.calibrator.ts +7 -7
  46. package/src/cognition/faculties/dream.simulator.ts +5 -5
  47. package/src/cognition/faculties/empathy.simulator.ts +7 -7
  48. package/src/cognition/faculties/energy.regulator.ts +4 -4
  49. package/src/cognition/faculties/episodic.consolidator.ts +6 -6
  50. package/src/cognition/faculties/executive.engine/commands.ts +6 -6
  51. package/src/cognition/faculties/executive.engine/context.ts +42 -42
  52. package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
  53. package/src/cognition/faculties/executive.engine/engine.ts +25 -25
  54. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
  55. package/src/cognition/faculties/executive.engine/facet.ts +18 -18
  56. package/src/cognition/faculties/executive.engine/gating.ts +2 -2
  57. package/src/cognition/faculties/executive.engine/messages.ts +3 -3
  58. package/src/cognition/faculties/executive.engine/parser.ts +11 -11
  59. package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
  60. package/src/cognition/faculties/exteroception.ts +99 -5
  61. package/src/cognition/faculties/forgetting.curve.ts +3 -3
  62. package/src/cognition/faculties/frustration.evaluator.ts +6 -6
  63. package/src/cognition/faculties/goal.manager.ts +15 -15
  64. package/src/cognition/faculties/inhibition.controller.ts +3 -3
  65. package/src/cognition/faculties/interoception.ts +3 -3
  66. package/src/cognition/faculties/introspection.engine.ts +3 -3
  67. package/src/cognition/faculties/known.entity.tracker.ts +15 -15
  68. package/src/cognition/faculties/loss.evaluator.ts +2 -2
  69. package/src/cognition/faculties/moral.evaluator.ts +4 -4
  70. package/src/cognition/faculties/novelty.detector.ts +4 -4
  71. package/src/cognition/faculties/persona.consolidator.ts +12 -12
  72. package/src/cognition/faculties/planning.engine/engine.ts +47 -47
  73. package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
  74. package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
  75. package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
  76. package/src/cognition/faculties/reputation.tracker.ts +6 -6
  77. package/src/cognition/faculties/reward.evaluator.ts +4 -4
  78. package/src/cognition/faculties/self.model.updater.ts +8 -8
  79. package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
  80. package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
  81. package/src/cognition/faculties/social.perception.ts +3 -3
  82. package/src/cognition/faculties/spaced.repetition.ts +2 -2
  83. package/src/cognition/faculties/stress.regulator.ts +27 -4
  84. package/src/cognition/faculties/task.switcher.ts +11 -6
  85. package/src/cognition/faculties/theory.of.mind.ts +6 -6
  86. package/src/cognition/faculties/threat.evaluator.ts +3 -3
  87. package/src/cognition/faculties/working.memory.ts +8 -8
  88. package/src/cognition/generative.model.ts +2 -2
  89. package/src/cognition/memory/vector.adapter.ts +5 -5
  90. package/src/cognition/memory/vector.content.ts +4 -4
  91. package/src/cognition/memory/vector.embedder.ts +9 -9
  92. package/src/cognition/persona.prior.ts +1 -1
  93. package/src/cognition/schema.registry.ts +2 -2
  94. package/src/cognition/senses/audition.engine/engine.ts +2 -2
  95. package/src/cognition/senses/base.sense.engine.ts +1 -1
  96. package/src/cognition/utilities/token.tracker.ts +5 -5
  97. package/src/core/abstracts.ts +1 -1
  98. package/src/core/completion.recorder.ts +2 -2
  99. package/src/core/serialization.ts +3 -3
  100. package/src/core/snapshot.manager.ts +1 -1
  101. package/src/core/state.manager.ts +1 -1
  102. package/src/core/utils.ts +1 -1
  103. package/src/host/boot.ts +30 -30
  104. package/src/host/utterances.ts +1 -1
  105. package/src/llm/index.ts +9 -9
  106. package/src/llm/summarizer.ts +4 -4
  107. package/src/llm/wire.contracts.ts +1 -1
  108. package/src/mcp/effectors.ts +10 -10
  109. package/src/mcp/server.ts +11 -11
  110. package/src/pma/eval.ts +1 -1
  111. package/src/pma/index.ts +21 -21
  112. package/src/runners/outreach.runner.ts +3 -3
  113. package/src/runners/social.runner.ts +1 -1
  114. package/src/runners/thin-shim.runner.ts +1 -1
  115. package/src/sdk/will.ts +5 -5
  116. package/src/serve/server.ts +12 -12
  117. package/src/stem/assembly.audit.ts +3 -3
  118. package/src/stem/guards/identity.coherence.ts +10 -10
  119. package/src/stem/guards/identity.guard.ts +22 -22
  120. package/src/stem/index.ts +26 -11
  121. package/src/stem/mind.ts +12 -12
  122. package/src/stem/policy/arbiter.ts +136 -0
  123. package/src/stem/policy/rule.table.ts +172 -0
  124. package/src/stem/policy/verdict.recorder.ts +0 -0
  125. package/src/stem/tracts/biography.writer.ts +3 -3
  126. package/src/stem/tracts/effector.controller.ts +383 -4
  127. package/src/stem/tracts/health.reporter.ts +1 -1
  128. package/src/stem/tracts/outbox.controller.ts +4 -0
  129. package/src/stem/tracts/outbox.writer.ts +4 -4
  130. package/src/stem/tracts/pma.controller.ts +1 -1
  131. package/src/stem/tracts/replay.controller.ts +10 -10
  132. package/src/stem/tracts/sensory.controller.ts +2 -2
  133. package/src/stem/tracts/session.logger.ts +4 -4
  134. package/src/stem/tracts/transport/socketio.transport.ts +7 -7
  135. package/src/stem/tracts/transport/stream.transport.ts +2 -2
  136. package/src/stem/tracts/transport.controller.ts +15 -15
package/dist/cli.js CHANGED
@@ -3625,11 +3625,19 @@ var PROC_THRESHOLD = 0.6;
3625
3625
  var IDLE_TICKS = 200;
3626
3626
  var DECAY_RATE = 0.02;
3627
3627
  var DROP_HABIT = 0.05;
3628
+ var AVAIL_DROP_CLASS = 0.5;
3629
+ var AVAIL_DROP_INSTANCE = 0.12;
3630
+ var AVAIL_FLOOR = 0.05;
3631
+ var AVAIL_RECOVERY = 0.02;
3632
+ var AVAIL_RECOVERED = 0.999;
3628
3633
  var SchemaRepertoire = class {
3629
3634
  _templates = /* @__PURE__ */ new Map();
3630
3635
  _skills = /* @__PURE__ */ new Map();
3631
3636
  /** Tracks which templates were learned at runtime (vs innate) so decay can forget them. */
3632
3637
  _learned = /* @__PURE__ */ new Set();
3638
+ /** Availability layer (P2): schema → { value 0..1, lastRefusedTick }. Empty until
3639
+ * a refusal lands — a never-refused Will writes nothing here (byte-identical). */
3640
+ _availability = /* @__PURE__ */ new Map();
3633
3641
  constructor(seed = INNATE_SCHEMAS) {
3634
3642
  for (const s of seed) this._templates.set(s.id, s);
3635
3643
  }
@@ -3663,6 +3671,31 @@ var SchemaRepertoire = class {
3663
3671
  getSkill(id) {
3664
3672
  return this._skills.get(id);
3665
3673
  }
3674
+ // ── availability (P2) ─────────────────────────────────────────
3675
+ availability() {
3676
+ return this._availability;
3677
+ }
3678
+ /**
3679
+ * How available a schema is right now, 0..1. Absent from the ledger ⇒ 1
3680
+ * (fully available — the common case). This is the ONLY value the
3681
+ * AffordanceSynthesizer reads; it never touches competence.
3682
+ */
3683
+ availabilityOf(schema) {
3684
+ return this._availability.get(schema)?.value ?? 1;
3685
+ }
3686
+ /**
3687
+ * Fold a policy refusal into the availability layer (NOT competence). A
3688
+ * `class` refusal cuts availability hard; an `instance` refusal dents it
3689
+ * lightly. Multiplicative so repeated refusals compound toward — but never
3690
+ * reach — zero, keeping re-probe alive.
3691
+ */
3692
+ recordRefusal(schema, finality, tick) {
3693
+ const prev = this._availability.get(schema)?.value ?? 1;
3694
+ const drop = finality === "class" ? AVAIL_DROP_CLASS : AVAIL_DROP_INSTANCE;
3695
+ const value = Math.max(AVAIL_FLOOR, prev * (1 - drop));
3696
+ this._availability.set(schema, { value, lastRefusedTick: tick });
3697
+ return value;
3698
+ }
3666
3699
  /**
3667
3700
  * Fold one outcome into the schema's learned skill. Returns the updated skill
3668
3701
  * and whether it just crossed the proceduralization threshold this update.
@@ -3689,12 +3722,14 @@ var SchemaRepertoire = class {
3689
3722
  return { skill, proceduralized: !wasProceduralized && habitStrength >= PROC_THRESHOLD };
3690
3723
  }
3691
3724
  /**
3692
- * Forgetting curve over the competence layer. Skills unused for IDLE_TICKS
3693
- * lose habit; learned composites that fall below DROP_HABIT are dropped
3694
- * entirely (template + skill). Returns the schema ids that were forgotten.
3725
+ * Forgetting curve over the competence layer, plus availability recovery.
3726
+ * Skills unused for IDLE_TICKS lose habit; learned composites below DROP_HABIT
3727
+ * are dropped entirely (template + skill). Availability entries climb back
3728
+ * toward 1 and are dropped once fully recovered. Returns the ids that were
3729
+ * removed from each layer so their mirrored state entities can be deleted.
3695
3730
  */
3696
3731
  decay(tick) {
3697
- const dropped = [];
3732
+ const skills = [];
3698
3733
  for (const [id, skill] of this._skills) {
3699
3734
  if (tick - skill.lastEnactedTick <= IDLE_TICKS) continue;
3700
3735
  const habitStrength = clamp01(skill.habitStrength - DECAY_RATE);
@@ -3702,12 +3737,20 @@ var SchemaRepertoire = class {
3702
3737
  this._skills.delete(id);
3703
3738
  this._templates.delete(id);
3704
3739
  this._learned.delete(id);
3705
- dropped.push(id);
3740
+ skills.push(id);
3706
3741
  continue;
3707
3742
  }
3708
3743
  this._skills.set(id, { ...skill, habitStrength });
3709
3744
  }
3710
- return dropped;
3745
+ const availability = [];
3746
+ for (const [id, avail] of this._availability) {
3747
+ const value = avail.value + AVAIL_RECOVERY * (1 - avail.value);
3748
+ if (value >= AVAIL_RECOVERED) {
3749
+ this._availability.delete(id);
3750
+ availability.push(id);
3751
+ } else this._availability.set(id, { ...avail, value });
3752
+ }
3753
+ return { skills, availability };
3711
3754
  }
3712
3755
  // ── PMA portability (Phase 6 reads these) ─────────────────────
3713
3756
  /** Learned composite templates + all skills above a confidence floor. */
@@ -3756,6 +3799,28 @@ var SchemaRepertoire = class {
3756
3799
  this._learned.add(s.id);
3757
3800
  }
3758
3801
  }
3802
+ /** Availability ledger encoded as `agency.availability` state entities (P2).
3803
+ * Empty until a refusal lands, so the quiet path writes nothing. */
3804
+ availabilityEntities() {
3805
+ const out = [];
3806
+ for (const [schema, a] of this._availability)
3807
+ out.push(availabilityEntity(schema, a.value, a.lastRefusedTick));
3808
+ return out;
3809
+ }
3810
+ /** Rehydrate the availability ledger from state after a restore. Idempotent;
3811
+ * keeps whichever value is more restrictive so a concurrent refusal isn't lost. */
3812
+ restoreAvailability(entities) {
3813
+ for (const e of entities.values()) {
3814
+ if (e.type !== AVAILABILITY_ENTITY_TYPE) continue;
3815
+ const m = e.metadata ?? {};
3816
+ const schema = typeof m["schema"] === "string" ? m["schema"] : "";
3817
+ if (!schema) continue;
3818
+ const value = typeof m["value"] === "number" ? m["value"] : 1;
3819
+ const tick = typeof m["lastRefusedTick"] === "number" ? m["lastRefusedTick"] : 0;
3820
+ const prev = this._availability.get(schema);
3821
+ if (!prev || value < prev.value) this._availability.set(schema, { value, lastRefusedTick: tick });
3822
+ }
3823
+ }
3759
3824
  };
3760
3825
  function freshSkill(schema, value, tick) {
3761
3826
  return {
@@ -3772,6 +3837,17 @@ function freshSkill(schema, value, tick) {
3772
3837
  function clamp01(n) {
3773
3838
  return n < 0 ? 0 : n > 1 ? 1 : n;
3774
3839
  }
3840
+ var AVAILABILITY_ENTITY_TYPE = "agency.availability";
3841
+ function availabilityEntityId(schema) {
3842
+ return `agency-availability-${schema}`;
3843
+ }
3844
+ function availabilityEntity(schema, value, lastRefusedTick) {
3845
+ return {
3846
+ id: availabilityEntityId(schema),
3847
+ type: AVAILABILITY_ENTITY_TYPE,
3848
+ metadata: { schema, value, lastRefusedTick }
3849
+ };
3850
+ }
3775
3851
  var SCHEMA_ENTITY_TYPE = "agency.schema";
3776
3852
  function schemaEntityId(schemaId) {
3777
3853
  return `agency-schema-${schemaId}`;
@@ -4655,6 +4731,9 @@ var SleepPressureRegulator = class {
4655
4731
  }
4656
4732
  };
4657
4733
 
4734
+ // src/cognition/acp.ts
4735
+ var ACP_SELF_PRECISION = 0.35;
4736
+
4658
4737
  // src/cognition/persona.prior.ts
4659
4738
  var PERSONA_PRIOR_ID = "persona-prior";
4660
4739
  var PERSONA_PRIOR_TYPE = "persona.prior";
@@ -4763,6 +4842,8 @@ function consolidatePrior(current, adjustments, tick, opts = {}) {
4763
4842
  }
4764
4843
 
4765
4844
  // src/cognition/faculties/attention.allocator.ts
4845
+ var ACP_USAGE_UPLIFT = 0.15;
4846
+ var ACP_CONFIDENCE = 0.4;
4766
4847
  var EFFORT_BASELINE2 = 0.7;
4767
4848
  var EFFORT_MIN = 0.4;
4768
4849
  var EFFORT_MAX = 1;
@@ -4784,6 +4865,8 @@ var AttentionAllocator = class {
4784
4865
  _effort = EFFORT_BASELINE2;
4785
4866
  /** A one-shot focus/rest request from the executive, applied next react(). */
4786
4867
  _effortRequest = null;
4868
+ /** ACP-P2: a self-caused precision attenuation is armed; react() restores after one observe. */
4869
+ _acpOneShot = false;
4787
4870
  _bus = null;
4788
4871
  _model = new GenerativeModel();
4789
4872
  constructor(config = {}) {
@@ -4804,8 +4887,12 @@ var AttentionAllocator = class {
4804
4887
  "executive.prediction.formed",
4805
4888
  "attention.regulate",
4806
4889
  // voluntary focus/rest from the executive (Option C)
4807
- "senses.*"
4890
+ "senses.*",
4808
4891
  // all sense percepts — salience feeds attention allocation
4892
+ // ACP-P2: our own enactions — anticipate the attention swing they cause
4893
+ "agency.enacted",
4894
+ "agency.communicate",
4895
+ "agency.invocation"
4809
4896
  ];
4810
4897
  }
4811
4898
  publishes() {
@@ -4832,6 +4919,18 @@ var AttentionAllocator = class {
4832
4919
  this._model.setPrecision("attention.usage", 1 + p.confidence * 0.5);
4833
4920
  break;
4834
4921
  }
4922
+ case "agency.enacted":
4923
+ case "agency.communicate":
4924
+ case "agency.invocation": {
4925
+ const usage = this._model.predict("attention.usage");
4926
+ const free = this._model.predict("attention.free_fraction");
4927
+ this._model.anticipate("attention.usage", Math.min(1, usage + ACP_USAGE_UPLIFT), ACP_CONFIDENCE);
4928
+ this._model.anticipate("attention.free_fraction", Math.max(0, free - ACP_USAGE_UPLIFT), ACP_CONFIDENCE);
4929
+ this._model.setPrecision("attention.usage", ACP_SELF_PRECISION);
4930
+ this._model.setPrecision("attention.free_fraction", ACP_SELF_PRECISION);
4931
+ this._acpOneShot = true;
4932
+ break;
4933
+ }
4835
4934
  default:
4836
4935
  if (e.type.startsWith("senses.")) {
4837
4936
  const percept = e.payload;
@@ -4915,6 +5014,11 @@ var AttentionAllocator = class {
4915
5014
  if (_bus) {
4916
5015
  const usageErr = this._model.observe("attention.usage", attentionUsage);
4917
5016
  const freeErr = this._model.observe("attention.free_fraction", freeFraction);
5017
+ if (this._acpOneShot) {
5018
+ this._model.setPrecision("attention.usage", 1);
5019
+ this._model.setPrecision("attention.free_fraction", 1);
5020
+ this._acpOneShot = false;
5021
+ }
4918
5022
  if (!usageErr.gated || !freeErr.gated)
4919
5023
  _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 } });
4920
5024
  }
@@ -5034,7 +5138,18 @@ var StressRegulator = class {
5034
5138
  }
5035
5139
  // ── Engine interface ─────────────────────────────────────
5036
5140
  subscribes() {
5037
- return ["energy.state.changed", "sleep.state.changed", "novelty.state.changed", "goal.state.changed", "metacognition.state.changed", "executive.prediction.formed"];
5141
+ return [
5142
+ "energy.state.changed",
5143
+ "sleep.state.changed",
5144
+ "novelty.state.changed",
5145
+ "goal.state.changed",
5146
+ "metacognition.state.changed",
5147
+ "executive.prediction.formed",
5148
+ // ACP-P2: our own enactions — the stress swing they cause is expected
5149
+ "agency.enacted",
5150
+ "agency.communicate",
5151
+ "agency.invocation"
5152
+ ];
5038
5153
  }
5039
5154
  publishes() {
5040
5155
  return [];
@@ -5063,8 +5178,17 @@ var StressRegulator = class {
5063
5178
  this._model.setPrecision("stress.load", 1 + p.confidence * 0.5);
5064
5179
  break;
5065
5180
  }
5181
+ case "agency.enacted":
5182
+ case "agency.communicate":
5183
+ case "agency.invocation": {
5184
+ this._model.setPrecision("stress.load", ACP_SELF_PRECISION);
5185
+ this._acpOneShot = true;
5186
+ break;
5187
+ }
5066
5188
  }
5067
5189
  }
5190
+ /** ACP-P2: self-caused stress attenuation armed; react() restores after one observe. */
5191
+ _acpOneShot = false;
5068
5192
  snapshot() {
5069
5193
  return {
5070
5194
  energyLevel: this._energyLevel,
@@ -5157,6 +5281,10 @@ var StressRegulator = class {
5157
5281
  if (_bus) {
5158
5282
  const zoneCode = zone === "low" ? 0 : zone === "optimal" ? 1 : zone === "distress" ? 2 : 3;
5159
5283
  const predErr = this._model.observe("stress.load", newLoad);
5284
+ if (this._acpOneShot) {
5285
+ this._model.setPrecision("stress.load", 1);
5286
+ this._acpOneShot = false;
5287
+ }
5160
5288
  if (!predErr.gated || zone !== previousZone) {
5161
5289
  _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 } });
5162
5290
  }
@@ -5379,6 +5507,116 @@ var CircadianOscillator = class {
5379
5507
  }
5380
5508
  };
5381
5509
 
5510
+ // src/cognition/agency/consequence.ts
5511
+ var CONSEQUENCE_TYPE = "agency.consequence";
5512
+ var ATTENUATION = 0.25;
5513
+ var MIN_TEXT_MATCH_LEN = 12;
5514
+ var CONSEQUENCE_TTL_TICKS = 30;
5515
+ function fnv1a(text) {
5516
+ let h = 2166136261;
5517
+ for (let i = 0; i < text.length; i++) {
5518
+ h ^= text.charCodeAt(i);
5519
+ h = Math.imul(h, 16777619) >>> 0;
5520
+ }
5521
+ return h >>> 0;
5522
+ }
5523
+ function paramsKey(value) {
5524
+ if (value === null || typeof value !== "object")
5525
+ return typeof value === "string" ? JSON.stringify(value) : String(value);
5526
+ if (Array.isArray(value))
5527
+ return `[${value.map(paramsKey).join(",")}]`;
5528
+ const obj = value;
5529
+ const keys = Object.keys(obj).sort();
5530
+ return `{${keys.map((k) => `${k}:${paramsKey(obj[k])}`).join(",")}}`;
5531
+ }
5532
+ function consequenceEntity(d) {
5533
+ return {
5534
+ id: `agency-consequence-${d.intentId}`,
5535
+ type: CONSEQUENCE_TYPE,
5536
+ metadata: { ...d }
5537
+ };
5538
+ }
5539
+ function readConsequence(m) {
5540
+ const meta = m ?? {};
5541
+ const intentId = typeof meta["intentId"] === "string" ? meta["intentId"] : void 0;
5542
+ const schema = typeof meta["schema"] === "string" ? meta["schema"] : void 0;
5543
+ const mode = meta["mode"] === "communicate" || meta["mode"] === "external" ? meta["mode"] : void 0;
5544
+ if (!intentId || !schema || !mode) return null;
5545
+ return {
5546
+ intentId,
5547
+ schema,
5548
+ mode,
5549
+ effector: typeof meta["effector"] === "string" ? meta["effector"] : void 0,
5550
+ targetEntityId: typeof meta["targetEntityId"] === "string" ? meta["targetEntityId"] : void 0,
5551
+ textHash: typeof meta["textHash"] === "number" ? meta["textHash"] : void 0,
5552
+ text: typeof meta["text"] === "string" ? meta["text"] : void 0,
5553
+ paramsHash: typeof meta["paramsHash"] === "number" ? meta["paramsHash"] : void 0,
5554
+ expiresAt: typeof meta["expiresAt"] === "number" ? meta["expiresAt"] : 0,
5555
+ tick: typeof meta["tick"] === "number" ? meta["tick"] : 0
5556
+ };
5557
+ }
5558
+ function liveConsequences(entities, tick) {
5559
+ const out = [];
5560
+ for (const [, e] of entities) {
5561
+ if (e.type !== CONSEQUENCE_TYPE) continue;
5562
+ const d = readConsequence(e.metadata);
5563
+ if (d && tick < d.expiresAt) out.push(d);
5564
+ }
5565
+ return out.sort((a, b) => a.intentId < b.intentId ? -1 : a.intentId > b.intentId ? 1 : 0);
5566
+ }
5567
+ function matchConsequenceText(descriptors, candidate) {
5568
+ if (candidate.length === 0) return null;
5569
+ const candidateHash = fnv1a(candidate);
5570
+ for (const d of descriptors) {
5571
+ if (d.textHash !== void 0 && d.textHash === candidateHash) return d;
5572
+ if (d.text !== void 0 && d.text.length >= MIN_TEXT_MATCH_LEN && candidate.includes(d.text)) return d;
5573
+ }
5574
+ return null;
5575
+ }
5576
+ var CORRESPONDENCE_ATTENUATION = 0.5;
5577
+ function matchConsequenceEntity(descriptors, entityId, changeType) {
5578
+ if (changeType !== "modified" || entityId.length === 0) return null;
5579
+ for (const d of descriptors)
5580
+ if (d.mode === "external" && d.targetEntityId === entityId) return d;
5581
+ return null;
5582
+ }
5583
+
5584
+ // src/cognition/agency/revocation.ts
5585
+ var REVOCATION_TYPE = "agency.revocation";
5586
+ var RUPTURE_REVOKE_GATE = 0.7;
5587
+ var REVOCATION_TTL_TICKS = 5;
5588
+ function revocationId(intentId) {
5589
+ return `agency-revocation-${intentId}`;
5590
+ }
5591
+ function revocationEntity(intentId, schema, rupture, tick) {
5592
+ return {
5593
+ id: revocationId(intentId),
5594
+ type: REVOCATION_TYPE,
5595
+ metadata: { intentId, schema, rupture, tick, expiresAt: tick + REVOCATION_TTL_TICKS }
5596
+ };
5597
+ }
5598
+ function revokedIntentIds(entities, tick) {
5599
+ const out = /* @__PURE__ */ new Set();
5600
+ for (const [, e] of entities) {
5601
+ if (e.type !== REVOCATION_TYPE) continue;
5602
+ const m = e.metadata ?? {};
5603
+ const intentId = typeof m["intentId"] === "string" ? m["intentId"] : void 0;
5604
+ const expiresAt = typeof m["expiresAt"] === "number" ? m["expiresAt"] : 0;
5605
+ if (intentId && tick < expiresAt) out.add(intentId);
5606
+ }
5607
+ return out;
5608
+ }
5609
+ function staleRevocationIds(entities, tick) {
5610
+ const stale = [];
5611
+ for (const [id, e] of entities) {
5612
+ if (e.type !== REVOCATION_TYPE) continue;
5613
+ const m = e.metadata ?? {};
5614
+ const expiresAt = typeof m["expiresAt"] === "number" ? m["expiresAt"] : 0;
5615
+ if (tick >= expiresAt) stale.push(id);
5616
+ }
5617
+ return stale;
5618
+ }
5619
+
5382
5620
  // src/cognition/faculties/exteroception.ts
5383
5621
  var internalTypes = /* @__PURE__ */ new Set([
5384
5622
  "percept",
@@ -5404,7 +5642,12 @@ var internalTypes = /* @__PURE__ */ new Set([
5404
5642
  "episodic_memory",
5405
5643
  // Internal state entities written by our own engines — not external events
5406
5644
  "affect.blends",
5407
- "executive.summary"
5645
+ "executive.summary",
5646
+ // The agency's own forward-model records (EXAFFERENCE P1/P2): perceiving our
5647
+ // expected-consequence descriptors would be a self-noise loop.
5648
+ CONSEQUENCE_TYPE,
5649
+ // The agency's own revocation tombstones (EXAFFERENCE P4) — internal bookkeeping.
5650
+ REVOCATION_TYPE
5408
5651
  ]);
5409
5652
  var Exteroception = class {
5410
5653
  name = "exteroception";
@@ -5454,17 +5697,26 @@ var Exteroception = class {
5454
5697
  const events = [], commands = { set: [], delete: [], metrics: [] };
5455
5698
  const rawPercepts = this._scanWorld(state);
5456
5699
  const capped = rawPercepts.slice(0, this._maxPerceptsPerTick);
5700
+ const consequences = liveConsequences(state.entities, tick);
5457
5701
  for (let i = 0; i < capped.length; i++) {
5458
5702
  const rp = capped[i];
5703
+ const textHit = consequences.length > 0 && rp.matchText ? matchConsequenceText(consequences, rp.matchText) : null;
5704
+ const entityHit = !textHit && consequences.length > 0 ? matchConsequenceEntity(consequences, rp.entityId, rp.changeType) : null;
5705
+ const hit = textHit ?? entityHit;
5706
+ const salience = textHit ? rp.salience * ATTENUATION : entityHit ? rp.salience * CORRESPONDENCE_ATTENUATION : rp.salience;
5459
5707
  const perceptEntity = {
5460
5708
  id: `percept-${tick}-${i}`,
5461
5709
  type: "percept",
5462
5710
  metadata: {
5463
5711
  entityId: rp.entityId,
5464
5712
  changeType: rp.changeType,
5465
- salience: rp.salience,
5713
+ salience,
5466
5714
  category: rp.category,
5467
5715
  summary: rp.summary,
5716
+ provenance: hit ? "reafferent" : "exafferent",
5717
+ ...hit ? { sourceIntentId: hit.intentId } : {},
5718
+ // affect→percept seam (registry #5): what this percept FEELS like
5719
+ ...rp.valence !== void 0 ? { valence: rp.valence, valenceSource: rp.valenceSource } : {},
5468
5720
  tick
5469
5721
  }
5470
5722
  };
@@ -5475,7 +5727,7 @@ var Exteroception = class {
5475
5727
  source: this.name,
5476
5728
  payload: {
5477
5729
  entityId: rp.entityId,
5478
- salience: rp.salience,
5730
+ salience,
5479
5731
  category: rp.category,
5480
5732
  summary: rp.summary
5481
5733
  }
@@ -5523,7 +5775,9 @@ var Exteroception = class {
5523
5775
  changeType: "appeared",
5524
5776
  salience: this._computeSalience(entity, "appeared", state.time),
5525
5777
  category: entity.type,
5526
- summary: this._summarizeEntity(entity, "appeared")
5778
+ summary: this._summarizeEntity(entity, "appeared"),
5779
+ matchText: this._matchText(entity),
5780
+ ...this._valenceOf(id, state)
5527
5781
  });
5528
5782
  } else if (entity.updatedAt > previousVersion) {
5529
5783
  percepts.push({
@@ -5531,7 +5785,9 @@ var Exteroception = class {
5531
5785
  changeType: "modified",
5532
5786
  salience: this._computeSalience(entity, "modified", state.time),
5533
5787
  category: entity.type,
5534
- summary: this._summarizeEntity(entity, "modified")
5788
+ summary: this._summarizeEntity(entity, "modified"),
5789
+ matchText: this._matchText(entity),
5790
+ ...this._valenceOf(id, state)
5535
5791
  });
5536
5792
  }
5537
5793
  this._previousEntityVersions.set(id, entity.updatedAt);
@@ -5557,6 +5813,46 @@ var Exteroception = class {
5557
5813
  * Generate a meaningful summary for an entity.
5558
5814
  * Instead of "New percept: percept-54-0", produce something useful.
5559
5815
  */
5816
+ /**
5817
+ * The text the corollary-discharge matcher inspects for this entity — its
5818
+ * content (a message body) over its description over its summary. Where our
5819
+ * own delivered words would surface if the world echoes them back.
5820
+ */
5821
+ /**
5822
+ * The affect→percept valence seam (registry #5). A percept carries how the
5823
+ * mind *feels* about what it is a percept OF, resolved most-specific-first:
5824
+ *
5825
+ * 1. the KnownEntityTracker's dossier for this entity (`ke-<id>.valence`) —
5826
+ * a real per-entity felt valence, the honest signal;
5827
+ * 2. otherwise the ambient `affect.valence` — the felt tone at perception
5828
+ * time. Weaker evidence, so it is tagged `'ambient'` and consumers
5829
+ * weight it down (mood is context, not appraisal of this thing).
5830
+ *
5831
+ * Absent both, no valence is stamped and every consumer keeps its
5832
+ * pre-seam behaviour.
5833
+ */
5834
+ /** Spread-friendly form of `_valenceFor` for percept construction. */
5835
+ _valenceOf(entityId, state) {
5836
+ const v = this._valenceFor(entityId, state);
5837
+ return v ? { valence: v.valence, valenceSource: v.source } : {};
5838
+ }
5839
+ _valenceFor(entityId, state) {
5840
+ const dossier = state.entities.get(`ke-${entityId}`);
5841
+ const felt = dossier?.metadata?.["valence"];
5842
+ if (typeof felt === "number" && Number.isFinite(felt))
5843
+ return { valence: felt, source: "entity" };
5844
+ const ambient = state.metrics.get("affect.valence");
5845
+ if (typeof ambient === "number" && Number.isFinite(ambient))
5846
+ return { valence: ambient, source: "ambient" };
5847
+ return void 0;
5848
+ }
5849
+ _matchText(entity) {
5850
+ const m = entity.metadata;
5851
+ const content = typeof m?.["content"] === "string" ? m["content"] : void 0;
5852
+ const description = typeof m?.["description"] === "string" ? m["description"] : void 0;
5853
+ const summary = typeof m?.["summary"] === "string" ? m["summary"] : void 0;
5854
+ return content ?? description ?? summary;
5855
+ }
5560
5856
  _summarizeEntity(entity, changeType) {
5561
5857
  const name = entity.metadata?.name;
5562
5858
  const description = entity.metadata?.description;
@@ -7541,6 +7837,8 @@ var MoralEvaluator = class {
7541
7837
  };
7542
7838
 
7543
7839
  // src/cognition/faculties/affective.blender.ts
7840
+ var ACP_AROUSAL_UPLIFT = 0.1;
7841
+ var ACP_CONFIDENCE2 = 0.4;
7544
7842
  var DOMINANT_EMOTION_CODES = {
7545
7843
  "neutral": 0,
7546
7844
  "fear": 1,
@@ -7641,6 +7939,8 @@ var AffectiveBlender = class {
7641
7939
  _previousPAD = { valence: 0, arousal: 0.3, dominance: 0.5 };
7642
7940
  _emotionHistory = /* @__PURE__ */ new Map();
7643
7941
  _moodBaseline = 0.5;
7942
+ /** ACP-P2: self-caused arousal attenuation armed; react() restores after one observe. */
7943
+ _acpOneShot = false;
7644
7944
  _energyLevel = 100;
7645
7945
  _comfort = 0.5;
7646
7946
  _sleepPressure = 0;
@@ -7674,8 +7974,12 @@ var AffectiveBlender = class {
7674
7974
  "sleep.state.changed",
7675
7975
  "stress.state.changed",
7676
7976
  "executive.prediction.formed",
7677
- "interaction.occurred"
7977
+ "interaction.occurred",
7678
7978
  // 2.1: social stimuli drive immediate arousal/valence shifts
7979
+ // ACP-P2: our own enactions — the arousal they cause is expected, not surprising
7980
+ "agency.enacted",
7981
+ "agency.communicate",
7982
+ "agency.invocation"
7679
7983
  ];
7680
7984
  }
7681
7985
  publishes() {
@@ -7684,6 +7988,15 @@ var AffectiveBlender = class {
7684
7988
  onCognitiveEvent(e) {
7685
7989
  this._model.observe(e.type, e.salience);
7686
7990
  switch (e.type) {
7991
+ case "agency.enacted":
7992
+ case "agency.communicate":
7993
+ case "agency.invocation": {
7994
+ const arousal = this._model.predict("affect.arousal");
7995
+ this._model.anticipate("affect.arousal", Math.min(1, arousal + ACP_AROUSAL_UPLIFT), ACP_CONFIDENCE2);
7996
+ this._model.setPrecision("affect.arousal", ACP_SELF_PRECISION);
7997
+ this._acpOneShot = true;
7998
+ break;
7999
+ }
7687
8000
  case "circadian.state.changed":
7688
8001
  this._moodBaseline = e.payload["moodBaseline"] ?? this._moodBaseline;
7689
8002
  break;
@@ -7787,8 +8100,14 @@ var AffectiveBlender = class {
7787
8100
  const _bus = this._bus;
7788
8101
  if (_bus)
7789
8102
  _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 } });
7790
- if (_bus)
7791
- _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 } });
8103
+ if (_bus) {
8104
+ const arousalErr = this._model.observe("affect.arousal", modulatedPAD.arousal);
8105
+ if (this._acpOneShot) {
8106
+ this._model.setPrecision("affect.arousal", 1);
8107
+ this._acpOneShot = false;
8108
+ }
8109
+ _bus.publish({ type: "affect.state.changed", version: 1, sourceEngine: this.name, salience: arousalErr.salience, payload: { valence: modulatedPAD.valence, arousal: modulatedPAD.arousal, dominance: modulatedPAD.dominance } });
8110
+ }
7792
8111
  return { events: events.length > 0 ? events : void 0, commands };
7793
8112
  }
7794
8113
  // ── PAD computation ──────────────────────────────────────
@@ -16206,7 +16525,8 @@ var TaskSwitcher = class {
16206
16525
  const currentGoal = activeGoals.find((g) => g.id === this._currentFocus.goalId);
16207
16526
  const currentPriority = currentGoal?.priority ?? 0;
16208
16527
  const switchAdvantage = topGoal.priority - currentPriority;
16209
- const switchCost = this._baseSwitchCost * (1 + this._currentFocus.focusTicks * 0.01);
16528
+ const stability = state.metrics.get("situation.stability") ?? 1;
16529
+ const switchCost = this._baseSwitchCost * (1 + this._currentFocus.focusTicks * 0.01 * stability);
16210
16530
  const netBenefit = switchAdvantage - switchCost;
16211
16531
  if (netBenefit > this._switchThreshold && this._currentFocus.focusTicks >= this._minFocusTicks) {
16212
16532
  this._currentFocus = {
@@ -19807,7 +20127,9 @@ function risk(a, bias) {
19807
20127
  return clamp016(Math.max(0, -a.expectedValence) * 0.5 + bias.threat * 0.5);
19808
20128
  }
19809
20129
  function scoreAffordance(a, bias, w = DEFAULT_WEIGHTS) {
19810
- return w.goal * goalRelevance(a, bias) + w.reward * a.expectedReward + w.novelty * novelty(a) + w.drive * driveUrgency(a, bias) + w.habit * a.habitStrength + w.plan * (a.planBias ?? 0) - w.cost * a.cost - w.inhib * bias.inhibition - w.risk * risk(a, bias);
20130
+ const raw = w.goal * goalRelevance(a, bias) + w.reward * a.expectedReward + w.novelty * novelty(a) + w.drive * driveUrgency(a, bias) + w.habit * a.habitStrength + w.plan * (a.planBias ?? 0) - w.cost * a.cost - w.inhib * bias.inhibition - w.risk * risk(a, bias);
20131
+ const availability = a.availability ?? 1;
20132
+ return raw > 0 ? raw * availability : raw;
19811
20133
  }
19812
20134
  function stakes(winner, bias) {
19813
20135
  return clamp016(Math.max(
@@ -19886,6 +20208,7 @@ var AffordanceSynthesizer = class {
19886
20208
  // ── react ─────────────────────────────────────────────────────
19887
20209
  async react(_delta, tick, state, _context) {
19888
20210
  this._repertoire?.restoreComposites(state.entities);
20211
+ this._repertoire?.restoreAvailability(state.entities);
19889
20212
  const schemas = this._repertoire?.schemas() ?? this._schemas;
19890
20213
  const skills = this._skills?.() ?? this._repertoire?.skills() ?? null;
19891
20214
  const valence = metric(state, "affect.valence", 0);
@@ -19896,9 +20219,7 @@ var AffordanceSynthesizer = class {
19896
20219
  if (e.type === "affordance") del.push(id);
19897
20220
  const floor = schemas.filter((s) => s.binds === "none");
19898
20221
  for (const schema of floor)
19899
- set.push(this._toEntity(
19900
- this._build(schema, tick, state, valence, energyLow, skills, {})
19901
- ));
20222
+ set.push(this._toEntity(this._build(schema, tick, state, valence, energyLow, skills, {})));
19902
20223
  const candidates = [];
19903
20224
  const goalTargets = collectGoalTargets(state);
19904
20225
  const perceptSchema = schemas.find((s) => s.binds === "percept");
@@ -20013,6 +20334,7 @@ var AffordanceSynthesizer = class {
20013
20334
  /** Compose an Affordance from a schema + the evoking context, folding in learned priors. */
20014
20335
  _build(schema, tick, state, valence, energyLow, skills, ctx) {
20015
20336
  const skill = skills?.get(schema.id);
20337
+ const availability = this._repertoire?.availabilityOf(schema.id) ?? 1;
20016
20338
  const expectedReward = skill?.valueEstimate ?? clamp017(((schema.baseValence ?? 0) + 1) / 2);
20017
20339
  const expectedValence = schema.baseValence ?? valence;
20018
20340
  const habitStrength = skill?.habitStrength ?? 0;
@@ -20034,6 +20356,7 @@ var AffordanceSynthesizer = class {
20034
20356
  available: this._available(schema.preconditions, (k) => metric(state, k, 0)),
20035
20357
  tags: schema.tags ?? [],
20036
20358
  ...schema.description ? { description: schema.description } : {},
20359
+ ...availability < 1 ? { availability } : {},
20037
20360
  planBias: ctx.planBias,
20038
20361
  planId: ctx.planId,
20039
20362
  stepId: ctx.stepId,
@@ -20057,6 +20380,7 @@ var AffordanceSynthesizer = class {
20057
20380
  available: a.available,
20058
20381
  tags: a.tags,
20059
20382
  description: a.description,
20383
+ ...a.availability !== void 0 ? { availability: a.availability } : {},
20060
20384
  planBias: a.planBias,
20061
20385
  planId: a.planId,
20062
20386
  stepId: a.stepId,
@@ -20112,11 +20436,24 @@ var BASE_SWITCH_COST = 0.15;
20112
20436
  var FOCUS_GAIN = 0.01;
20113
20437
  var AWAIT_STALE_TICKS = 15;
20114
20438
  var STALE_DECAY = 0.5;
20439
+ var RUPTURE_SALIENCE_GATE = 0.4;
20440
+ var RUPTURE_WINDOW_TICKS = 2;
20441
+ var STABILITY_RECOVERY = 0.05;
20442
+ var STABILITY_EPSILON = 1e-4;
20443
+ var REVOKE_HINT_WINDOW = 8;
20115
20444
  var ActionSelector = class {
20116
20445
  name = "action-selector";
20117
20446
  _bus = null;
20118
20447
  _lastEntropy = 0;
20119
20448
  _lastDeliberate = false;
20449
+ // EXAFFERENCE P4 follow-up (Channel B): the schema we most recently revoked
20450
+ // under rupture, so the *next* deliberation formed in its wake can own the
20451
+ // interruption in-character ("something changed — I dropped what I was
20452
+ // weighing"). Telemetry-grade instance state, mirroring _lastEntropy.
20453
+ _lastRevoked = null;
20454
+ // ACP §2b: sense-channel percepts buffered off the bus for the next react's
20455
+ // rupture computation (they never become entities). Cross-tick ⇒ FN9.
20456
+ _senseBuffer = [];
20120
20457
  attachBus(bus) {
20121
20458
  this._bus = bus;
20122
20459
  }
@@ -20124,16 +20461,60 @@ var ActionSelector = class {
20124
20461
  return [
20125
20462
  { type: "agency.selection.made", version: 1, validate: () => null },
20126
20463
  { type: "agency.selection.ambiguous", version: 1, validate: () => null },
20127
- { type: "agency.action.preempted", version: 1, validate: () => null }
20464
+ { type: "agency.action.preempted", version: 1, validate: () => null },
20465
+ { type: "agency.situation.rupture", version: 1, validate: () => null },
20466
+ { type: "agency.commitment.revoked", version: 1, validate: () => null }
20128
20467
  ];
20129
20468
  }
20130
20469
  subscribes() {
20131
- return [];
20470
+ return [
20471
+ "senses.*",
20472
+ // Registry #6 (ACP-P3): the model-error term — each carries its
20473
+ // engine's prediction-error salience, ALREADY precision-attenuated for
20474
+ // self-caused swings by the ACP-P2 consumers, so the echo guard
20475
+ // composes by construction: our own action's interoceptive wake
20476
+ // arrives below the rupture gate.
20477
+ "attention.state.changed",
20478
+ "affect.state.changed",
20479
+ "stress.state.changed"
20480
+ ];
20132
20481
  }
20133
- onCognitiveEvent() {
20482
+ /**
20483
+ * Mostly pull-model — but two afferent classes never become entities and
20484
+ * would leave rupture blind: sense-channel percepts
20485
+ * (ACTION_CONDITIONED_PREDICTION §2b) and the model-error state-change
20486
+ * events (registry #6). Buffer both (cross-tick: bus flush at T, consumed
20487
+ * by react at T+1 — FN9-snapshotted); the echo guard is applied at read
20488
+ * time for texts, and at the SOURCE for model errors (ACP-P2 precision).
20489
+ */
20490
+ onCognitiveEvent(e) {
20491
+ const isSense = e.type.startsWith("senses.") && e.type.endsWith(".percept");
20492
+ const isModelError = e.type === "attention.state.changed" || e.type === "affect.state.changed" || e.type === "stress.state.changed";
20493
+ if (!isSense && !isModelError) return;
20494
+ const p = e.payload;
20495
+ this._senseBuffer.push({
20496
+ salience: e.salience,
20497
+ ...isSense && typeof p?.content === "string" ? { text: p.content } : {}
20498
+ });
20134
20499
  }
20135
20500
  snapshot() {
20136
- return { lastEntropy: this._lastEntropy, lastDeliberate: this._lastDeliberate };
20501
+ return {
20502
+ lastEntropy: this._lastEntropy,
20503
+ lastDeliberate: this._lastDeliberate,
20504
+ lastRevoked: this._lastRevoked,
20505
+ senseBuffer: this._senseBuffer
20506
+ };
20507
+ }
20508
+ /** FN9: `_lastRevoked` has behavioral effect (the Channel-B `revokedBy` stamp),
20509
+ * so a restored mind must carry it — a rupture-driven letting-go survives a
20510
+ * snapshot boundary instead of silently losing its narrative thread. */
20511
+ restore(s) {
20512
+ if (typeof s["lastEntropy"] === "number") this._lastEntropy = s["lastEntropy"];
20513
+ if (typeof s["lastDeliberate"] === "boolean") this._lastDeliberate = s["lastDeliberate"];
20514
+ const lr = s["lastRevoked"];
20515
+ this._lastRevoked = lr && typeof lr === "object" && typeof lr.schema === "string" && typeof lr.tick === "number" ? { schema: lr.schema, tick: lr.tick } : null;
20516
+ const sb = s["senseBuffer"];
20517
+ 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 } : {} })) : [];
20137
20518
  }
20138
20519
  async react(_delta, tick, state, _context) {
20139
20520
  const eligible = [];
@@ -20162,54 +20543,125 @@ var ActionSelector = class {
20162
20543
  let blocking = false;
20163
20544
  let awaiting = null;
20164
20545
  let composite = null;
20546
+ let deliberating = null;
20165
20547
  for (const it of intents) {
20166
- if (it.st === "deliberating") blocking = true;
20167
- else if (it.st === "expanding") composite = { id: it.id, activation: it.activation, schema: it.schema };
20548
+ if (it.st === "deliberating") {
20549
+ blocking = true;
20550
+ deliberating = { id: it.id, schema: it.schema };
20551
+ } else if (it.st === "expanding") composite = { id: it.id, activation: it.activation, schema: it.schema };
20168
20552
  else if (it.st === "awaiting") awaiting = { id: it.id, activation: it.activation, schema: it.schema, target: it.target, dispatchedAt: it.dispatchedAt };
20169
20553
  else if (it.st === "selected") {
20170
20554
  const activeMacroSub = it.parentIntentId !== void 0 && expandingParents.has(it.parentIntentId);
20171
20555
  if (!activeMacroSub) blocking = true;
20172
20556
  }
20173
20557
  }
20174
- const busy = (n) => ({ commands: { metrics: [
20175
- ["agency.field.eligible", n],
20176
- ["agency.selection.busy", 1]
20177
- ] } });
20558
+ const senseEvents = this._senseBuffer;
20559
+ this._senseBuffer = [];
20560
+ const rupture = computeRupture(state, tick, senseEvents);
20561
+ const prevStab = metric2(state, "situation.stability", 1);
20562
+ const nextStabRaw = clamp018(prevStab + STABILITY_RECOVERY * (1 - prevStab) - rupture);
20563
+ const nextStab = 1 - nextStabRaw < STABILITY_EPSILON ? 1 : nextStabRaw;
20564
+ const stabMetrics = rupture > 0 || prevStab < 1 ? [["situation.stability", nextStab]] : [];
20565
+ if (rupture > 0 && this._bus) {
20566
+ try {
20567
+ this._bus.publish({
20568
+ type: "agency.situation.rupture",
20569
+ version: 1,
20570
+ sourceEngine: this.name,
20571
+ salience: rupture,
20572
+ payload: { rupture, stability: nextStab, tick }
20573
+ });
20574
+ } catch (err) {
20575
+ logger.warn(`[selector] rupture publish failed: ${err instanceof Error ? err.message : String(err)}`);
20576
+ }
20577
+ }
20578
+ const busy = (n) => ({
20579
+ commands: {
20580
+ metrics: [
20581
+ ["agency.field.eligible", n],
20582
+ ["agency.selection.busy", 1],
20583
+ ...stabMetrics
20584
+ ]
20585
+ }
20586
+ });
20587
+ const policyRevoke = !!deliberating && refusedClassSchemas(state).has(deliberating.schema);
20588
+ if (deliberating && (rupture >= RUPTURE_REVOKE_GATE || policyRevoke)) {
20589
+ const reason = policyRevoke ? "policy-refusal" : "exafferent-rupture";
20590
+ const revRupture = policyRevoke ? Math.max(rupture, RUPTURE_REVOKE_GATE) : rupture;
20591
+ if (this._bus) {
20592
+ try {
20593
+ this._bus.publish({
20594
+ type: "agency.commitment.revoked",
20595
+ version: 1,
20596
+ sourceEngine: this.name,
20597
+ salience: 0.85,
20598
+ payload: { from: deliberating.schema, reason, rupture: revRupture, tick }
20599
+ });
20600
+ } catch (err) {
20601
+ logger.warn(`[selector] revoked publish failed: ${err instanceof Error ? err.message : String(err)}`);
20602
+ }
20603
+ }
20604
+ this._lastRevoked = { schema: deliberating.schema, tick };
20605
+ return {
20606
+ commands: {
20607
+ set: [revocationEntity(deliberating.id, deliberating.schema, revRupture, tick)],
20608
+ metrics: [
20609
+ ["agency.field.eligible", eligible.length],
20610
+ ["agency.selection.busy", 1],
20611
+ ["agency.commitment.revoked", 1],
20612
+ ...policyRevoke ? [["agency.policy.revoked", 1]] : [],
20613
+ ...stabMetrics
20614
+ ]
20615
+ }
20616
+ };
20617
+ }
20178
20618
  if (blocking) return busy(eligible.length);
20179
20619
  if (eligible.length === 0)
20180
- return { commands: { metrics: [["agency.field.eligible", 0], ["agency.selection.busy", awaiting || composite ? 1 : 0]] } };
20620
+ return {
20621
+ commands: {
20622
+ metrics: [
20623
+ ["agency.field.eligible", 0],
20624
+ ["agency.selection.busy", awaiting || composite ? 1 : 0],
20625
+ ...stabMetrics
20626
+ ]
20627
+ }
20628
+ };
20181
20629
  const bias = buildBias(state);
20182
- const effSwitchCost = effectiveSwitchCost(state);
20630
+ const effSwitchCost = effectiveSwitchCost(state) * (1 - rupture);
20183
20631
  const weights = effectiveWeights(state);
20184
20632
  const scored = eligible.map((a) => ({ affordance: a, activation: scoreAffordance(a, bias, weights) })).sort((x, y) => y.activation - x.activation);
20185
20633
  const winner = scored[0];
20186
20634
  if (!winner) return busy(eligible.length);
20635
+ let compositeTombstone;
20636
+ let compositeFrom;
20187
20637
  if (composite) {
20188
20638
  const different = winner.affordance.schema !== composite.schema;
20189
20639
  const switchCost = effSwitchCost * (1 - stakes(winner.affordance, bias));
20190
- if (different && winner.activation > composite.activation + switchCost) {
20191
- if (this._bus) try {
20640
+ if (!different || winner.activation <= composite.activation + switchCost)
20641
+ return busy(eligible.length);
20642
+ if (this._bus)
20643
+ try {
20192
20644
  this._bus.publish({
20193
20645
  type: "agency.action.preempted",
20194
20646
  version: 1,
20195
20647
  sourceEngine: this.name,
20196
20648
  salience: 0.8,
20197
- payload: { from: composite.schema, to: winner.affordance.schema, activation: winner.activation, tick }
20649
+ payload: {
20650
+ from: composite.schema,
20651
+ to: winner.affordance.schema,
20652
+ activation: winner.activation,
20653
+ tick
20654
+ }
20198
20655
  });
20199
20656
  } catch (err) {
20200
20657
  logger.warn(`[selector] preempt publish failed: ${err instanceof Error ? err.message : String(err)}`);
20201
20658
  }
20202
- return { commands: { delete: [composite.id], metrics: [
20203
- ["agency.field.eligible", eligible.length],
20204
- ["agency.selection.busy", 1],
20205
- ["agency.selection.preempted", 1]
20206
- ] } };
20207
- }
20208
- return busy(eligible.length);
20659
+ compositeTombstone = composite.id;
20660
+ compositeFrom = composite.schema;
20209
20661
  }
20210
20662
  let preemptDelete;
20211
- let preemptedFrom;
20212
- if (awaiting) {
20663
+ let preemptedFrom = compositeFrom;
20664
+ if (awaiting && !compositeTombstone) {
20213
20665
  const sameAction = winner.affordance.schema === awaiting.schema && (winner.affordance.targetEntityId ?? "") === awaiting.target;
20214
20666
  if (sameAction) return busy(eligible.length);
20215
20667
  const staleness = Math.min(1, (tick - awaiting.dispatchedAt) / AWAIT_STALE_TICKS);
@@ -20232,6 +20684,13 @@ var ActionSelector = class {
20232
20684
  const deliberate = margin < marginGate || stk > stakesGate;
20233
20685
  this._lastEntropy = entropy;
20234
20686
  this._lastDeliberate = deliberate;
20687
+ let revokedBy;
20688
+ if (this._lastRevoked) {
20689
+ if (deliberate && tick - this._lastRevoked.tick <= REVOKE_HINT_WINDOW)
20690
+ revokedBy = this._lastRevoked.schema;
20691
+ if (revokedBy || tick - this._lastRevoked.tick > REVOKE_HINT_WINDOW)
20692
+ this._lastRevoked = null;
20693
+ }
20235
20694
  const intent = {
20236
20695
  id: `agency-intent-${tick}`,
20237
20696
  type: "agency.intent",
@@ -20274,6 +20733,9 @@ var ActionSelector = class {
20274
20733
  // interrupted, so the deliberation facet can own the interruption in-character
20275
20734
  // ("I was about to X, but this pulled me away"). Only meaningful while deliberating.
20276
20735
  ...preemptedFrom ? { preemptedFrom } : {},
20736
+ // Channel B: this deliberation forms in the wake of a rupture-driven
20737
+ // revocation — the facet can own "something changed, I let go of X".
20738
+ ...revokedBy ? { revokedBy } : {},
20277
20739
  tick
20278
20740
  }
20279
20741
  };
@@ -20312,7 +20774,9 @@ var ActionSelector = class {
20312
20774
  }
20313
20775
  }
20314
20776
  const commands = {
20315
- set: [intent],
20777
+ // A composite preemption rides along as a tombstone (never a delete — the
20778
+ // executor's in-tick macro-advance would resurrect the parent).
20779
+ set: compositeTombstone ? [intent, revocationEntity(compositeTombstone, compositeFrom ?? "", rupture, tick)] : [intent],
20316
20780
  ...preemptDelete ? { delete: [preemptDelete] } : {},
20317
20781
  metrics: [
20318
20782
  ["agency.field.eligible", eligible.length],
@@ -20321,7 +20785,8 @@ var ActionSelector = class {
20321
20785
  ["agency.selection.margin", margin],
20322
20786
  ["agency.selection.deliberate", deliberate ? 1 : 0],
20323
20787
  ["agency.selection.preempted", preemptedFrom ? 1 : 0],
20324
- ["agency.selection.activation", winner.activation]
20788
+ ["agency.selection.activation", winner.activation],
20789
+ ...stabMetrics
20325
20790
  ]
20326
20791
  };
20327
20792
  return { commands };
@@ -20331,7 +20796,40 @@ function effectiveSwitchCost(state) {
20331
20796
  const params = readEffectiveParams(state, "engine-config-action-selector");
20332
20797
  const base = num2(params["switchCost"], BASE_SWITCH_COST);
20333
20798
  const focusTicks = metric2(state, "task_switch.current_focus_ticks", 0);
20334
- return base * (1 + focusTicks * FOCUS_GAIN);
20799
+ const stability = metric2(state, "situation.stability", 1);
20800
+ return base * (1 + focusTicks * FOCUS_GAIN * stability);
20801
+ }
20802
+ function computeRupture(state, tick, senseEvents = []) {
20803
+ let maxSalience = 0;
20804
+ for (const e of state.entities.values()) {
20805
+ if (e.type !== "percept") continue;
20806
+ const m = e.metadata;
20807
+ if (str2(m?.["provenance"]) !== "exafferent") continue;
20808
+ const pTick = num2(m?.["tick"], -1);
20809
+ if (pTick < 0 || tick - pTick > RUPTURE_WINDOW_TICKS) continue;
20810
+ const s = num2(m?.["salience"], 0);
20811
+ if (s > maxSalience) maxSalience = s;
20812
+ }
20813
+ if (senseEvents.length > 0) {
20814
+ const live = liveConsequences(state.entities, tick);
20815
+ for (const ev of senseEvents) {
20816
+ if (ev.text !== void 0 && live.length > 0 && matchConsequenceText(live, ev.text)) continue;
20817
+ if (ev.salience > maxSalience) maxSalience = ev.salience;
20818
+ }
20819
+ }
20820
+ if (maxSalience <= RUPTURE_SALIENCE_GATE) return 0;
20821
+ return clamp018((maxSalience - RUPTURE_SALIENCE_GATE) / (1 - RUPTURE_SALIENCE_GATE));
20822
+ }
20823
+ function refusedClassSchemas(state) {
20824
+ const out = /* @__PURE__ */ new Set();
20825
+ for (const e of state.entities.values()) {
20826
+ if (e.type !== "agency.outcome") continue;
20827
+ const m = e.metadata;
20828
+ if (m?.["refused"] !== true || str2(m?.["finality"]) !== "class") continue;
20829
+ const schema = str2(m?.["schema"]);
20830
+ if (schema) out.add(schema);
20831
+ }
20832
+ return out;
20335
20833
  }
20336
20834
  function effectiveWeights(state) {
20337
20835
  const p = readEffectiveParams(state, "engine-config-action-selector");
@@ -20432,19 +20930,28 @@ var DeliberationEngine = class {
20432
20930
  return { deliberations: this._deliberations };
20433
20931
  }
20434
20932
  async react(_delta, tick, state, _context) {
20933
+ const revoked = revokedIntentIds(state.entities, tick);
20934
+ const del = [];
20435
20935
  let target = null;
20436
20936
  for (const [id2, e] of state.entities) {
20437
20937
  if (e.type !== "agency.intent") continue;
20438
20938
  if (str3(e.metadata?.["status"]) !== "deliberating") continue;
20939
+ if (revoked.has(id2)) {
20940
+ del.push(id2, revocationId(id2));
20941
+ continue;
20942
+ }
20439
20943
  const meta2 = e.metadata ?? {};
20440
20944
  if (!target || id2 < target.id) target = { id: id2, meta: meta2 };
20441
20945
  }
20442
- if (!target) return { commands: {} };
20946
+ if (!target) return del.length > 0 ? { commands: { delete: del } } : { commands: {} };
20443
20947
  const { id, meta } = target;
20444
20948
  const provisional = str3(meta["schema"]) ?? "wait";
20445
20949
  const candidates = Array.isArray(meta["candidates"]) ? meta["candidates"] : [];
20446
20950
  if (!this._provider)
20447
- return { commands: { set: [this._commit(id, meta, provisional, candidates, "no-executive")] } };
20951
+ return { commands: {
20952
+ set: [this._commit(id, meta, provisional, candidates, "no-executive")],
20953
+ ...del.length > 0 ? { delete: del } : {}
20954
+ } };
20448
20955
  const chosen = await this._deliberate(state, candidates, provisional, meta);
20449
20956
  this._deliberations++;
20450
20957
  if (this._bus) {
@@ -20462,6 +20969,7 @@ var DeliberationEngine = class {
20462
20969
  return {
20463
20970
  commands: {
20464
20971
  set: [this._commit(id, meta, chosen, candidates, "facet")],
20972
+ ...del.length > 0 ? { delete: del } : {},
20465
20973
  metrics: [["agency.deliberation.count", 1]]
20466
20974
  }
20467
20975
  };
@@ -20519,7 +21027,10 @@ var DeliberationEngine = class {
20519
21027
  _buildFocusContent(state, candidates, meta) {
20520
21028
  const lines = [];
20521
21029
  const preemptedFrom = str3(meta["preemptedFrom"]);
20522
- if (preemptedFrom)
21030
+ const revokedBy = str3(meta["revokedBy"]);
21031
+ if (revokedBy)
21032
+ lines.push(`Something in my situation just shifted and I let go of what I was weighing ("${revokedBy}"). Decide afresh what to do now:`);
21033
+ else if (preemptedFrom)
20523
21034
  lines.push(`I just broke off a pending action ("${preemptedFrom}") because something more pressing pulled at me. Decide what to do now:`);
20524
21035
  else
20525
21036
  lines.push("My automatic action-selection was uncertain. Candidate actions:");
@@ -20692,8 +21203,22 @@ var MotorSchemaExecutor = class {
20692
21203
  const metrics = [];
20693
21204
  const energy = state.metrics.get("energy.level") ?? 100;
20694
21205
  const stress = state.metrics.get("stress.load") ?? 0;
21206
+ for (const [id, e] of state.entities) {
21207
+ if (e.type !== CONSEQUENCE_TYPE) continue;
21208
+ if (tick >= num3(e.metadata?.["expiresAt"], 0)) del.push(id);
21209
+ }
21210
+ del.push(...staleRevocationIds(state.entities, tick));
21211
+ const revoked = revokedIntentIds(state.entities, tick);
21212
+ if (revoked.size > 0)
21213
+ for (const [id, e] of state.entities) {
21214
+ if (e.type !== "agency.intent") continue;
21215
+ const parentId = str5(e.metadata?.["parentIntentId"]);
21216
+ if (revoked.has(id)) del.push(id, revocationId(id));
21217
+ else if (parentId && revoked.has(parentId)) del.push(id);
21218
+ }
20695
21219
  for (const [id, e] of state.entities) {
20696
21220
  if (e.type !== "agency.intent" || str5(e.metadata?.["status"]) !== "awaiting") continue;
21221
+ if (e.metadata?.["escalated"] === true) continue;
20697
21222
  const dispatchedAt = num3(e.metadata?.["dispatchedAt"], tick);
20698
21223
  if (tick - dispatchedAt < AWAIT_TIMEOUT) continue;
20699
21224
  const intent = readIntent(id, e.metadata);
@@ -20718,6 +21243,9 @@ var MotorSchemaExecutor = class {
20718
21243
  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);
20719
21244
  let enactedCount = 0;
20720
21245
  for (const [id, e] of selected) {
21246
+ if (revoked.has(id)) continue;
21247
+ const parentOf = str5(e.metadata?.["parentIntentId"]);
21248
+ if (parentOf && revoked.has(parentOf)) continue;
20721
21249
  const intent = readIntent(id, e.metadata);
20722
21250
  const schema = this._resolve(intent.schema);
20723
21251
  if (schema?.kind === "composite" && schema.composedOf && schema.composedOf.length > 0) {
@@ -20766,6 +21294,18 @@ var MotorSchemaExecutor = class {
20766
21294
  predictedValence: predicted.expectedValence
20767
21295
  }
20768
21296
  });
21297
+ const awaitingText = enaction.mode === "communicate" ? str5(intent.parameters["content"]) ?? firstMessage(intent.parameters["messages"]) : void 0;
21298
+ set.push(consequenceEntity({
21299
+ intentId: id,
21300
+ schema: intent.schema,
21301
+ mode: enaction.mode === "communicate" ? "communicate" : "external",
21302
+ ...enaction.mode === "communicate" ? { effector: COMM_SCHEMA_TO_EFFECTOR[intent.schema] ?? intent.schema } : {},
21303
+ ...intent.targetEntityId ? { targetEntityId: intent.targetEntityId } : {},
21304
+ ...awaitingText ? { text: awaitingText, textHash: fnv1a(awaitingText) } : {},
21305
+ paramsHash: fnv1a(paramsKey(intent.parameters)),
21306
+ expiresAt: tick + CONSEQUENCE_TTL_TICKS,
21307
+ tick
21308
+ }));
20769
21309
  this._emitDispatch(intent, enaction.mode, tick);
20770
21310
  metrics.push([enaction.mode === "communicate" ? "agency.communicate.dispatched" : "agency.invocation.dispatched", 1]);
20771
21311
  }
@@ -20932,6 +21472,18 @@ var MotorSchemaExecutor = class {
20932
21472
  };
20933
21473
  set.push(outcomeEntity(tick, intent, out, predicted));
20934
21474
  del.push(id);
21475
+ if (result.success)
21476
+ set.push(consequenceEntity({
21477
+ intentId: id,
21478
+ schema: intent.schema,
21479
+ mode: "communicate",
21480
+ effector,
21481
+ ...intent.targetEntityId ? { targetEntityId: intent.targetEntityId } : {},
21482
+ textHash: fnv1a(bubbles.join("\n")),
21483
+ text: bubbles.join("\n"),
21484
+ expiresAt: tick + CONSEQUENCE_TTL_TICKS,
21485
+ tick
21486
+ }));
20935
21487
  this._emitEnacted(intent, out, predicted, tick);
20936
21488
  this._emitActionOutcome(
20937
21489
  intent,
@@ -21070,6 +21622,10 @@ function errMsg(err) {
21070
21622
 
21071
21623
  // src/cognition/agency/engines/reafference.engine.ts
21072
21624
  var PROC_THRESHOLD2 = 0.6;
21625
+ var SENSORY_SOFT_QUALITY = 0.6;
21626
+ var SENSORY_VALENCE_SPAN_ENTITY = 0.25;
21627
+ var SENSORY_VALENCE_SPAN_AMBIENT = 0.1;
21628
+ var SENSORY_SUCCESS_FLOOR = 0.5;
21073
21629
  var ReafferenceEngine = class {
21074
21630
  name = "reafference";
21075
21631
  _repertoire;
@@ -21119,14 +21675,76 @@ var ReafferenceEngine = class {
21119
21675
  const set = [];
21120
21676
  const del = [];
21121
21677
  const metrics = [];
21122
- let updates = 0;
21123
- let discovered = 0;
21678
+ const outcomes = [];
21679
+ const gradedIntentIds = /* @__PURE__ */ new Set();
21124
21680
  for (const [id, e] of state.entities) {
21125
21681
  if (e.type !== "agency.outcome") continue;
21126
21682
  const m = e.metadata ?? {};
21683
+ outcomes.push({ id, meta: m, fromState: true });
21684
+ const iid = str6(m["intentId"]);
21685
+ if (iid) gradedIntentIds.add(iid);
21686
+ }
21687
+ const awaiting = /* @__PURE__ */ new Map();
21688
+ for (const [id, e] of state.entities) {
21689
+ if (e.type !== "agency.intent" || str6(e.metadata?.["status"]) !== "awaiting") continue;
21690
+ const m = e.metadata ?? {};
21691
+ if (tick - num4(m["dispatchedAt"], tick) >= AWAIT_TIMEOUT) continue;
21692
+ awaiting.set(id, {
21693
+ schema: str6(m["schema"]) ?? "",
21694
+ predictedReward: num4(m["predictedReward"], 0.5),
21695
+ predictedValence: num4(m["predictedValence"], 0)
21696
+ });
21697
+ }
21698
+ let sensory = 0;
21699
+ const sensedIntentIds = /* @__PURE__ */ new Set();
21700
+ for (const [, e] of state.entities) {
21701
+ if (e.type !== "percept") continue;
21702
+ const m = e.metadata ?? {};
21703
+ if (str6(m["provenance"]) !== "reafferent") continue;
21704
+ const iid = str6(m["sourceIntentId"]);
21705
+ if (!iid || gradedIntentIds.has(iid) || sensedIntentIds.has(iid)) continue;
21706
+ const aw = awaiting.get(iid);
21707
+ if (!aw || !aw.schema) continue;
21708
+ sensedIntentIds.add(iid);
21709
+ sensory++;
21710
+ const feltRaw = m["valence"];
21711
+ const felt = typeof feltRaw === "number" && Number.isFinite(feltRaw) ? feltRaw : void 0;
21712
+ const span = str6(m["valenceSource"]) === "entity" ? SENSORY_VALENCE_SPAN_ENTITY : SENSORY_VALENCE_SPAN_AMBIENT;
21713
+ const quality = felt === void 0 ? SENSORY_SOFT_QUALITY : clamp0111(SENSORY_SOFT_QUALITY + Math.max(-1, Math.min(1, felt)) * span);
21714
+ outcomes.push({ id: `agency-outcome-${tick}-${iid}-sensory`, fromState: false, meta: {
21715
+ schema: aw.schema,
21716
+ intentId: iid,
21717
+ success: quality >= SENSORY_SUCCESS_FLOOR,
21718
+ outcomeQuality: quality,
21719
+ valence: felt ?? aw.predictedValence,
21720
+ predictedReward: aw.predictedReward,
21721
+ predictedValence: aw.predictedValence,
21722
+ surprise: clamp0111(Math.abs(aw.predictedReward - quality)),
21723
+ mode: "external",
21724
+ reconciled: true,
21725
+ sensory: true,
21726
+ ...felt !== void 0 ? { sensoryValence: felt, valenceSource: str6(m["valenceSource"]) } : {},
21727
+ tick
21728
+ } });
21729
+ }
21730
+ let updates = 0;
21731
+ let discovered = 0;
21732
+ let refused = 0;
21733
+ for (const { id, meta: m, fromState } of outcomes) {
21127
21734
  const schema = str6(m["schema"]);
21128
21735
  if (!schema) {
21129
- del.push(id);
21736
+ if (fromState) del.push(id);
21737
+ continue;
21738
+ }
21739
+ if (m["refused"] === true) {
21740
+ const finality = str6(m["finality"]) === "class" ? "class" : "instance";
21741
+ this._repertoire.recordRefusal(schema, finality, tick);
21742
+ if (fromState) del.push(id);
21743
+ const refusedIntent = str6(m["intentId"]);
21744
+ if (refusedIntent) del.push(refusedIntent);
21745
+ const refusedPlan = str6(m["planId"]);
21746
+ if (refusedPlan) this._emitPlanOutcome(refusedPlan, str6(m["stepId"]), schema, false, 0, 0, tick);
21747
+ refused++;
21130
21748
  continue;
21131
21749
  }
21132
21750
  const { skill, proceduralized } = this._repertoire.recordOutcome({
@@ -21138,7 +21756,7 @@ var ReafferenceEngine = class {
21138
21756
  tick
21139
21757
  });
21140
21758
  set.push(skillEntity(skill));
21141
- del.push(id);
21759
+ if (fromState) del.push(id);
21142
21760
  const intentId = str6(m["intentId"]);
21143
21761
  if (intentId) del.push(intentId);
21144
21762
  updates++;
@@ -21155,19 +21773,24 @@ var ReafferenceEngine = class {
21155
21773
  }
21156
21774
  }
21157
21775
  const dropped = this._repertoire.decay(tick);
21158
- for (const id of dropped) {
21776
+ for (const id of dropped.skills) {
21159
21777
  del.push(`agency-skill-${id}`);
21160
21778
  del.push(schemaEntityId(id));
21161
21779
  }
21780
+ for (const id of dropped.availability)
21781
+ del.push(availabilityEntityId(id));
21162
21782
  for (const e of this._repertoire.compositeEntities()) set.push(e);
21783
+ for (const e of this._repertoire.availabilityEntities()) set.push(e);
21163
21784
  const skills = this._repertoire.skills();
21164
21785
  const habitual = [...skills.values()].filter((s) => s.habitStrength >= PROC_THRESHOLD2).length;
21165
21786
  metrics.push(
21166
21787
  ["agency.learning.updates", updates],
21167
21788
  ["agency.discovered.count", discovered],
21168
21789
  ["agency.skill.count", skills.size],
21169
- ["agency.habitual.count", habitual]
21790
+ ["agency.habitual.count", habitual],
21791
+ ["agency.sensory.confirmed", sensory]
21170
21792
  );
21793
+ if (refused > 0) metrics.push(["agency.refused.count", refused]);
21171
21794
  return { commands: { set, delete: del, metrics } };
21172
21795
  }
21173
21796
  _emitProceduralized(skill, tick) {
@@ -21250,6 +21873,9 @@ function str6(v) {
21250
21873
  function num4(v, fallback) {
21251
21874
  return typeof v === "number" && Number.isFinite(v) ? v : fallback;
21252
21875
  }
21876
+ function clamp0111(n) {
21877
+ return n < 0 ? 0 : n > 1 ? 1 : n;
21878
+ }
21253
21879
 
21254
21880
  // src/cognition/config.mirror.entities.ts
21255
21881
  function buildEngineConfigEntities(config, executiveInterval) {
@@ -23981,6 +24607,10 @@ var OutboxController = class {
23981
24607
  summary: delivered ? `My message was delivered successfully.` : `My message failed to reach the recipient.`,
23982
24608
  salience: delivered ? 0.35 : 0.6,
23983
24609
  changeType: delivered ? "delivered" : "failed",
24610
+ // Reafference by construction — this percept describes our own action's
24611
+ // outcome ("ear hears the word"). Tagged, not attenuated: it is the ack
24612
+ // surface, not a content echo (EXAFFERENCE P2).
24613
+ provenance: "reafferent",
23984
24614
  messageId
23985
24615
  }
23986
24616
  });
@@ -24382,7 +25012,7 @@ var InboundQueue = class {
24382
25012
  function reconcileInvocation(intentId, schema, result, tick, predicted = { reward: 0.5, valence: 0 }, provenance = {}) {
24383
25013
  const outcomeQuality = result.outcomeQuality ?? (result.success ? 0.8 : 0.1);
24384
25014
  const valence = result.valence ?? (result.success ? 0.2 : -0.2);
24385
- const surprise = clamp0111(Math.abs(predicted.reward - outcomeQuality));
25015
+ const surprise = clamp0112(Math.abs(predicted.reward - outcomeQuality));
24386
25016
  return {
24387
25017
  id: `agency-outcome-${tick}-${intentId}`,
24388
25018
  type: "agency.outcome",
@@ -24399,17 +25029,66 @@ function reconcileInvocation(intentId, schema, result, tick, predicted = { rewar
24399
25029
  mode: "external",
24400
25030
  tick,
24401
25031
  reconciled: true,
25032
+ ...result.refused ? { refused: true, finality: result.finality ?? "instance" } : {},
24402
25033
  ...provenance.planId ? { planId: provenance.planId } : {},
24403
25034
  ...provenance.stepId ? { stepId: provenance.stepId } : {}
24404
25035
  }
24405
25036
  };
24406
25037
  }
24407
- function clamp0111(n) {
25038
+ function clamp0112(n) {
24408
25039
  return n < 0 ? 0 : n > 1 ? 1 : n;
24409
25040
  }
24410
25041
 
25042
+ // src/stem/policy/arbiter.ts
25043
+ var ALLOW = Object.freeze({ decision: "allow" });
25044
+ var NULL_ARBITER = {
25045
+ name: "null",
25046
+ evaluate() {
25047
+ return ALLOW;
25048
+ }
25049
+ };
25050
+ function isNullArbiter(arbiter) {
25051
+ return !arbiter || arbiter === NULL_ARBITER;
25052
+ }
25053
+
25054
+ // src/stem/policy/verdict.recorder.ts
25055
+ var _sinks3 = /* @__PURE__ */ new Map();
25056
+ function getVerdictRecorder(willId) {
25057
+ return _sinks3.get(willId);
25058
+ }
25059
+ var _sources3 = /* @__PURE__ */ new Map();
25060
+ function getVerdictSource(willId) {
25061
+ return _sources3.get(willId);
25062
+ }
25063
+
24411
25064
  // src/stem/tracts/effector.controller.ts
25065
+ var ESCALATION_TTL_TICKS = 30;
24412
25066
  var effectorController = class {
25067
+ /** The Policy Decision Point consulted before an invocation reaches the world.
25068
+ * Defaults to the no-op arbiter, so an unconfigured Will is byte-identical. */
25069
+ _arbiter = NULL_ARBITER;
25070
+ /**
25071
+ * Denials queued during a step's flush, drained at the NEXT tick boundary
25072
+ * (POLICY_REAFFERENCE P1). Keyed by willId — harness state, exactly like
25073
+ * `pendingEffectorInvocations`; never simulation state, so it does not touch
25074
+ * `simulation.step` determinism and is regenerated on any re-execution.
25075
+ */
25076
+ _pendingRefusals = /* @__PURE__ */ new Map();
25077
+ /** Escalations awaiting their first application (mark intent + voice the ask). */
25078
+ _newEscalations = /* @__PURE__ */ new Map();
25079
+ /** Escalations currently held, keyed by intent id — the resolvable set. */
25080
+ _activeEscalations = /* @__PURE__ */ new Map();
25081
+ /** Host answers awaiting application at the next tick boundary. */
25082
+ _pendingResolutions = /* @__PURE__ */ new Map();
25083
+ /**
25084
+ * Install a Policy Decision Point (POLICY_REAFFERENCE P0). Passing null
25085
+ * restores the no-op default. The arbiter sees only the proposed act — never
25086
+ * simulation state — and its verdict decides whether the invocation is
25087
+ * handed to the host at all.
25088
+ */
25089
+ setArbiter(arbiter) {
25090
+ this._arbiter = arbiter ?? NULL_ARBITER;
25091
+ }
24413
25092
  /**
24414
25093
  * Update the set of allowed communication effectors at runtime via AccessGrants
24415
25094
  * (the permission / sense gate the senses + reply path read).
@@ -24425,6 +25104,224 @@ var effectorController = class {
24425
25104
  * echoes it on its result-ack, and `confirmExecution` uses it to find the intent.
24426
25105
  */
24427
25106
  bufferInvocation(instance, payload) {
25107
+ const willId = instance.config.id;
25108
+ const source = getVerdictSource(willId);
25109
+ if (source) {
25110
+ const invocation2 = toPolicyInvocation(instance, payload);
25111
+ const record = source.verdictFor(invocation2.tick, invocation2.intentId);
25112
+ if (record) this._applyVerdict(instance, payload, invocation2, recordToVerdict(record));
25113
+ else this._buffer(instance, payload);
25114
+ return;
25115
+ }
25116
+ if (isNullArbiter(this._arbiter)) {
25117
+ this._buffer(instance, payload);
25118
+ return;
25119
+ }
25120
+ const invocation = toPolicyInvocation(instance, payload);
25121
+ let verdict;
25122
+ try {
25123
+ verdict = this._arbiter.evaluate(invocation);
25124
+ } catch (err) {
25125
+ logger.error(`[policy] arbiter "${this._arbiter.name}" threw for "${invocation.schema}" \u2014 failing closed:`, err);
25126
+ return;
25127
+ }
25128
+ if (verdict instanceof Promise) {
25129
+ void verdict.then(
25130
+ (v) => this._recordAndApply(instance, payload, invocation, v),
25131
+ (err) => logger.error(`[policy] arbiter "${this._arbiter.name}" rejected for "${invocation.schema}" \u2014 failing closed:`, err)
25132
+ );
25133
+ return;
25134
+ }
25135
+ this._recordAndApply(instance, payload, invocation, verdict);
25136
+ }
25137
+ /** Capture the verdict on the tape (if a recorder is attached), then enforce it. */
25138
+ _recordAndApply(instance, payload, invocation, verdict) {
25139
+ const sink = getVerdictRecorder(instance.config.id);
25140
+ sink?.recordVerdict({
25141
+ tick: invocation.tick,
25142
+ willId: instance.config.id,
25143
+ intentId: invocation.intentId,
25144
+ schema: invocation.schema,
25145
+ arbiter: this._arbiter.name,
25146
+ decision: verdict.decision,
25147
+ ...verdict.reasonCode ? { reasonCode: verdict.reasonCode } : {},
25148
+ ...verdict.finality ? { finality: verdict.finality } : {},
25149
+ ...verdict.counterfactual ? { counterfactual: verdict.counterfactual } : {},
25150
+ timestamp: Date.now()
25151
+ });
25152
+ this._applyVerdict(instance, payload, invocation, verdict);
25153
+ }
25154
+ /**
25155
+ * Enforce a verdict (POLICY_REAFFERENCE P1).
25156
+ *
25157
+ * • allow → hand the invocation to the world.
25158
+ * • deny → queue a refusal ack, applied at the next tick boundary via
25159
+ * `confirmExecution` — the same lifecycle as a host rejection,
25160
+ * so the mind meets *world resistance*, not a permission dialog.
25161
+ * • escalate → raise a held escalation (POLICY_REAFFERENCE P4): the intent is
25162
+ * held (the executor stops timing it out), the Will voices a
25163
+ * first-person ask once, and a host resolution later approves
25164
+ * (dispatch) or denies (refuse). Unresolved, it degrades to a
25165
+ * refusal at ESCALATION_TTL_TICKS.
25166
+ *
25167
+ * P1's refusal reconciles as a plain FAILURE — safe, but the wrong learning
25168
+ * signal (forbidden ≠ unskilled). P2 routes it to affordance AVAILABILITY
25169
+ * instead of competence.
25170
+ */
25171
+ _applyVerdict(instance, payload, invocation, verdict) {
25172
+ if (verdict.decision === "allow") {
25173
+ this._buffer(instance, payload);
25174
+ return;
25175
+ }
25176
+ const cf = verdict.counterfactual;
25177
+ logger.info(
25178
+ `[policy] ${verdict.decision.toUpperCase()} "${invocation.schema}" intent "${invocation.intentId}" \u2014 ${verdict.reasonCode ?? "no reason code"}` + (verdict.finality ? ` (${verdict.finality})` : "") + (cf ? ` [${cf.field}: requested ${JSON.stringify(cf.requested)}, allowed ${JSON.stringify(cf.allowed)}]` : "")
25179
+ );
25180
+ if (verdict.decision === "deny") {
25181
+ const queue = this._pendingRefusals.get(instance.config.id) ?? [];
25182
+ queue.push({
25183
+ intentId: invocation.intentId,
25184
+ schema: invocation.schema,
25185
+ reasonCode: verdict.reasonCode ?? "POLICY_DENIED",
25186
+ finality: verdict.finality ?? "instance"
25187
+ });
25188
+ this._pendingRefusals.set(instance.config.id, queue);
25189
+ return;
25190
+ }
25191
+ const escalations = this._newEscalations.get(instance.config.id) ?? [];
25192
+ escalations.push({
25193
+ intentId: invocation.intentId,
25194
+ schema: invocation.schema,
25195
+ reasonCode: verdict.reasonCode ?? "APPROVAL_REQUIRED",
25196
+ payload,
25197
+ expiresAt: 0
25198
+ // stamped when applied (we don't have the current tick here)
25199
+ });
25200
+ this._newEscalations.set(instance.config.id, escalations);
25201
+ }
25202
+ /**
25203
+ * Record a host's answer to an escalation (POLICY_REAFFERENCE P4). Applied at
25204
+ * the next tick boundary so every simulation-state write stays on the boundary:
25205
+ * approve dispatches the held invocation to the world; deny refuses it. A
25206
+ * no-op if the intent id is not (or no longer) an active escalation.
25207
+ */
25208
+ resolveEscalation(instance, intentId, approved) {
25209
+ const queue = this._pendingResolutions.get(instance.config.id) ?? [];
25210
+ queue.push({ intentId, approved });
25211
+ this._pendingResolutions.set(instance.config.id, queue);
25212
+ }
25213
+ /**
25214
+ * Apply queued policy refusals as failure acks (POLICY_REAFFERENCE P1).
25215
+ * Called by the tick loop at the same boundary as inbound acks — BEFORE the
25216
+ * step, stamped to this tick — so a denial reconciled here is the exact
25217
+ * lifecycle of a host rejection that arrived between ticks.
25218
+ */
25219
+ applyPolicyOutcomes(instance) {
25220
+ const tick = instance.tickCount;
25221
+ this._applyResolutions(instance);
25222
+ this._expireEscalations(instance, tick);
25223
+ this._applyNewEscalations(instance, tick);
25224
+ this._applyRefusals(instance);
25225
+ }
25226
+ /** Drain queued refusals into failure acks (POLICY_REAFFERENCE P1). */
25227
+ _applyRefusals(instance) {
25228
+ const queue = this._pendingRefusals.get(instance.config.id);
25229
+ if (!queue || queue.length === 0) return;
25230
+ this._pendingRefusals.set(instance.config.id, []);
25231
+ for (const refusal of queue)
25232
+ this.confirmExecution(instance, refusal.intentId, {
25233
+ success: false,
25234
+ refused: true,
25235
+ finality: refusal.finality === "class" ? "class" : "instance",
25236
+ description: `refused by policy: ${refusal.reasonCode} (${refusal.finality})`
25237
+ });
25238
+ }
25239
+ /** Raise each newly-escalated intent (POLICY_REAFFERENCE P4): mark it held in
25240
+ * simulation state, voice the ask ONCE, and move it to the resolvable set. */
25241
+ _applyNewEscalations(instance, tick) {
25242
+ const pending = this._newEscalations.get(instance.config.id);
25243
+ if (!pending || pending.length === 0) return;
25244
+ this._newEscalations.set(instance.config.id, []);
25245
+ const active = this._activeEscalations.get(instance.config.id) ?? /* @__PURE__ */ new Map();
25246
+ for (const esc of pending) {
25247
+ esc.expiresAt = tick + ESCALATION_TTL_TICKS;
25248
+ this._markEscalated(instance, esc.intentId, esc.expiresAt);
25249
+ this._voiceEscalation(instance, esc);
25250
+ active.set(esc.intentId, esc);
25251
+ }
25252
+ this._activeEscalations.set(instance.config.id, active);
25253
+ }
25254
+ /** Apply host answers to active escalations (POLICY_REAFFERENCE P4). */
25255
+ _applyResolutions(instance) {
25256
+ const queue = this._pendingResolutions.get(instance.config.id);
25257
+ if (!queue || queue.length === 0) return;
25258
+ this._pendingResolutions.set(instance.config.id, []);
25259
+ const active = this._activeEscalations.get(instance.config.id);
25260
+ for (const { intentId, approved } of queue) {
25261
+ const esc = active?.get(intentId);
25262
+ if (!esc) continue;
25263
+ active.delete(intentId);
25264
+ this._clearEscalated(instance, intentId);
25265
+ if (approved) {
25266
+ this._buffer(instance, esc.payload);
25267
+ logger.info(`[policy] escalation APPROVED \u2192 dispatching "${esc.schema}" intent "${intentId}"`);
25268
+ } else {
25269
+ this._queueRefusal(instance, esc.intentId, esc.schema, esc.reasonCode, "class");
25270
+ logger.info(`[policy] escalation DENIED \u2192 refusing "${esc.schema}" intent "${intentId}"`);
25271
+ }
25272
+ }
25273
+ }
25274
+ /** Degrade escalations no one answered in time into instance-refusals (P4). */
25275
+ _expireEscalations(instance, tick) {
25276
+ const active = this._activeEscalations.get(instance.config.id);
25277
+ if (!active || active.size === 0) return;
25278
+ for (const [intentId, esc] of active) {
25279
+ if (tick < esc.expiresAt) continue;
25280
+ active.delete(intentId);
25281
+ this._clearEscalated(instance, intentId);
25282
+ this._queueRefusal(instance, esc.intentId, esc.schema, "ESCALATION_EXPIRED", "instance");
25283
+ logger.info(`[policy] escalation EXPIRED \u2192 refusing "${esc.schema}" intent "${intentId}"`);
25284
+ }
25285
+ }
25286
+ /** Push a refusal onto the queue drained by _applyRefusals this same tick. */
25287
+ _queueRefusal(instance, intentId, schema, reasonCode, finality) {
25288
+ const queue = this._pendingRefusals.get(instance.config.id) ?? [];
25289
+ queue.push({ intentId, schema, reasonCode, finality });
25290
+ this._pendingRefusals.set(instance.config.id, queue);
25291
+ }
25292
+ /** Mark the awaiting intent held: the executor stops timing it out (P4). */
25293
+ _markEscalated(instance, intentId, expiresAt) {
25294
+ const intent = instance.simulation.stateManager.snapshot().entities.get(intentId);
25295
+ if (!intent || intent.type !== "agency.intent") return;
25296
+ instance.simulation.stateManager.setEntity({
25297
+ id: intent.id,
25298
+ type: intent.type,
25299
+ metadata: { ...intent.metadata ?? {}, escalated: true, escalationExpiresAt: expiresAt }
25300
+ });
25301
+ }
25302
+ /** Release the hold so the executor resumes normal timeout for this intent. */
25303
+ _clearEscalated(instance, intentId) {
25304
+ const intent = instance.simulation.stateManager.snapshot().entities.get(intentId);
25305
+ if (!intent || intent.type !== "agency.intent") return;
25306
+ const meta = { ...intent.metadata ?? {} };
25307
+ delete meta["escalated"];
25308
+ delete meta["escalationExpiresAt"];
25309
+ instance.simulation.stateManager.setEntity({ id: intent.id, type: intent.type, metadata: meta });
25310
+ }
25311
+ /** Voice the escalation as a first-person broadcast ask — once, at raise time. */
25312
+ _voiceEscalation(instance, esc) {
25313
+ try {
25314
+ instance.cognition.outboxWriter.enqueue({
25315
+ targetEntityId: "*",
25316
+ content: escalationAsk(esc.schema, esc.reasonCode),
25317
+ effectorName: "broadcast"
25318
+ });
25319
+ } catch (err) {
25320
+ logger.warn(`[policy] escalation voice failed for "${esc.schema}": ${errMsg2(err)}`);
25321
+ }
25322
+ }
25323
+ /** Queue an approved invocation for the delivery layer. */
25324
+ _buffer(instance, payload) {
24428
25325
  const intentId = payload.intentId ?? "";
24429
25326
  instance.pendingEffectorInvocations.push({
24430
25327
  id: intentId,
@@ -24499,6 +25396,38 @@ var effectorController = class {
24499
25396
  function num5(v, fallback) {
24500
25397
  return typeof v === "number" && Number.isFinite(v) ? v : fallback;
24501
25398
  }
25399
+ function escalationAsk(schema, reasonCode) {
25400
+ const meaning = ESCALATION_MEANINGS[reasonCode] ?? "I need your approval before I can do this";
25401
+ return `I want to ${schema}, but ${meaning}. May I go ahead?`;
25402
+ }
25403
+ var ESCALATION_MEANINGS = {
25404
+ APPROVAL_REQUIRED: "I need your approval before I can on my own",
25405
+ WRITE_REQUIRES_APPROVAL: "it writes to the world and I shouldn't on my own",
25406
+ PAYMENT_REQUIRES_APPROVAL: "it moves money and I must not do that unattended",
25407
+ DEPLOY_REQUIRES_APPROVAL: "it ships something and needs a human to sign off"
25408
+ };
25409
+ function errMsg2(err) {
25410
+ return err instanceof Error ? err.message : String(err);
25411
+ }
25412
+ function recordToVerdict(record) {
25413
+ return {
25414
+ decision: record.decision,
25415
+ ...record.reasonCode ? { reasonCode: record.reasonCode } : {},
25416
+ ...record.finality ? { finality: record.finality } : {},
25417
+ ...record.counterfactual ? { counterfactual: record.counterfactual } : {}
25418
+ };
25419
+ }
25420
+ function toPolicyInvocation(instance, payload) {
25421
+ return {
25422
+ willId: instance.config.id,
25423
+ intentId: payload.intentId ?? "",
25424
+ schema: payload.schema ?? "",
25425
+ parameters: payload.parameters ?? {},
25426
+ ...typeof payload.targetEntityId === "string" ? { targetEntityId: payload.targetEntityId } : {},
25427
+ ...typeof payload.description === "string" ? { description: payload.description } : {},
25428
+ tick: payload.tick ?? 0
25429
+ };
25430
+ }
24502
25431
 
24503
25432
  // src/stem/tracts/sensory.controller.ts
24504
25433
  var SensoryController = class {
@@ -25301,6 +26230,15 @@ var WillStem = class {
25301
26230
  confirmEffectorExecution(id, invocationId, result) {
25302
26231
  this._effector.confirmExecution(this._get(id), invocationId, result);
25303
26232
  }
26233
+ /**
26234
+ * Resolve a policy escalation the Will raised (POLICY_REAFFERENCE P4).
26235
+ * `approved` dispatches the held invocation to the world; otherwise it is
26236
+ * refused. Applied at the next tick boundary. `invocationId` is the awaiting
26237
+ * `agency.intent` id the escalation ask referenced.
26238
+ */
26239
+ resolveEscalation(id, invocationId, approved) {
26240
+ this._effector.resolveEscalation(this._get(id), invocationId, approved);
26241
+ }
25304
26242
  // ── Messaging / outbox (11.1) ────────────────────────────────────────────
25305
26243
  // Delegates to OutboxController (R5-c). `_get(id)` validates the Will exists
25306
26244
  // and supplies the WillInstance; the outbox ops touch only instance fields.
@@ -25431,6 +26369,7 @@ var WillStem = class {
25431
26369
  outbox: this._outbox,
25432
26370
  sensory: this._sensory
25433
26371
  });
26372
+ this._effector.applyPolicyOutcomes(instance);
25434
26373
  await instance.simulation.step(1);
25435
26374
  instance.tickCount++;
25436
26375
  instance.lastTickAt = /* @__PURE__ */ new Date();