@mindot/will 0.8.0 → 0.10.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 (142) hide show
  1. package/README.md +15 -11
  2. package/dist/index.d.ts +188 -141
  3. package/dist/index.js +6985 -3337
  4. package/dist/index.js.map +1 -1
  5. package/dist/surface/channels/discord.d.ts +177 -0
  6. package/dist/surface/channels/discord.js +394 -0
  7. package/dist/surface/channels/discord.js.map +1 -0
  8. package/dist/{channels → surface/channels}/whatsapp.d.ts +2 -2
  9. package/dist/{channels → surface/channels}/whatsapp.js +11 -5
  10. package/dist/surface/channels/whatsapp.js.map +1 -0
  11. package/dist/{cli.js → surface/cli.js} +5382 -1573
  12. package/dist/surface/cli.js.map +1 -0
  13. package/dist/{mcp → surface/mcp}/effectors.d.ts +1 -1
  14. package/dist/{mcp → surface/mcp}/effectors.js +10 -4
  15. package/dist/surface/mcp/effectors.js.map +1 -0
  16. package/dist/{will-cS6k4uiJ.d.ts → will-evj9_vrd.d.ts} +6658 -5078
  17. package/package.json +11 -12
  18. package/src/cognition/agency/consequence.ts +356 -1
  19. package/src/cognition/agency/conversation.aim.ts +292 -0
  20. package/src/cognition/agency/engines/action.selector.ts +142 -5
  21. package/src/cognition/agency/engines/affordance.synthesizer.ts +200 -7
  22. package/src/cognition/agency/engines/deliberation.engine.ts +76 -3
  23. package/src/cognition/agency/engines/motor.schema.executor.ts +509 -32
  24. package/src/cognition/agency/engines/reafference.engine.ts +160 -4
  25. package/src/cognition/agency/execution.primitives.ts +17 -4
  26. package/src/cognition/agency/proactive.communicator.ts +19 -3
  27. package/src/cognition/agency/reconcile.learning.ts +5 -4
  28. package/src/cognition/agency/restart.ts +66 -0
  29. package/src/cognition/agency/schemas/innate.ts +96 -1
  30. package/src/cognition/agency/schemas/repertoire.ts +19 -1
  31. package/src/cognition/agency/selection.scoring.ts +49 -0
  32. package/src/cognition/agency/settlement.ts +203 -0
  33. package/src/cognition/agency/types.ts +73 -1
  34. package/src/cognition/cache/composition.ts +232 -0
  35. package/src/cognition/cache/deliberation.cache.ts +219 -0
  36. package/src/cognition/cache/fingerprint.ts +120 -0
  37. package/src/cognition/cache/types.ts +105 -0
  38. package/src/cognition/config.mirror.entities.ts +108 -1
  39. package/src/cognition/event.schemas.ts +22 -8
  40. package/src/cognition/faculties/affective.blender.ts +21 -4
  41. package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
  42. package/src/cognition/faculties/circadian.oscillator.ts +36 -7
  43. package/src/cognition/faculties/episodic.consolidator.ts +59 -3
  44. package/src/cognition/faculties/executive.engine/action.record.ts +129 -0
  45. package/src/cognition/faculties/executive.engine/commands.ts +189 -14
  46. package/src/cognition/faculties/executive.engine/context.ts +106 -46
  47. package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
  48. package/src/cognition/faculties/executive.engine/engine.ts +714 -136
  49. package/src/cognition/faculties/executive.engine/escalation.buffer.ts +230 -45
  50. package/src/cognition/faculties/executive.engine/facet.supervisor.ts +320 -65
  51. package/src/cognition/faculties/executive.engine/facet.ts +81 -26
  52. package/src/cognition/faculties/executive.engine/gating.ts +14 -14
  53. package/src/cognition/faculties/executive.engine/parser.ts +120 -38
  54. package/src/cognition/faculties/executive.engine/prompt.factory.ts +331 -38
  55. package/src/cognition/faculties/executive.engine/types.ts +84 -12
  56. package/src/cognition/faculties/exteroception.ts +99 -98
  57. package/src/cognition/faculties/goal.manager.ts +144 -16
  58. package/src/cognition/faculties/known.entity.tracker.ts +287 -29
  59. package/src/cognition/faculties/moral.evaluator.ts +8 -3
  60. package/src/cognition/faculties/persona.consolidator.ts +141 -0
  61. package/src/cognition/faculties/planning.engine/engine.ts +4 -4
  62. package/src/cognition/faculties/planning.engine/plan.frontier.ts +1 -1
  63. package/src/cognition/faculties/reputation.tracker.ts +66 -2
  64. package/src/cognition/faculties/self.model.updater.ts +19 -12
  65. package/src/cognition/faculties/semantic.engine/integrator.ts +1 -1
  66. package/src/cognition/faculties/social.perception.ts +47 -3
  67. package/src/cognition/faculties/threat.evaluator.ts +7 -0
  68. package/src/cognition/faculties/working.memory.ts +16 -21
  69. package/src/cognition/identity.entity.ts +205 -0
  70. package/src/cognition/index.ts +7 -0
  71. package/src/cognition/instruction.handler.ts +1 -1
  72. package/src/cognition/memory/vector.adapter.ts +12 -3
  73. package/src/cognition/memory/vector.embedder.ts +45 -4
  74. package/src/cognition/percept.entity.ts +122 -0
  75. package/src/cognition/persona.prior.ts +6 -0
  76. package/src/cognition/sense.boundary.ts +176 -0
  77. package/src/cognition/senses/audition.engine/engine.ts +492 -53
  78. package/src/cognition/senses/base.sense.engine.ts +105 -7
  79. package/src/cognition/senses/index.ts +83 -13
  80. package/src/cognition/senses/provenance.ts +128 -0
  81. package/src/cognition/senses/somatosensation.engine.ts +117 -9
  82. package/src/cognition/social.identity.ts +296 -0
  83. package/src/cognition/utilities/token.tracker.ts +94 -10
  84. package/src/core/orchestrator.ts +51 -0
  85. package/src/index.ts +39 -5
  86. package/src/llm/gate.ts +48 -0
  87. package/src/llm/index.ts +127 -33
  88. package/src/llm/routing.ts +6 -0
  89. package/src/llm/summarizer.ts +1 -1
  90. package/src/llm/wire.contracts.ts +57 -0
  91. package/src/pma/index.ts +68 -54
  92. package/src/stem/assembly.audit.ts +1 -0
  93. package/src/stem/guards/identity.coherence.ts +1 -1
  94. package/src/stem/index.ts +171 -23
  95. package/src/stem/mind.ts +198 -57
  96. package/src/stem/policy/arbiter.ts +10 -0
  97. package/src/stem/profiles/built-in.ts +7 -0
  98. package/src/{profiles → stem/profiles}/companion.ts +1 -1
  99. package/src/{profiles → stem/profiles}/company-brain.ts +1 -1
  100. package/src/{profiles → stem/profiles}/customer-service.ts +1 -1
  101. package/src/{profiles → stem/profiles}/game-npc.ts +1 -1
  102. package/src/{profiles → stem/profiles}/index.ts +1 -1
  103. package/src/{profiles → stem/profiles}/smart-home.ts +1 -1
  104. package/src/stem/tracts/effector/escalation.lifecycle.ts +239 -0
  105. package/src/stem/tracts/effector/policy.enforcement.ts +261 -0
  106. package/src/stem/tracts/effector/types.ts +110 -0
  107. package/src/stem/tracts/effector.controller.ts +112 -401
  108. package/src/stem/tracts/outbox.controller.ts +30 -17
  109. package/src/stem/tracts/outbox.writer.ts +40 -2
  110. package/src/stem/tracts/sensory.controller.ts +6 -6
  111. package/src/stem/tracts/session.logger.ts +6 -1
  112. package/src/stem/tracts/transport/types.ts +20 -1
  113. package/src/stem/tracts/transport.controller.ts +33 -9
  114. package/src/surface/channels/discord.ts +609 -0
  115. package/src/{channels → surface/channels}/roster.ts +1 -1
  116. package/src/surface/channels/types.ts +136 -0
  117. package/src/{channels → surface/channels}/whatsapp.ts +22 -10
  118. package/src/{cli.ts → surface/cli.ts} +14 -9
  119. package/src/{host → surface/host}/boot.ts +3 -3
  120. package/src/{host → surface/host}/utterances.ts +2 -2
  121. package/src/{mcp → surface/mcp}/effectors.ts +19 -6
  122. package/src/{mcp → surface/mcp}/server.ts +19 -5
  123. package/src/{sdk → surface/sdk}/will.ts +194 -33
  124. package/src/{serve → surface/serve}/server.ts +11 -4
  125. package/src/types.ts +24 -3
  126. package/dist/channels/discord.d.ts +0 -69
  127. package/dist/channels/discord.js +0 -193
  128. package/dist/channels/discord.js.map +0 -1
  129. package/dist/channels/whatsapp.js.map +0 -1
  130. package/dist/cli.js.map +0 -1
  131. package/dist/mcp/effectors.js.map +0 -1
  132. package/src/channels/discord.ts +0 -214
  133. package/src/channels/types.ts +0 -46
  134. package/src/cognition/faculties/executive.engine/messages.ts +0 -102
  135. package/src/extensions/livestream.ext.ts +0 -570
  136. package/src/extensions/time.ext.ts +0 -339
  137. package/src/profiles/built-in.ts +0 -7
  138. package/src/runners/coherence.runner.ts +0 -49
  139. package/src/runners/outreach.runner.ts +0 -155
  140. package/src/runners/social.runner.ts +0 -179
  141. package/src/runners/thin-shim.runner.ts +0 -205
  142. /package/dist/{cli.d.ts → surface/cli.d.ts} +0 -0
@@ -2,35 +2,124 @@
2
2
  // src/cognition/faculties/executive.engine/escalation.buffer.ts
3
3
  // ─────────────────────────────────────────────────────────────
4
4
  //
5
- // EscalationBuffer — pending auditionexecutive escalations (R5-g-2).
5
+ // EscalationBuffer — facetmaster handoffs awaiting a tick boundary (R5-g-2).
6
6
  //
7
- // The AuditionEngine publishes `audition.task.signal` events when a facet
8
- // conversation surfaces work the master should own. Those events arrive
9
- // between tick boundaries, while state is read-only, so they can't be written
10
- // as percepts directly. This buffer holds them until the next
11
- // onReasoningComplete(), where they are drained into high-salience percept
12
- // entities (StateCommands.set) so Exteroception surfaces them as
13
- // "## Percepts (What I Notice)" on the following master cycle.
7
+ // A focused facet sometimes surfaces something only the singular seat can own:
8
+ // work to plan, or an intention toward someone it is not itself talking to. It
9
+ // publishes `executive.facet.handoff`; those events arrive between tick
10
+ // boundaries, while state is read-only, so they can't be written as percepts
11
+ // directly. This buffer holds them until the next onReasoningComplete(), where
12
+ // they are drained into high-salience percept entities (StateCommands.set) so
13
+ // Exteroception surfaces them as "## Percepts (What I Notice)" on the following
14
+ // master cycle.
14
15
  //
15
- // The master reads these as environmental signals — NEVER as incoming
16
- // messages — and responds by creating plans/goals, never by emitting [REPLY];
17
- // the facet handles the actual communication.
16
+ // The master reads these as things IT noticed about its own situation — NEVER
17
+ // as incoming messages — and responds by creating plans/goals or by deciding
18
+ // whether it still means to make a contact. It never replies; the facet that
19
+ // raised the handoff owns the talking.
18
20
  //
19
- // Extracted verbatim from ExecutiveEngine (R5-g-2) as a pure collaborator.
20
- // The salience-spike that wakes the master after a push stays in the engine
21
- // (it touches gating state, not this buffer).
21
+ // ── Why the topic is not `audition.task.signal` ──────────────
22
+ //
23
+ // It used to be, and the payload was conversation-shaped: `{ entityId, threadId,
24
+ // … }`. That made the master's handoff channel the private property of ONE sense
25
+ // engine. A planning facet, a supervision facet, a deliberation facet — none of
26
+ // them had any way to hand anything to the master at all, and every facet type
27
+ // added afterwards would have had to mint its own topic and its own handler
28
+ // beside `_onAuditionTaskSignal`. The master does not need to know which faculty
29
+ // raised a handoff; it needs to know that a focused part of it formed something
30
+ // its singular seat must own. One topic, one handler, a discriminated `kind`.
22
31
  // ─────────────────────────────────────────────────────────────
23
32
 
24
33
  import type { EntityInput } from '#core/types'
34
+ import { perceptEntity } from '#cognition/percept.entity'
35
+
36
+ /**
37
+ * Work the master should plan or re-goal. The facet has already said whatever
38
+ * needed saying to the person in front of it; this is the cognitive remainder.
39
+ */
40
+ export interface EscalationHandoff {
41
+ kind: 'escalation'
42
+ reasoning: string
43
+ }
25
44
 
26
- export interface PendingEscalation {
27
- entityId: string
28
- threadId: string
45
+ /**
46
+ * SOMETHING I SAID I WOULD DO, declared by the part of me that said it.
47
+ *
48
+ * It was `{ target, gist }` — a contact and nothing else — because the defect it
49
+ * was built for was capability-shaped rather than promise-shaped: a conversation
50
+ * facet can speak in its own thread but cannot open a channel to a third party,
51
+ * so the handoff carried the one act it structurally could not perform. Named
52
+ * "undertaking" and dressed in promise language, it read as a general commitment
53
+ * system with a strange restriction, and it was not one.
54
+ *
55
+ * `what` is the commitment in the mind's own words — "reach FKEM about the demo",
56
+ * "have the scoping doc by Friday", "look into the pricing". `target` is set only
57
+ * when it is toward someone the mind would have to REACH, which is still the case
58
+ * the tract exists for; a promise about work has no target and needs none.
59
+ *
60
+ * Note what this deliberately does NOT become: a task list. It is filed beside
61
+ * what the facet concluded and read once by the master, which may make a goal of
62
+ * it — the conversation format already says to raise [GOALS_NEW] for anything to
63
+ * follow through on, and goals are the thing that persists. This only ensures the
64
+ * seat that decides never learns of it too late to decide.
65
+ */
66
+ export interface UndertakingHandoff {
67
+ kind: 'undertaking'
68
+ /** What I said I would do, in my own words. */
69
+ what: string
70
+ /** Who it is toward — only when keeping it means reaching someone. */
71
+ target?: string
72
+ /** The words I had in mind for them, when I had any. */
73
+ gist?: string
29
74
  reasoning: string
30
- tick: number
31
75
  }
32
76
 
33
- /** Requester context captured from the first buffered escalation, used to tag new goals. */
77
+ /** Everything a facet can hand up. Add a variant here, not a new topic. */
78
+ export type HandoffBody = EscalationHandoff | UndertakingHandoff
79
+
80
+ /**
81
+ * Schema validator for `executive.facet.handoff` — every facet type publishes
82
+ * through it, so the shape is checked at the bus rather than trusted per-caller.
83
+ * Returns null when valid, else the reason.
84
+ */
85
+ export function validateFacetHandoff( payload: unknown ): string | null {
86
+ if( !payload || typeof payload !== 'object') return 'payload must be object'
87
+ const body = ( payload as { body?: unknown } ).body
88
+ if( !body || typeof body !== 'object') return 'payload.body is required'
89
+ const kind = ( body as { kind?: unknown } ).kind
90
+ if( kind === 'escalation') return null
91
+ if( kind === 'undertaking'){
92
+ // `what` is the requirement now, not `target`. A promise about work has no
93
+ // one to reach and is still a promise; a promise with a target but nothing
94
+ // said is not one.
95
+ const what = ( body as { what?: unknown } ).what
96
+ if( typeof what !== 'string' || what.trim().length === 0 )
97
+ return 'undertaking handoff requires a non-empty what'
98
+ const target = ( body as { target?: unknown } ).target
99
+ return target === undefined || ( typeof target === 'string' && target.trim().length > 0 )
100
+ ? null
101
+ : 'undertaking handoff target, when present, must be a non-empty string'
102
+ }
103
+ return `unknown handoff kind: ${String( kind )}`
104
+ }
105
+
106
+ export interface PendingHandoff {
107
+ /** Which facet raised it — for tracing a percept back to the thread that made it. */
108
+ facetId?: string
109
+ /**
110
+ * Who the facet was attending to, when it was attending to a someone. Absent
111
+ * for facets whose focus has no person in it (planning, deliberation) — which
112
+ * is exactly the case the old conversation-shaped payload could not express.
113
+ */
114
+ subjectEntityId?: string
115
+ subjectName?: string
116
+ threadId?: string
117
+ /** Sim tick at which the facet formed this. Undertaking discharge keys on it. */
118
+ tick: number
119
+ body: HandoffBody
120
+ }
121
+
122
+ /** Requester context captured from the first buffered handoff, used to tag new goals. */
34
123
  export interface EscalationRequester {
35
124
  entityId: string
36
125
  threadId: string
@@ -39,49 +128,145 @@ export interface EscalationRequester {
39
128
  export interface DrainedEscalations {
40
129
  /** High-salience percept entities to merge into StateCommands.set. */
41
130
  percepts: EntityInput[]
42
- /** First escalation's requester context, or undefined when the buffer was empty. */
131
+ /** First handoff's requester context, or undefined when none carried one. */
43
132
  requester?: EscalationRequester
44
133
  }
45
134
 
135
+ /** "in my conversation with X" when there was one, "while I was working" otherwise. */
136
+ function whereItHappened( h: PendingHandoff ): string {
137
+ const who = h.subjectName ?? h.subjectEntityId
138
+ return who ? `In my conversation with ${who}` : 'While I was working on something else'
139
+ }
140
+
46
141
  export class EscalationBuffer {
47
- private _pending: PendingEscalation[] = []
142
+ private _pending: PendingHandoff[] = []
48
143
 
49
- /** Buffer one escalation for injection on the next master cycle. */
50
- push( escalation: PendingEscalation ): void {
51
- this._pending.push( escalation )
144
+ /** Buffer one handoff for injection on the next master cycle. */
145
+ push( handoff: PendingHandoff ): void {
146
+ this._pending.push( handoff )
52
147
  }
53
148
 
54
149
  get size(): number { return this._pending.length }
55
150
  get isEmpty(): boolean { return this._pending.length === 0 }
56
151
 
57
152
  /**
58
- * Convert every buffered escalation into a high-salience percept entity and
59
- * clear the buffer. Returns the percepts plus the first escalation's
60
- * requester context (used to tag goals the master creates in response).
153
+ * Convert every buffered handoff into a high-salience percept entity and clear
154
+ * the buffer. Returns the percepts plus the first handoff's requester context
155
+ * (used to tag goals the master creates in response).
156
+ *
157
+ * WHY THESE DO NOT GO THROUGH A SENSE DOOR (SIGNAL_BOUNDARY P4).
158
+ *
159
+ * P4's rule is that only a sense or exteroception writes a percept, and these
160
+ * two are the standing exception. A sense door carries AFFERENCE: a signal
161
+ * crossing into the mind, either from the world (exafferent) or from the
162
+ * mind's own act returning THROUGH the world (reafferent). A facet handing off
163
+ * to the master crosses neither boundary — it never left the mind. Routing it
164
+ * through a sense would dress one part of a mind up as news from outside,
165
+ * which is precisely the laundering P1 removed from `inspect`, and §6 of the
166
+ * epoch names that class of over-unification as the thing not to do.
167
+ *
168
+ * They still had to stop being hand-rolled literals, and they have: everything
169
+ * goes through `perceptEntity()`, so the shape is the one shape.
170
+ *
171
+ * What is left open, and worth deciding rather than inheriting: a handoff is
172
+ * using the percept entity as a DELIVERY MECHANISM, because the percept block
173
+ * is what the executive prompt renders. A `self.handoff` type with its own
174
+ * section would say what this actually is. That is a prompt change and a
175
+ * `_reconcileUndertakings` change, so it is a decision, not a cleanup.
61
176
  */
62
177
  drainToPercepts(): DrainedEscalations {
63
178
  const percepts: EntityInput[] = []
64
- for( const esc of this._pending ){
65
- percepts.push({
66
- id: `escalation-percept-${esc.entityId}-${esc.tick}`,
67
- type: 'percept',
68
- metadata: {
69
- category: 'task-escalation',
70
- summary: `[Task from conversation with ${esc.entityId}] ${esc.reasoning}`,
71
- salience: 0.85,
72
- source: 'audition-facet',
73
- entityId: esc.entityId,
74
- threadId: esc.threadId,
75
- // Guides the master's response: plan, don't reply
76
- directive: 'Create a plan or update goals. Do not emit [REPLY] — the facet handles communication.',
77
- }
78
- })
179
+ let seq = 0
180
+ for( const h of this._pending ){
181
+ // Distinct ids per drain: two handoffs from one thread on one tick are two
182
+ // separate things the mind noticed, and keying on (subject, tick) alone would
183
+ // silently collapse them into whichever came last.
184
+ const id = `escalation-percept-${h.subjectEntityId ?? h.facetId ?? 'self'}-${h.tick}-${seq++}`
185
+
186
+ // FACTS the builder owns, and the writer's OWN fields beside them. Split
187
+ // this way because `perceptEntity()` spreads `extra` first: a writer can
188
+ // add whatever it likes and still cannot clobber `tick` or `provenance`.
189
+ const core = {
190
+ id,
191
+ tick: h.tick,
192
+ salience: 0.85,
193
+ // REAFFERENT. Nothing outside the mind produced this: a facet of it
194
+ // raised the handoff, and the master is now noticing its own part's
195
+ // doing. Tagging it costs no behaviour — the rupture gate excludes
196
+ // reafferent and untagged alike — but it stops the exclusion being an
197
+ // accident, and it is the honest answer rather than the convenient one.
198
+ provenance: 'reafferent' as const,
199
+ ...( h.subjectEntityId ? { entityId: h.subjectEntityId } : {} ),
200
+ }
201
+ const mine = {
202
+ source: 'executive-facet',
203
+ ...( h.threadId ? { threadId: h.threadId } : {} ),
204
+ ...( h.facetId ? { facetId: h.facetId } : {} ),
205
+ }
206
+
207
+ // Built through `perceptEntity()` like every other percept in the package
208
+ // (SIGNAL_BOUNDARY P4). These were the last hand-rolled `type: 'percept'`
209
+ // literals, and a hand-rolled one is exactly how `tick` went missing on
210
+ // the wake and delivery percepts and made them immortal.
211
+ //
212
+ // They do NOT go through a sense door, and should not — see the note on
213
+ // `drainToPercepts` above for why.
214
+ percepts.push( h.body.kind === 'undertaking'
215
+ ? perceptEntity( {
216
+ ...core,
217
+ category: 'undertaking',
218
+ // THE GAP, AND ONLY THE GAP. First person because the mind is noticing
219
+ // what IT said it would do, not because it is being told what to do
220
+ // about it. A mind that has said it will make contact remembers
221
+ // deciding and cannot tell from the inside whether the words went out
222
+ // — so it follows up on a message it never sent. Naming the gap is
223
+ // what lets it check; naming the remedy was never this line's job.
224
+ //
225
+ // What used to follow this sentence: "If I still mean it, I reach out
226
+ // with target 'X'. If I no longer do, I let it go — but I do not leave
227
+ // it half-done while telling them it is handled." An instruction
228
+ // naming an effector and its argument, and a value judgement, both
229
+ // written in the mind's own voice so it could not disagree with
230
+ // either. The pull now lives in the affordance field where it can be
231
+ // out-competed, and the judgement belongs to a persona, not a buffer.
232
+ summary: `${whereItHappened( h )} I said I would reach ${h.body.target}`
233
+ + `. Nothing has gone to them since; saying it in that conversation did not send it.`,
234
+ }, {
235
+ ...mine,
236
+ // Who was promised, and when the promise was made. The executive
237
+ // reconciles against these (see _reconcileUndertakings): once the mind
238
+ // has actually reached this target since `tick`, the percept is retired
239
+ // rather than left standing as a claim the words are still unsent.
240
+ undertakingTarget: h.body.target,
241
+ // The EVIDENCE, rendered beneath the label since P2 — which is what
242
+ // makes the prose above unnecessary. `gist` is what the mind meant to
243
+ // say; it is a fact about the promise, not a script for keeping it,
244
+ // and it is carried whole rather than clipped into a sentence.
245
+ data: {
246
+ target: h.body.target,
247
+ promisedAt: h.tick,
248
+ ...( h.subjectName ?? h.subjectEntityId ? { promisedTo: h.subjectName ?? h.subjectEntityId } : {} ),
249
+ ...( h.body.gist ? { gist: h.body.gist } : {} ),
250
+ contactedSince: false,
251
+ },
252
+ } )
253
+ : perceptEntity( {
254
+ ...core,
255
+ category: 'task-escalation',
256
+ // What was raised, and by which part of me. It used to close with
257
+ // "this is mine to plan or re-goal; the part of me that raised it
258
+ // handles the talking" — a role instruction. Whether to plan it,
259
+ // re-goal it or drop it is the master's call, and it has a whole
260
+ // faculty for making it.
261
+ summary: `[Raised by ${h.subjectName ?? h.subjectEntityId ?? 'my own focused work'}] ${h.body.reasoning}`,
262
+ }, mine ) )
79
263
  }
80
264
 
81
- // Capture requester context before clearing — used to tag new goals.
82
- const first = this._pending[0]
265
+ // Capture requester context before clearing — used to tag new goals. Only a
266
+ // handoff that came from a conversation has one.
267
+ const first = this._pending.find( h => h.subjectEntityId )
83
268
  const requester: EscalationRequester | undefined =
84
- first ? { entityId: first.entityId, threadId: first.threadId } : undefined
269
+ first ? { entityId: first.subjectEntityId!, threadId: first.threadId ?? '' } : undefined
85
270
 
86
271
  this._pending = []
87
272