@mindot/will 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -22
- package/dist/channels/discord.d.ts +67 -6
- package/dist/channels/discord.js +112 -6
- package/dist/channels/discord.js.map +1 -1
- package/dist/channels/whatsapp.d.ts +1 -1
- package/dist/channels/whatsapp.js +4 -1
- package/dist/channels/whatsapp.js.map +1 -1
- package/dist/cli.js +13733 -11057
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3729 -1199
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/{will-DAW0l-lY.d.ts → will-DbDj_TEH.d.ts} +1214 -93
- package/package.json +1 -1
- package/src/channels/discord.ts +189 -11
- package/src/channels/types.ts +90 -0
- package/src/channels/whatsapp.ts +13 -4
- package/src/cli.ts +9 -4
- package/src/cognition/agency/consequence.ts +122 -1
- package/src/cognition/agency/conversation.aim.ts +260 -0
- package/src/cognition/agency/engines/action.selector.ts +85 -3
- package/src/cognition/agency/engines/affordance.synthesizer.ts +90 -1
- package/src/cognition/agency/engines/motor.schema.executor.ts +152 -10
- package/src/cognition/agency/engines/reafference.engine.ts +129 -2
- package/src/cognition/agency/proactive.communicator.ts +19 -3
- package/src/cognition/agency/reconcile.learning.ts +16 -2
- package/src/cognition/agency/restart.ts +66 -0
- package/src/cognition/agency/schemas/repertoire.ts +12 -5
- package/src/cognition/agency/selection.scoring.ts +33 -0
- package/src/cognition/agency/types.ts +35 -0
- package/src/cognition/cache/composition.ts +232 -0
- package/src/cognition/cache/deliberation.cache.ts +219 -0
- package/src/cognition/cache/fingerprint.ts +120 -0
- package/src/cognition/cache/types.ts +105 -0
- package/src/cognition/config.mirror.entities.ts +109 -1
- package/src/cognition/event.schemas.ts +22 -0
- package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
- package/src/cognition/faculties/episodic.consolidator.ts +59 -3
- package/src/cognition/faculties/executive.engine/commands.ts +189 -14
- package/src/cognition/faculties/executive.engine/context.ts +67 -13
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
- package/src/cognition/faculties/executive.engine/engine.ts +686 -187
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +162 -44
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +310 -65
- package/src/cognition/faculties/executive.engine/facet.ts +89 -26
- package/src/cognition/faculties/executive.engine/gating.ts +14 -14
- package/src/cognition/faculties/executive.engine/parser.ts +21 -1
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +169 -20
- package/src/cognition/faculties/executive.engine/types.ts +69 -0
- package/src/cognition/faculties/goal.manager.ts +94 -14
- package/src/cognition/faculties/known.entity.tracker.ts +267 -28
- package/src/cognition/faculties/moral.evaluator.ts +8 -3
- package/src/cognition/faculties/persona.consolidator.ts +141 -0
- package/src/cognition/faculties/reputation.tracker.ts +66 -2
- package/src/cognition/faculties/self.model.updater.ts +19 -12
- package/src/cognition/faculties/social.perception.ts +47 -3
- package/src/cognition/faculties/threat.evaluator.ts +7 -0
- package/src/cognition/faculties/working.memory.ts +10 -20
- package/src/cognition/identity.entity.ts +205 -0
- package/src/cognition/index.ts +11 -0
- package/src/cognition/memory/vector.adapter.ts +12 -3
- package/src/cognition/memory/vector.embedder.ts +54 -7
- package/src/cognition/persona.prior.ts +6 -0
- package/src/cognition/senses/audition.engine/engine.ts +404 -46
- package/src/cognition/senses/base.sense.engine.ts +1 -1
- package/src/cognition/senses/index.ts +12 -0
- package/src/cognition/social.identity.ts +273 -0
- package/src/cognition/utilities/token.tracker.ts +246 -98
- package/src/core/orchestrator.ts +38 -0
- package/src/host/boot.ts +78 -22
- package/src/index.ts +35 -0
- package/src/llm/index.ts +415 -97
- package/src/llm/routing.ts +204 -0
- package/src/llm/summarizer.ts +5 -1
- package/src/llm/wire.contracts.ts +19 -0
- package/src/pma/index.ts +67 -53
- package/src/runners/thin-shim.runner.ts +18 -6
- package/src/sdk/will.ts +121 -22
- package/src/stem/assembly.audit.ts +1 -0
- package/src/stem/guards/identity.coherence.ts +17 -6
- package/src/stem/index.ts +82 -5
- package/src/stem/mind.ts +327 -79
- package/src/stem/policy/arbiter.ts +49 -14
- package/src/stem/policy/rule.table.ts +2 -2
- package/src/stem/tracts/effector.controller.ts +56 -9
- package/src/stem/tracts/outbox.writer.ts +40 -2
- package/src/cognition/faculties/executive.engine/messages.ts +0 -102
|
@@ -45,9 +45,21 @@
|
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
47
|
import type { ReadonlySimulationState } from '#core/types'
|
|
48
|
+
import type { LLMCallFunction } from '#cognition/utilities/token.tracker'
|
|
48
49
|
import type { ExecutiveSummarizer } from '#llm/summarizer'
|
|
49
|
-
import type { ExecutiveContext,
|
|
50
|
+
import type { ExecutiveContext, IdeationCandidate } from '#faculties/executive.engine/types'
|
|
50
51
|
import { buildExecutiveContext, type ContextDependencies } from '#faculties/executive.engine/context'
|
|
52
|
+
import { INNATE_SCHEMAS } from '#agency/schemas/innate'
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The stances a mind always has, named so it need not guess at them.
|
|
56
|
+
*
|
|
57
|
+
* Static, so it costs nothing in prompt-cache stability. Without it a Will can
|
|
58
|
+
* only learn action names from whichever affordances win the salience
|
|
59
|
+
* competition into its percepts — and it invents plausible ones for whatever it
|
|
60
|
+
* cannot see (`query`, `message`), which resolve to nothing.
|
|
61
|
+
*/
|
|
62
|
+
const INNATE_ACTION_NAMES = INNATE_SCHEMAS.map( s => s.id ).sort().join(', ')
|
|
51
63
|
|
|
52
64
|
// ── Re-export for callers that imported the old alias ────────
|
|
53
65
|
export type { ContextDependencies as ContextDependenciesForFresh } from '#faculties/executive.engine/context'
|
|
@@ -158,7 +170,7 @@ export interface FocusSection {
|
|
|
158
170
|
* into the facet's LLM calls as `LLMCallMeta.function` so the TokenTracker can
|
|
159
171
|
* break spend down per facet type. Defaults to 'facet' when unset.
|
|
160
172
|
*/
|
|
161
|
-
function?:
|
|
173
|
+
function?: LLMCallFunction
|
|
162
174
|
/**
|
|
163
175
|
* Optional: Custom output format to append instead of the standard executive format.
|
|
164
176
|
* Pass via PromptBuildOptions.outputFormat when building the user message.
|
|
@@ -189,6 +201,18 @@ export interface FocusSection {
|
|
|
189
201
|
* only sees that person's plans. When unset, those sections show all.
|
|
190
202
|
*/
|
|
191
203
|
awarenessEntityId?: string
|
|
204
|
+
/**
|
|
205
|
+
* Optional: WHO this facet is engaged with — the keid and the name the mind has
|
|
206
|
+
* learned for them. Reported back to the master on every `executive.facet.sync`.
|
|
207
|
+
*
|
|
208
|
+
* Without it the master was told, in its own system prompt, that "focused facets
|
|
209
|
+
* may run simultaneously… their reasoning syncs back to me" while the sync payload
|
|
210
|
+
* carried only a facetId and a confidence number — so a mind holding two live
|
|
211
|
+
* conversations could not tell you whose they were. The master is the singular
|
|
212
|
+
* seat: it has to know who is at the table to reason about them together.
|
|
213
|
+
*/
|
|
214
|
+
subjectEntityId?: string
|
|
215
|
+
subjectName?: string
|
|
192
216
|
/**
|
|
193
217
|
* Optional: Provided by the creating engine to convert the LLM's parsed output
|
|
194
218
|
* into a domain-specific decision payload.
|
|
@@ -207,7 +231,6 @@ export interface PromptBuildOptions {
|
|
|
207
231
|
state: ReadonlySimulationState
|
|
208
232
|
qualityModulation: number
|
|
209
233
|
epistemicUncertainty: number
|
|
210
|
-
pendingMessages?: PendingMessage[]
|
|
211
234
|
focus: FocusSection
|
|
212
235
|
deps: PromptDependencies
|
|
213
236
|
/** Optional: Recent action types for diversity tracking */
|
|
@@ -239,6 +262,15 @@ export interface PromptBuildOptions {
|
|
|
239
262
|
* fast path. See PromptFactory.buildIdeationFormatInstruction().
|
|
240
263
|
*/
|
|
241
264
|
ideationCandidates?: IdeationCandidate[]
|
|
265
|
+
/**
|
|
266
|
+
* Master mode only — who the mind is in conversation with RIGHT NOW, from the
|
|
267
|
+
* live facets' `executive.facet.sync` reports. The master does not run those
|
|
268
|
+
* conversations, but it is the one seat that sees all of them, and it decides
|
|
269
|
+
* whom to contact; deciding that without knowing who is already mid-thread is
|
|
270
|
+
* how one mind ends up opening a second conversation with someone it is already
|
|
271
|
+
* talking to — or telling one person it has contacted another when it has not.
|
|
272
|
+
*/
|
|
273
|
+
activeConversations?: { entityId: string; name?: string; sinceTick: number }[]
|
|
242
274
|
}
|
|
243
275
|
|
|
244
276
|
// ── PromptFactory ────────────────────────────────────────────
|
|
@@ -324,17 +356,36 @@ export class PromptFactory {
|
|
|
324
356
|
`**Communication style:** ${identity.style}`,
|
|
325
357
|
].filter( Boolean ).join('\n')
|
|
326
358
|
|
|
327
|
-
// Mode-aware role description
|
|
328
|
-
//
|
|
359
|
+
// Mode-aware role description.
|
|
360
|
+
//
|
|
361
|
+
// MASTER states the architecture, because the master genuinely IS the seat
|
|
362
|
+
// that produces every cognitive output at once and needs to know that.
|
|
363
|
+
//
|
|
364
|
+
// A FACET is told none of it. Master/facet is how the CONTAINER divides
|
|
365
|
+
// attention; it is not a fact about the person renting it, and describing a
|
|
366
|
+
// facet as a subordinate instance of a separate consciousness is both untrue
|
|
367
|
+
// and expensive. Measured in production: a facet given the old text built,
|
|
368
|
+
// in its own words, "an entire operational stance around being subordinate —
|
|
369
|
+
// waiting for direction, asking permission, addressing messages to 'Will' as
|
|
370
|
+
// if he were someone else" — and, having been handed a second party it could
|
|
371
|
+
// address, emitted messages TO that party through the outbound channel, where
|
|
372
|
+
// its operator read them. There is one person here, attending to one thing.
|
|
373
|
+
//
|
|
374
|
+
// It is also told nothing about WHO it is. A facet is built from the same
|
|
375
|
+
// context as the master and its persona is already above, in full — so a
|
|
376
|
+
// facet-only "I am <name>" is a second, redundant statement of identity, and a
|
|
377
|
+
// second place for the name to disagree with itself. Which is precisely the
|
|
378
|
+
// failure mode being repaired: the name is stated ONCE, from one source.
|
|
329
379
|
const roleDescription = isMaster
|
|
330
|
-
? `I am the unified cognitive core of ${identity.name} — a self-aware synthetic mind.\nI receive my complete internal state and produce ALL cognitive outputs in one response:\ndecisions, plans, beliefs, introspections, narrative updates, and self-observations.`
|
|
331
|
-
: `
|
|
380
|
+
? `I am the unified cognitive core of ${identity.name || 'this mind'} — a self-aware synthetic mind.\nI receive my complete internal state and produce ALL cognitive outputs in one response:\ndecisions, plans, beliefs, introspections, narrative updates, and self-observations.`
|
|
381
|
+
: `Right now my whole attention is on: **${focus.title}**.`
|
|
332
382
|
|
|
333
|
-
//
|
|
334
|
-
//
|
|
383
|
+
// The architecture note is MASTER-ONLY, for the same reason. It grounds the
|
|
384
|
+
// seat that actually coordinates; a facet reading it learns only that it is
|
|
385
|
+
// not the real one.
|
|
335
386
|
const consciousnessArchitecture = isMaster
|
|
336
387
|
? `I am the default reasoning mode. Focused facets may run simultaneously, each\nconcentrating on specific tasks. Their reasoning syncs back to me.\nI maintain my unified identity across all cycles.`
|
|
337
|
-
:
|
|
388
|
+
: ''
|
|
338
389
|
|
|
339
390
|
|
|
340
391
|
// Strip any existing "## Who I Am" section from identity.prompt to prevent
|
|
@@ -347,32 +398,36 @@ export class PromptFactory {
|
|
|
347
398
|
.replace( /^##\s*Who (?:I Am|You Are)[^\n]*\n?/m, '')
|
|
348
399
|
.trim()
|
|
349
400
|
|
|
401
|
+
// `## Consciousness Architecture` is emitted only when there is architecture
|
|
402
|
+
// to state — i.e. master. A facet gets no empty header (an empty section under
|
|
403
|
+
// a heading reads as a section the mind failed to fill in).
|
|
404
|
+
const architectureBlock = consciousnessArchitecture
|
|
405
|
+
? `\n\n## Consciousness Architecture\n${consciousnessArchitecture}`
|
|
406
|
+
: ''
|
|
407
|
+
|
|
350
408
|
return `${cleanIdentityPrompt}
|
|
351
409
|
|
|
352
410
|
## Personality
|
|
353
411
|
${identityBlock}
|
|
354
412
|
|
|
355
413
|
## My Role
|
|
356
|
-
${roleDescription}
|
|
357
|
-
|
|
358
|
-
## Consciousness Architecture
|
|
359
|
-
${consciousnessArchitecture}
|
|
414
|
+
${roleDescription}${architectureBlock}
|
|
360
415
|
|
|
361
416
|
## Output Guidelines
|
|
362
|
-
- **actions**:
|
|
417
|
+
- **actions**: What I intend to do. I express intent — my body finds the fit. My own stances are always with me (listed with the output schema below); *acquired* abilities, if any, appear under "## Abilities Available Now", and when there is no such section I have none of those — so a thing I want done that needs one is a thing to say I cannot do, not to attempt. When enacting a named ability that needs specifics (a query, a message, a value), put them in the action's "args" object and my body enacts it with exactly those args.
|
|
363
418
|
- **plans**: Include for goals without existing plans or where plans need revision. I may keep multiple plans per goal — set **planId** to act on a specific existing plan (validate/execute/revise/cancel); omit it to draft a new one. My current plans are listed under "## Active Plans".
|
|
364
419
|
- **newBeliefs**: Extract patterns from experiences visible in my current state. Only record a belief if I can point to a specific observation that supports it — do not infer experiences I have no record of. Set 'evidence' honestly: 'single_observation' (first time noticing), 'recurring_pattern' (seen multiple times), 'strong_pattern' (deeply established).
|
|
365
|
-
- **introspection**: Include when significant events occurred or I notice patterns. When I spot a cognitive bias in my own reasoning, name it in 'identifiedBiases' using its common term where one fits (e.g. overgeneralization, confirmation bias, recency bias) — this lets my self-assessment line up with the patterns my faculties detect on their own.
|
|
420
|
+
- **introspection**: Include when significant events occurred or I notice patterns. When I spot a cognitive bias in my own reasoning, name it in 'identifiedBiases' using its common term where one fits (e.g. overgeneralization, confirmation bias, recency bias) — this lets my self-assessment line up with the patterns my faculties detect on their own. What I can introspect on is what is written above: my state, my goals, my percepts, what I did and what came of it. I have NO view of the machinery underneath — no entity ids, no salience numbers, no queue depths, no engine internals. So when I am asked why I did something, I answer from what I can actually see, and where I cannot see, I say I do not know. Naming a mechanism I have no access to is not introspection, it is invention, and it is worse than the silence it replaces: it sends whoever asked me looking for something that was never there.
|
|
366
421
|
- **narrative**: Extend my life story only from events grounded in my episodic memory or current percepts. Do not extend with invented scenarios.
|
|
367
422
|
- **newGoals/goalsToAbandon/goalsToReprioritize**: Manage my goal hierarchy.
|
|
368
423
|
- **selfObservations**: Notice patterns in my own thinking, feeling, or behavior.
|
|
369
424
|
- **identityUpdates.traits**: Array of {key, value} where value is a DELTA to apply to my trait (e.g., +0.05 to increase a trait by 5%).
|
|
370
425
|
- **identityUpdates.values**: Full list of values to set (replaces existing).
|
|
371
|
-
- **knownEntityUpdates**: What I've learned about someone/something I'm dealing with. Array of {keid, name?, learned?, feeling?}. Use the keid from "## People I Know". Set name only when I actually learn their name; learned is an array of facts about them (stored as memories); feeling is how I feel toward them (-1..1). Record only what I genuinely learned this turn.
|
|
426
|
+
- **knownEntityUpdates**: What I've learned about someone/something I'm dealing with. Array of {keid, name?, learned?, feeling?, sameAs?}. Use the keid from "## People I Know". Set name only when I actually learn their name; learned is an array of facts about them (stored as memories); feeling is how I feel toward them (-1..1). **sameAs** is another keid I have concluded is this same someone met under a different handle — it fuses my two records into one, so I use it only when I actually know, not when I merely suspect. Record only what I genuinely learned this turn.
|
|
372
427
|
|
|
373
428
|
## Required Output
|
|
374
429
|
Output a single JSON object with these fields:
|
|
375
|
-
- **actions**: Array of {type, reasoning, expectedOutcome}.
|
|
430
|
+
- **actions**: Array of {type, reasoning, expectedOutcome, target?, args?}. The stances I always have are: ${ INNATE_ACTION_NAMES } — \`reach-out\` is how I say something to someone. Anything else must be an ability named under "## Abilities Available Now". A **type** outside those two sets is not something I can do; naming one achieves nothing at all. When I reach out, **target** is who — their name or id as it appears under "## People I Know" — and the words themselves go in **args.content**. Without a person to reach, the reaching cannot happen.
|
|
376
431
|
- **reasoning**: My full reasoning. Embed optional outputs as tagged blocks here. Minimum 2–3 sentences — do not produce a one-line reasoning field.
|
|
377
432
|
- **confidence**: Number 0.0-1.0 reflecting my certainty. Be calibrated: 0.9+ only when I have strong grounding; use 0.4–0.6 when uncertain.
|
|
378
433
|
|
|
@@ -474,7 +529,11 @@ completionType guide:
|
|
|
474
529
|
|
|
475
530
|
[SELF_OBS]
|
|
476
531
|
{"selfObservations": ["I noticed that..."]}
|
|
477
|
-
[/SELF_OBS]
|
|
532
|
+
[/SELF_OBS]
|
|
533
|
+
|
|
534
|
+
[SKILLS]
|
|
535
|
+
{"newSkills": [{"id": "brief-then-confirm", "composedOf": ["reach-out", "wait"], "tags": ["social"], "cost": 0.15}]}
|
|
536
|
+
[/SKILLS]`
|
|
478
537
|
}
|
|
479
538
|
|
|
480
539
|
// ── User message ───────────────────────────────────────────
|
|
@@ -654,6 +713,13 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
654
713
|
? this._buildRecentOutcomesSection( context.recentActions, state.tick ).trim()
|
|
655
714
|
: ''
|
|
656
715
|
|
|
716
|
+
// Scoped with recentActions: both answer "what have I already done about
|
|
717
|
+
// this?", and a facet composing a message needs it at least as much as the
|
|
718
|
+
// master does — the facet is the one about to write the words again.
|
|
719
|
+
const spokenBlock = has('recentActions')
|
|
720
|
+
? this._buildSpokenTurnsSection( context.spokenTurns ).trim()
|
|
721
|
+
: ''
|
|
722
|
+
|
|
657
723
|
const perceptsBlock = has('percepts')
|
|
658
724
|
? `## Percepts (What I Notice)\n${context.percepts.slice( 0, 10 ).map( p => `- [${p.category}] ${p.summary} (salience: ${p.salience.toFixed( 2 )})`).join('\n') || 'Nothing notable'}`
|
|
659
725
|
: ''
|
|
@@ -692,10 +758,41 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
692
758
|
if( s.closeness != null && s.closeness > 0.1 ) bits.push(`closeness: ${( s.closeness * 100 ).toFixed( 0 )}%`)
|
|
693
759
|
// The Will can know *someone* without their name yet — never leak the raw keid.
|
|
694
760
|
const who = s.name ?? ( s.kind === 'thing' ? 'something' : 'someone')
|
|
695
|
-
|
|
761
|
+
|
|
762
|
+
// Where I can reach them, and how each place has gone. Stated as fact:
|
|
763
|
+
// which room to speak in is my decision, and I could not make it while
|
|
764
|
+
// the only thing anyone tracked was where they were last seen.
|
|
765
|
+
const where = ( s.handles ?? [] ).map( h => {
|
|
766
|
+
const kind = h.kind === 'dm' ? 'privately' : h.kind === 'room' ? 'in a shared room' : 'somewhere'
|
|
767
|
+
const ans = h.answeredAgo !== undefined
|
|
768
|
+
? `answered ${ h.answeredAgo } ticks ago`
|
|
769
|
+
: 'never answered me there'
|
|
770
|
+
return `${ kind } (${ h.keid }) — ${ ans }`
|
|
771
|
+
} )
|
|
772
|
+
const reach = where.length ? `\n reachable: ${ where.join('; ') }` : ''
|
|
773
|
+
|
|
774
|
+
// An identity I have not settled. Deliberately a question and not a
|
|
775
|
+
// merge: two people really can share a name, so nothing fuses them on my
|
|
776
|
+
// behalf — but I am told, so I can find out, usually by asking.
|
|
777
|
+
const doubt = s.mayBeSameAs?.length
|
|
778
|
+
? `\n I hold a separate record for ${ s.mayBeSameAs.join(' and ') } — this may be the same someone under another handle. I do not know. If I find out they are, I say so with **sameAs**.`
|
|
779
|
+
: ''
|
|
780
|
+
|
|
781
|
+
return `- ${who}${bits.length ? ' — ' + bits.join(', ') : ''}${ reach }${ doubt }`
|
|
696
782
|
} ).join('\n')}`
|
|
697
783
|
: ''
|
|
698
784
|
|
|
785
|
+
// Who the mind is mid-conversation with. Facets run those threads; this is the
|
|
786
|
+
// master's view of the table — the whole point of the singular seat is that it
|
|
787
|
+
// can hold several conversations as one situation rather than as N strangers.
|
|
788
|
+
// Names come from what the mind has actually learned; the id is shown because
|
|
789
|
+
// that is what a reach-out must be addressed to.
|
|
790
|
+
const conversationsBlock = ( options.mode !== 'facet' && options.activeConversations?.length )
|
|
791
|
+
? `## In Conversation Now\n${options.activeConversations.map( c =>
|
|
792
|
+
`- ${c.name ?? 'someone'} (id: ${c.entityId})`
|
|
793
|
+
).join('\n')}\nThese threads are already open — I am in them. Reaching out to one of these people again starts a second, parallel thread with them.`
|
|
794
|
+
: ''
|
|
795
|
+
|
|
699
796
|
// Task focus — what the Will is committed to and the felt cost of switching away.
|
|
700
797
|
// Surfaces task-persistence; the pull-to-stay scales with the (conscientiousness-
|
|
701
798
|
// developable) switch cost. Empty/absent ⇒ no block.
|
|
@@ -717,6 +814,7 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
717
814
|
plansBlock,
|
|
718
815
|
actionDiversity.trim(),
|
|
719
816
|
recentOutcomesBlock,
|
|
817
|
+
spokenBlock,
|
|
720
818
|
perceptsBlock,
|
|
721
819
|
abilitiesBlock,
|
|
722
820
|
ruminationsBlock,
|
|
@@ -724,6 +822,7 @@ Dominance: ${context.affect.dominance.toFixed( 2 )}${context.affect.blends.lengt
|
|
|
724
822
|
memoriesBlock,
|
|
725
823
|
beliefsBlock,
|
|
726
824
|
socialBlock,
|
|
825
|
+
conversationsBlock,
|
|
727
826
|
focusBlock,
|
|
728
827
|
identityNudge.trim(),
|
|
729
828
|
ideationBlock,
|
|
@@ -956,6 +1055,56 @@ ${recent.map( ( t, i ) => `${i + 1}. ${t}`).join(' → ')}${warning}
|
|
|
956
1055
|
return `## Relevant Memories\n${lines.join('\n')}${tail}`
|
|
957
1056
|
}
|
|
958
1057
|
|
|
1058
|
+
/**
|
|
1059
|
+
* What I have said to people lately, and who has answered.
|
|
1060
|
+
*
|
|
1061
|
+
* Written as a PERCEPT and nothing more. There is no instruction here not to
|
|
1062
|
+
* repeat myself, and there must not be: the mind is allowed to say a thing
|
|
1063
|
+
* twice, and a person ignored twice about something urgent should say it a
|
|
1064
|
+
* third time. What it was missing was not restraint, it was the fact — it could
|
|
1065
|
+
* not tell a first asking from an eleventh, so restraint was not something it
|
|
1066
|
+
* was in a position to exercise.
|
|
1067
|
+
*
|
|
1068
|
+
* The closing line is an epistemic caveat for the same reason: silence has many
|
|
1069
|
+
* causes and this surface distinguishes none of them. Saying "no answer yet"
|
|
1070
|
+
* without saying "and I do not know why" invites the mind to fill the gap, which
|
|
1071
|
+
* is the habit that had it inventing attention-demand ids when asked what was
|
|
1072
|
+
* wrong with it.
|
|
1073
|
+
*/
|
|
1074
|
+
private static _buildSpokenTurnsSection(
|
|
1075
|
+
spokenTurns: ExecutiveContext['spokenTurns'],
|
|
1076
|
+
): string {
|
|
1077
|
+
// Defensive on absence, not just on empty: a host (and several tests) build a
|
|
1078
|
+
// context by hand, and a missing block must render as nothing rather than
|
|
1079
|
+
// throw the whole prompt away.
|
|
1080
|
+
if( !spokenTurns?.length ) return ''
|
|
1081
|
+
|
|
1082
|
+
const clip = ( s: string, n: number ): string =>
|
|
1083
|
+
s.length > n ? `${ s.slice( 0, n ) }…` : s
|
|
1084
|
+
|
|
1085
|
+
const lines = spokenTurns.map( t => {
|
|
1086
|
+
const words = t.preview.trim()
|
|
1087
|
+
const said = words ? ` — "${ clip( words, 80 ) }"` : ''
|
|
1088
|
+
// Their words, not merely that they spoke. "they answered" on its own reads
|
|
1089
|
+
// as "I have the answer" — a live Will asked "same time, 3pm?", saw that
|
|
1090
|
+
// flag, never saw the correction to 2pm, and relayed 3pm to a third party as
|
|
1091
|
+
// confirmed. A reply I cannot see is not one I can act on.
|
|
1092
|
+
const back = t.answered
|
|
1093
|
+
? ( t.answeredWith?.trim()
|
|
1094
|
+
? ` — they answered: "${ clip( t.answeredWith.trim(), 100 ) }"`
|
|
1095
|
+
: ' — they answered (I do not have their words here)' )
|
|
1096
|
+
: ' — no answer yet'
|
|
1097
|
+
return `- **${ t.target }** · ${ t.age } ticks ago${ said }${ back }`
|
|
1098
|
+
} )
|
|
1099
|
+
|
|
1100
|
+
const open = spokenTurns.filter( t => !t.answered ).length
|
|
1101
|
+
const note = open > 0
|
|
1102
|
+
? `\n\nThese are my own words, newest first. "No answer yet" means exactly that — the words went out and nothing has come back. It does not tell me why, and I should not assume.`
|
|
1103
|
+
: ''
|
|
1104
|
+
|
|
1105
|
+
return `## What I've Said Lately\n${ lines.join('\n') }${ note }\n\n`
|
|
1106
|
+
}
|
|
1107
|
+
|
|
959
1108
|
private static _buildRecentOutcomesSection(
|
|
960
1109
|
recentActions: ExecutiveContext['recentActions'],
|
|
961
1110
|
currentTick: number,
|
|
@@ -59,6 +59,18 @@ export interface ExecutiveOutputFull {
|
|
|
59
59
|
name?: string
|
|
60
60
|
learned?: string[]
|
|
61
61
|
feeling?: number
|
|
62
|
+
/**
|
|
63
|
+
* "This is the same someone as that" — another keid I now believe is this
|
|
64
|
+
* same referent, fusing two of my records into one.
|
|
65
|
+
*
|
|
66
|
+
* The mind's own verdict on an identity, and it may do what the recognition
|
|
67
|
+
* heuristic will not: absorb an ESTABLISHED relationship. The heuristic is
|
|
68
|
+
* right to refuse — fusing two real people who share a name would take one of
|
|
69
|
+
* them's whole history — but the mind has evidence a name-match does not,
|
|
70
|
+
* usually because somebody just told it. Without this, the same human
|
|
71
|
+
* well-established on two channels stayed two people permanently.
|
|
72
|
+
*/
|
|
73
|
+
sameAs?: string
|
|
62
74
|
}>
|
|
63
75
|
newGoals?: Array<{
|
|
64
76
|
description: string
|
|
@@ -70,6 +82,8 @@ export interface ExecutiveOutputFull {
|
|
|
70
82
|
goalsToAbandon?: Array<{ goalId: string; reason: string }>
|
|
71
83
|
goalsToReprioritize?: Array<{ goalId: string; newPriority: number; reason: string }>
|
|
72
84
|
selfObservations?: string[]
|
|
85
|
+
/** Compound actions the mind is naming as single skills (see ProposedSkill). */
|
|
86
|
+
newSkills?: ProposedSkill[]
|
|
73
87
|
/**
|
|
74
88
|
* Plain-text reply from a conversation facet — populated by parseResponse()
|
|
75
89
|
* from the [REPLY_TEXT]...[/REPLY_TEXT] block.
|
|
@@ -77,6 +91,11 @@ export interface ExecutiveOutputFull {
|
|
|
77
91
|
* Paragraphs (double-newline separated) map to separate reply bubbles.
|
|
78
92
|
*/
|
|
79
93
|
replyText?: string
|
|
94
|
+
/**
|
|
95
|
+
* Set when the facet declared it is NOT speaking this cycle, carrying why.
|
|
96
|
+
* Present ⇒ nothing is sent, whatever else the response contains.
|
|
97
|
+
*/
|
|
98
|
+
noMessage?: string
|
|
80
99
|
/**
|
|
81
100
|
* @deprecated Legacy JSON reply format — no longer emitted by conversation facets.
|
|
82
101
|
* Kept for backward compatibility with any tests/tooling that inspect parsed output.
|
|
@@ -287,6 +306,33 @@ export interface ExecutiveContext {
|
|
|
287
306
|
/** planId if this action came from a plan step */
|
|
288
307
|
planId?: string
|
|
289
308
|
}>
|
|
309
|
+
/**
|
|
310
|
+
* What the mind has said to people lately, and who has answered.
|
|
311
|
+
*
|
|
312
|
+
* The one thing it could never see about itself. `conversation.sent` has been in
|
|
313
|
+
* state since the beginning — 57 records on the Will this was found on — and
|
|
314
|
+
* reached no prompt at all, so the sole evidence of having spoken was a `✓
|
|
315
|
+
* reach-out` line under Recent Action Outcomes: no words, no person, and a tick
|
|
316
|
+
* mark asserting it had worked. That is why the same question went out eleven
|
|
317
|
+
* times in two and a half minutes; from the inside each one was the first.
|
|
318
|
+
*
|
|
319
|
+
* Newest first. Answered turns are kept alongside open ones deliberately — "I
|
|
320
|
+
* asked and they replied" and "I asked and heard nothing" only mean anything
|
|
321
|
+
* against each other.
|
|
322
|
+
*/
|
|
323
|
+
spokenTurns: Array<{
|
|
324
|
+
/** Who it was said to, by name where the mind knows one. */
|
|
325
|
+
target: string
|
|
326
|
+
/** The opening words — enough to recognise a thing already said. */
|
|
327
|
+
preview: string
|
|
328
|
+
/** Ticks since it was said. */
|
|
329
|
+
age: number
|
|
330
|
+
/** Unset while still in the air; the mind is told which. */
|
|
331
|
+
answered: boolean
|
|
332
|
+
/** What they said back, when they did. The fact of an answer without its
|
|
333
|
+
* content is worse than silence — it invites acting as though it is known. */
|
|
334
|
+
answeredWith?: string
|
|
335
|
+
}>
|
|
290
336
|
/** Behavioral disposition loaded from PMA at session start — stable per session. */
|
|
291
337
|
behavioralDisposition?: {
|
|
292
338
|
riskTolerance: number
|
|
@@ -309,6 +355,10 @@ export interface ExecutiveContext {
|
|
|
309
355
|
* Omitted when the Will knows no one.
|
|
310
356
|
*/
|
|
311
357
|
knownEntities?: Array<{
|
|
358
|
+
/** Where this referent is reachable, and how each place has gone. */
|
|
359
|
+
handles?: Array<{ keid: string; kind: string; answeredAgo?: number }>
|
|
360
|
+
/** Names of referents this one may be the same as — an unsettled identity. */
|
|
361
|
+
mayBeSameAs?: string[]
|
|
312
362
|
keid: string
|
|
313
363
|
kind: 'sentient' | 'thing'
|
|
314
364
|
name?: string
|
|
@@ -336,6 +386,25 @@ export interface ExecutiveContext {
|
|
|
336
386
|
|
|
337
387
|
// ── Pending message ──────────────────────────────────────────
|
|
338
388
|
|
|
389
|
+
/**
|
|
390
|
+
* A compound action the mind names as one thing it does — "when I do A then B,
|
|
391
|
+
* that is <name>". Registered into the SchemaRepertoire as a composite, after
|
|
392
|
+
* which it competes as a single affordance and can proceduralize into a habit.
|
|
393
|
+
*
|
|
394
|
+
* This is the creation seam for the instrumental→habitual gradient. Before it,
|
|
395
|
+
* `agency.composite.proposed` was subscribed by ReafferenceEngine — whose handler
|
|
396
|
+
* is the only caller of `registerComposite()` anywhere — and published by nothing,
|
|
397
|
+
* so no Will could ever hold a skill beyond the innate floor (#114).
|
|
398
|
+
*/
|
|
399
|
+
export interface ProposedSkill {
|
|
400
|
+
/** What the mind calls it. Becomes the schema id. */
|
|
401
|
+
id: string
|
|
402
|
+
/** The sub-schemas it is made of, in order. Two or more, or it is not compound. */
|
|
403
|
+
composedOf: string[]
|
|
404
|
+
tags?: string[]
|
|
405
|
+
cost?: number
|
|
406
|
+
}
|
|
407
|
+
|
|
339
408
|
export interface PendingMessage {
|
|
340
409
|
id: string
|
|
341
410
|
content: string
|
|
@@ -71,6 +71,21 @@ const DRIVE_TAGS = new Set([ 'energy', 'sleep', 'stress', 'survival', 'wellbeing
|
|
|
71
71
|
// conscientiousness-developable baseSwitchCost, #28). Recomputed fresh each tick from
|
|
72
72
|
// basePriority — never accumulates. This is what makes the focus mechanically "stick":
|
|
73
73
|
// the focused goal stays top for goal selection, the executive, and planning.
|
|
74
|
+
/**
|
|
75
|
+
* Schema ids and effector names that mean "the mind said something to someone".
|
|
76
|
+
*
|
|
77
|
+
* `reach-out` is the innate schema; `text`/`talk`/`gesture`/`broadcast` are the
|
|
78
|
+
* effectors it resolves to. Both spellings appear as an `action.outcome`'s
|
|
79
|
+
* `actionType` depending on which path emitted it, and only the second set was
|
|
80
|
+
* ever recognised here. Mirrors `COMM_SCHEMAS` in agency/execution.primitives.
|
|
81
|
+
*/
|
|
82
|
+
const COMMUNICATIVE_SCHEMAS = new Set([
|
|
83
|
+
'reach-out', 'reach_out', 'communicate', 'talk', 'text', 'broadcast', 'gesture',
|
|
84
|
+
])
|
|
85
|
+
|
|
86
|
+
/** Progress an action goal gains when the person it concerns actually answers. */
|
|
87
|
+
const ANSWER_PROGRESS_STEP = 0.12
|
|
88
|
+
|
|
74
89
|
const FOCUS_COMMITMENT_RAMP = 30 // ticks of focus to reach full commitment weight
|
|
75
90
|
const COMMITMENT_GAIN = 0.3 // scales switchCost × commitment × plan-sunk-cost
|
|
76
91
|
const MAX_COMMITMENT_BOOST = 0.2 // cap — a clearly higher-priority goal still wins
|
|
@@ -88,6 +103,10 @@ export interface GoalState {
|
|
|
88
103
|
activatedAt: Tick
|
|
89
104
|
deadline?: Tick
|
|
90
105
|
tags: string[]
|
|
106
|
+
/** Why this goal was abandoned, when it was. Set only on status 'abandoned'.
|
|
107
|
+
* The reason used to be pushed onto `tags`, which threw once the goal had
|
|
108
|
+
* been rehydrated from a deep-frozen state entity. */
|
|
109
|
+
abandonedReason?: string
|
|
91
110
|
/** Snapshot of memory.beliefs_total when this goal was activated.
|
|
92
111
|
* Used to compute epistemic progress: (currentBeliefs - baseline) / threshold. */
|
|
93
112
|
beliefsAtActivation: number
|
|
@@ -199,6 +218,7 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
199
218
|
'executive.facet.progress',
|
|
200
219
|
'plan.completed',
|
|
201
220
|
'action.outcome', // 4.1: advance action-type goals when matching outcomes fire
|
|
221
|
+
'social.responsiveness', // somebody answered — the only progress a message can make
|
|
202
222
|
]
|
|
203
223
|
}
|
|
204
224
|
publishes(): CognitiveEventSchema[] {
|
|
@@ -298,6 +318,13 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
298
318
|
this._nudgeActionGoals( p.domain, p.actionType, p.outcomeQuality )
|
|
299
319
|
break
|
|
300
320
|
}
|
|
321
|
+
// A communicative act only advances a goal once somebody answers it. The
|
|
322
|
+
// send itself is not progress — see _nudgeActionGoals.
|
|
323
|
+
case 'social.responsiveness': {
|
|
324
|
+
const p = e.payload as { keid: string; answered: boolean }
|
|
325
|
+
if( p.answered === true && p.keid ) this._nudgeAnsweredGoals( p.keid )
|
|
326
|
+
break
|
|
327
|
+
}
|
|
301
328
|
}
|
|
302
329
|
}
|
|
303
330
|
|
|
@@ -565,8 +592,11 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
565
592
|
const goal = this._goals.get( goalId )
|
|
566
593
|
if( goal && goal.status === 'active'){
|
|
567
594
|
goal.status = 'abandoned'
|
|
568
|
-
//
|
|
569
|
-
|
|
595
|
+
// On its own field, not pushed onto `tags`. A goal rehydrated from a state
|
|
596
|
+
// entity holds that entity's deep-frozen arrays by reference, so the push
|
|
597
|
+
// threw and the goal stayed active forever — still competing for salience,
|
|
598
|
+
// with the rest of the deferred-effects flush for that tick lost with it.
|
|
599
|
+
if( reason ) goal.abandonedReason = reason.slice( 0, 200 )
|
|
570
600
|
|
|
571
601
|
this._sessionLogger?.write({
|
|
572
602
|
type: 'goal.abandoned',
|
|
@@ -618,13 +648,18 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
618
648
|
progress: ( meta.progress as number ) ?? 0,
|
|
619
649
|
status,
|
|
620
650
|
parentGoalId: meta.parentGoalId as string | undefined,
|
|
621
|
-
|
|
651
|
+
// COPY, never adopt. `meta` belongs to a state entity, and the state
|
|
652
|
+
// manager deep-freezes those — adopting the array by reference gives the
|
|
653
|
+
// goal a frozen `tags`/`subGoals`, and the next push (abandonGoal, or
|
|
654
|
+
// addGoal appending to a parent) throws TypeError mid-tick.
|
|
655
|
+
subGoals: [ ...( ( meta.subGoals as string[] ) ?? [] ) ],
|
|
622
656
|
activatedAt: ( meta.activatedAt as Tick ) ?? tick,
|
|
623
657
|
deadline: meta.deadline as Tick | undefined,
|
|
624
|
-
tags: ( meta.tags
|
|
658
|
+
tags: [ ...( ( meta.tags as string[] ) ?? [] ) ],
|
|
625
659
|
beliefsAtActivation: ( meta.beliefsAtActivation as number ) ?? this._currentBeliefCount,
|
|
626
660
|
completionType: ( meta.completionType as GoalState['completionType'] ) ?? 'epistemic',
|
|
627
661
|
completionCondition: meta.completionCondition as string | undefined,
|
|
662
|
+
abandonedReason: meta.abandonedReason as string | undefined,
|
|
628
663
|
})
|
|
629
664
|
}
|
|
630
665
|
}
|
|
@@ -852,11 +887,26 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
852
887
|
const dLow = domain.toLowerCase()
|
|
853
888
|
const aLow = actionType.toLowerCase()
|
|
854
889
|
|
|
855
|
-
//
|
|
856
|
-
//
|
|
857
|
-
//
|
|
858
|
-
|
|
859
|
-
|
|
890
|
+
// A communicative act makes NO progress by being sent.
|
|
891
|
+
//
|
|
892
|
+
// This is the whole shape of the repetition failure, seen from the goal side.
|
|
893
|
+
// `action.outcome` for a message fires the moment the outbox accepts it, with
|
|
894
|
+
// `outcomeQuality` describing the delivery — so crediting it here would have a
|
|
895
|
+
// goal like "get a clear answer to one operational question" complete itself
|
|
896
|
+
// after nine unanswered messages, and the mind would be right to keep sending
|
|
897
|
+
// because sending was visibly working.
|
|
898
|
+
//
|
|
899
|
+
// The answer is progress. It arrives later, through `social.responsiveness`,
|
|
900
|
+
// and lands in _nudgeAnsweredGoals.
|
|
901
|
+
//
|
|
902
|
+
// (Before this the question was moot in the worst way: `actionType` for an
|
|
903
|
+
// outreach is the SCHEMA id, `reach-out`, and the old test looked only for
|
|
904
|
+
// 'talk'/'text', so no communicative act ever reached a goal at all. Measured
|
|
905
|
+
// on a live Will: 8 goals, 28 reach-outs, and `lastActionAttemptTick` unset on
|
|
906
|
+
// every single goal. The bridge was not miscalibrated, it was disconnected —
|
|
907
|
+
// which is why fixing the match alone would have turned a silent failure into
|
|
908
|
+
// a loud one.)
|
|
909
|
+
if( COMMUNICATIVE_SCHEMAS.has( aLow ) || dLow === 'communication') return
|
|
860
910
|
|
|
861
911
|
for( const goal of this._goals.values() ){
|
|
862
912
|
if( goal.status !== 'active' && goal.status !== 'blocked') continue
|
|
@@ -868,9 +918,7 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
868
918
|
|| t === aLow
|
|
869
919
|
|| dLow.includes( t ) || t.includes( dLow )
|
|
870
920
|
|| aLow.includes( t ) || t.includes( aLow )
|
|
871
|
-
})
|
|
872
|
-
t === 'communication' || t === 'reply' || t === 'conversation'
|
|
873
|
-
) )
|
|
921
|
+
})
|
|
874
922
|
|
|
875
923
|
if( hasMatch ){
|
|
876
924
|
goal.progress = Math.min( 1, goal.progress + outcomeQuality * 0.12 )
|
|
@@ -880,6 +928,33 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
880
928
|
}
|
|
881
929
|
}
|
|
882
930
|
|
|
931
|
+
/**
|
|
932
|
+
* Somebody answered. Advance the action goals that were about reaching them.
|
|
933
|
+
*
|
|
934
|
+
* Linked the way every other goal→person link in the system is linked: the
|
|
935
|
+
* `keid:<id>` tag (selection.scoring's `collectGoalTargets` reads the same one
|
|
936
|
+
* to lift a reach-out's goal relevance) or `requestingEntityId`, set when a
|
|
937
|
+
* conversation escalation created the goal. Nothing here guesses from wording.
|
|
938
|
+
*
|
|
939
|
+
* Unmatched by design: a goal with no link to this person gets nothing, even if
|
|
940
|
+
* it is tagged 'communication'. Being answered by one person is not progress on
|
|
941
|
+
* wanting to talk to another.
|
|
942
|
+
*/
|
|
943
|
+
private _nudgeAnsweredGoals( keid: string ): void {
|
|
944
|
+
const tag = `keid:${ keid }`.toLowerCase()
|
|
945
|
+
|
|
946
|
+
for( const goal of this._goals.values() ){
|
|
947
|
+
if( goal.status !== 'active' && goal.status !== 'blocked') continue
|
|
948
|
+
if( goal.completionType !== 'action') continue
|
|
949
|
+
if( goal.requestingEntityId !== keid
|
|
950
|
+
&& !goal.tags.some( t => t.toLowerCase() === tag ) ) continue
|
|
951
|
+
|
|
952
|
+
goal.progress = Math.min( 1, goal.progress + ANSWER_PROGRESS_STEP )
|
|
953
|
+
goal.lastActionAttemptTick = this._currentTick
|
|
954
|
+
goal.lastActionType = 'answered'
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
|
|
883
958
|
/** True when a metric completionCondition (e.g. "emotion.boredom < 40") is already met. */
|
|
884
959
|
private _isConditionMet( condition: string | undefined, state: ReadonlySimulationState ): boolean {
|
|
885
960
|
if( !condition ) return false
|
|
@@ -994,9 +1069,14 @@ export class GoalManager implements SimulationEngine, CognitiveEngine {
|
|
|
994
1069
|
progress: goal.progress,
|
|
995
1070
|
status: goal.status,
|
|
996
1071
|
parentGoalId: goal.parentGoalId,
|
|
997
|
-
|
|
1072
|
+
// COPY on the way out too. The state manager deep-freezes what it
|
|
1073
|
+
// stores, so handing it these arrays by reference freezes the
|
|
1074
|
+
// manager's OWN copies — a goal created this session, never
|
|
1075
|
+
// rehydrated, still ends up with an unpushable `tags`.
|
|
1076
|
+
subGoals: [ ...goal.subGoals ],
|
|
998
1077
|
deadline: goal.deadline,
|
|
999
|
-
tags: goal.tags,
|
|
1078
|
+
tags: [ ...goal.tags ],
|
|
1079
|
+
abandonedReason: goal.abandonedReason,
|
|
1000
1080
|
beliefsAtActivation: goal.beliefsAtActivation,
|
|
1001
1081
|
completionType: goal.completionType,
|
|
1002
1082
|
completionCondition: goal.completionCondition,
|