@mindot/will 0.1.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/LICENSE +202 -0
- package/README.md +777 -0
- package/dist/index.d.ts +8144 -0
- package/dist/index.js +26231 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
- package/src/cognition/agency/access.grants.ts +59 -0
- package/src/cognition/agency/competence.codec.ts +89 -0
- package/src/cognition/agency/engines/action.selector.ts +462 -0
- package/src/cognition/agency/engines/affordance.synthesizer.ts +384 -0
- package/src/cognition/agency/engines/deliberation.engine.ts +241 -0
- package/src/cognition/agency/engines/instruction.intake.ts +54 -0
- package/src/cognition/agency/engines/motor.schema.executor.ts +532 -0
- package/src/cognition/agency/engines/reafference.engine.ts +241 -0
- package/src/cognition/agency/execution.primitives.ts +118 -0
- package/src/cognition/agency/index.ts +58 -0
- package/src/cognition/agency/proactive.communicator.ts +274 -0
- package/src/cognition/agency/reconcile.learning.ts +82 -0
- package/src/cognition/agency/schemas/external.ts +59 -0
- package/src/cognition/agency/schemas/innate.ts +111 -0
- package/src/cognition/agency/schemas/repertoire.ts +246 -0
- package/src/cognition/agency/selection.scoring.ts +191 -0
- package/src/cognition/agency/types.ts +165 -0
- package/src/cognition/bus.ts +391 -0
- package/src/cognition/completion.inbox.ts +86 -0
- package/src/cognition/config.mirror.entities.ts +475 -0
- package/src/cognition/conversation.memory.ts +81 -0
- package/src/cognition/event.log.ts +116 -0
- package/src/cognition/event.schemas.ts +568 -0
- package/src/cognition/faculties/aesthetic.evaluator.ts +313 -0
- package/src/cognition/faculties/affective.blender.ts +562 -0
- package/src/cognition/faculties/attachment.evaluator.ts +432 -0
- package/src/cognition/faculties/attention.allocator.ts +422 -0
- package/src/cognition/faculties/autobiographical.narrator.ts +300 -0
- package/src/cognition/faculties/bias.detector.ts +363 -0
- package/src/cognition/faculties/circadian.oscillator.ts +294 -0
- package/src/cognition/faculties/confidence.calibrator.ts +324 -0
- package/src/cognition/faculties/dream.simulator.ts +251 -0
- package/src/cognition/faculties/empathy.simulator.ts +253 -0
- package/src/cognition/faculties/energy.regulator.ts +320 -0
- package/src/cognition/faculties/episodic.consolidator.ts +728 -0
- package/src/cognition/faculties/executive.engine/commands.ts +459 -0
- package/src/cognition/faculties/executive.engine/config.ts +46 -0
- package/src/cognition/faculties/executive.engine/context.ts +605 -0
- package/src/cognition/faculties/executive.engine/deferred.effects.ts +104 -0
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +60 -0
- package/src/cognition/faculties/executive.engine/effort.gate.ts +114 -0
- package/src/cognition/faculties/executive.engine/engine.ts +1028 -0
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +90 -0
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +271 -0
- package/src/cognition/faculties/executive.engine/facet.ts +628 -0
- package/src/cognition/faculties/executive.engine/gating.ts +222 -0
- package/src/cognition/faculties/executive.engine/index.ts +6 -0
- package/src/cognition/faculties/executive.engine/messages.ts +102 -0
- package/src/cognition/faculties/executive.engine/parser.ts +380 -0
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +1053 -0
- package/src/cognition/faculties/executive.engine/types.ts +323 -0
- package/src/cognition/faculties/exteroception.ts +338 -0
- package/src/cognition/faculties/forgetting.curve.ts +202 -0
- package/src/cognition/faculties/frustration.evaluator.ts +280 -0
- package/src/cognition/faculties/goal.manager.ts +1008 -0
- package/src/cognition/faculties/inhibition.controller.ts +351 -0
- package/src/cognition/faculties/interoception.ts +417 -0
- package/src/cognition/faculties/introspection.engine.ts +246 -0
- package/src/cognition/faculties/known.entity.tracker.ts +439 -0
- package/src/cognition/faculties/loss.evaluator.ts +224 -0
- package/src/cognition/faculties/moral.evaluator.ts +437 -0
- package/src/cognition/faculties/novelty.detector.ts +254 -0
- package/src/cognition/faculties/persona.consolidator.ts +845 -0
- package/src/cognition/faculties/planning.engine/engine.ts +859 -0
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +83 -0
- package/src/cognition/faculties/planning.engine/plan.store.ts +174 -0
- package/src/cognition/faculties/planning.engine/plan.supervision.ts +494 -0
- package/src/cognition/faculties/planning.engine/types.ts +143 -0
- package/src/cognition/faculties/reputation.tracker.ts +294 -0
- package/src/cognition/faculties/reward.evaluator.ts +304 -0
- package/src/cognition/faculties/self.model.updater.ts +677 -0
- package/src/cognition/faculties/semantic.engine/clustering.ts +673 -0
- package/src/cognition/faculties/semantic.engine/index.ts +6 -0
- package/src/cognition/faculties/semantic.engine/integrator.ts +750 -0
- package/src/cognition/faculties/semantic.engine/types.ts +53 -0
- package/src/cognition/faculties/sleep.pressure.regulator.ts +262 -0
- package/src/cognition/faculties/social.perception.ts +333 -0
- package/src/cognition/faculties/spaced.repetition.ts +605 -0
- package/src/cognition/faculties/stress.regulator.ts +375 -0
- package/src/cognition/faculties/task.switcher.ts +239 -0
- package/src/cognition/faculties/theory.of.mind.ts +333 -0
- package/src/cognition/faculties/threat.evaluator.ts +312 -0
- package/src/cognition/faculties/working.memory.ts +406 -0
- package/src/cognition/generative.model.ts +344 -0
- package/src/cognition/heartbeat.ts +70 -0
- package/src/cognition/index.ts +291 -0
- package/src/cognition/instruction.handler.ts +146 -0
- package/src/cognition/memory/index.ts +35 -0
- package/src/cognition/memory/vector.adapter.ts +316 -0
- package/src/cognition/memory/vector.content.ts +47 -0
- package/src/cognition/memory/vector.embedder.ts +212 -0
- package/src/cognition/memory/vector.index.ts +459 -0
- package/src/cognition/memory/vector.types.ts +65 -0
- package/src/cognition/orchestrator.ts +142 -0
- package/src/cognition/persona.prior.ts +265 -0
- package/src/cognition/schema.registry.ts +145 -0
- package/src/cognition/senses/audition.engine/engine.ts +976 -0
- package/src/cognition/senses/audition.engine/salience.ts +59 -0
- package/src/cognition/senses/base.sense.engine.ts +117 -0
- package/src/cognition/senses/gustation.engine.ts +24 -0
- package/src/cognition/senses/index.ts +172 -0
- package/src/cognition/senses/olfaction.engine.ts +24 -0
- package/src/cognition/senses/somatosensation.engine.ts +23 -0
- package/src/cognition/senses/vision.engine.ts +23 -0
- package/src/cognition/types.ts +63 -0
- package/src/cognition/utilities/token.tracker.ts +492 -0
- package/src/core/abstracts.ts +60 -0
- package/src/core/async.engine.ts +539 -0
- package/src/core/clock.ts +196 -0
- package/src/core/completion.recorder.ts +122 -0
- package/src/core/conflict.detector.ts +140 -0
- package/src/core/distributed.ts +626 -0
- package/src/core/event.bus.ts +292 -0
- package/src/core/inbound.recorder.ts +100 -0
- package/src/core/index.ts +167 -0
- package/src/core/logger.ts +85 -0
- package/src/core/metrics.ts +107 -0
- package/src/core/orchestrator.ts +625 -0
- package/src/core/replay.ts +589 -0
- package/src/core/scenario.ts +120 -0
- package/src/core/serialization.ts +448 -0
- package/src/core/simulation.ts +197 -0
- package/src/core/snapshot.manager.ts +293 -0
- package/src/core/state.manager.ts +283 -0
- package/src/core/types.ts +274 -0
- package/src/core/utils.ts +82 -0
- package/src/core/wall.clock.ts +23 -0
- package/src/extensions/livestream.ext.ts +570 -0
- package/src/extensions/time.ext.ts +339 -0
- package/src/index.ts +91 -0
- package/src/llm/gate.ts +140 -0
- package/src/llm/index.ts +716 -0
- package/src/llm/summarizer.ts +170 -0
- package/src/llm/wire.contracts.ts +67 -0
- package/src/pma/eval.ts +651 -0
- package/src/pma/index.ts +1082 -0
- package/src/profiles/built-in.ts +7 -0
- package/src/profiles/companion.ts +28 -0
- package/src/profiles/company-brain.ts +54 -0
- package/src/profiles/customer-service.ts +30 -0
- package/src/profiles/game-npc.ts +20 -0
- package/src/profiles/index.ts +42 -0
- package/src/profiles/smart-home.ts +34 -0
- package/src/runners/coherence.runner.ts +49 -0
- package/src/runners/outreach.runner.ts +158 -0
- package/src/runners/social.runner.ts +182 -0
- package/src/runners/thin-shim.runner.ts +195 -0
- package/src/sdk/will.ts +334 -0
- package/src/stem/assembly.audit.ts +88 -0
- package/src/stem/distribution.ts +203 -0
- package/src/stem/guards/identity.coherence.ts +152 -0
- package/src/stem/guards/identity.guard.ts +227 -0
- package/src/stem/index.ts +1145 -0
- package/src/stem/mind.ts +1090 -0
- package/src/stem/tracts/ack.reconciler.ts +55 -0
- package/src/stem/tracts/biography.writer.ts +190 -0
- package/src/stem/tracts/effector.controller.ts +143 -0
- package/src/stem/tracts/health.reporter.ts +113 -0
- package/src/stem/tracts/inbound.queue.ts +54 -0
- package/src/stem/tracts/outbox.controller.ts +167 -0
- package/src/stem/tracts/outbox.writer.ts +133 -0
- package/src/stem/tracts/pma.controller.ts +109 -0
- package/src/stem/tracts/replay.controller.ts +177 -0
- package/src/stem/tracts/sensory.controller.ts +168 -0
- package/src/stem/tracts/session.logger.ts +157 -0
- package/src/stem/tracts/transport/index.ts +38 -0
- package/src/stem/tracts/transport/loopback.transport.ts +86 -0
- package/src/stem/tracts/transport/socketio.transport.ts +200 -0
- package/src/stem/tracts/transport/stream.transport.ts +152 -0
- package/src/stem/tracts/transport/types.ts +193 -0
- package/src/stem/tracts/transport.controller.ts +380 -0
- package/src/types.ts +116 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// src/cognition/faculties/executive.engine/parser.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
import { logger } from '#core/logger'
|
|
6
|
+
import { REPLY_TEXT_TAG } from '#llm/wire.contracts'
|
|
7
|
+
import type { ReadonlySimulationState } from '#core/types'
|
|
8
|
+
import type { ExecutiveOutputFull, ExecutiveOutputMinimal, IdeationCandidate, IdeationOutput } from '#faculties/executive.engine/types'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse the LLM's text response into an ExecutiveOutputFull.
|
|
12
|
+
* Handles three paths:
|
|
13
|
+
* 1. Direct JSON parse (when LLM outputs valid JSON)
|
|
14
|
+
* 2. Balanced-bracket extraction (when JSON has unescaped content in strings)
|
|
15
|
+
* 3. Heuristic fallback (when parsing fails entirely)
|
|
16
|
+
*/
|
|
17
|
+
export function parseResponse(
|
|
18
|
+
responseText: string,
|
|
19
|
+
state: ReadonlySimulationState,
|
|
20
|
+
recentActionTypes: string[]
|
|
21
|
+
): ExecutiveOutputFull {
|
|
22
|
+
// Find the first code block that contains "actions" (skips tool-query preamble)
|
|
23
|
+
const
|
|
24
|
+
codeBlocks = [ ...responseText.matchAll(/```(?:json)?\s*\n?([\s\S]*?)\n?```/g) ],
|
|
25
|
+
actionsBlock = codeBlocks.find( m => m[1]!.includes('"actions"') ),
|
|
26
|
+
fullText = actionsBlock?.[1]?.trim() ?? responseText.trim()
|
|
27
|
+
|
|
28
|
+
let
|
|
29
|
+
actions: Array<{ type: string; reasoning: string; expectedOutcome: string }>,
|
|
30
|
+
confidence = 0.5
|
|
31
|
+
|
|
32
|
+
// Strategy 1: direct JSON.parse
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse( fullText ) as {
|
|
35
|
+
actions: typeof actions
|
|
36
|
+
reasoning?: string
|
|
37
|
+
confidence?: number
|
|
38
|
+
}
|
|
39
|
+
if( !Array.isArray( parsed.actions ) )
|
|
40
|
+
throw new Error('actions is not an array')
|
|
41
|
+
|
|
42
|
+
actions = parsed.actions
|
|
43
|
+
confidence = parsed.confidence ?? 0.5
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Strategy 2: balanced-bracket extractor
|
|
47
|
+
const actionsStr = extractBalancedArray( fullText, 'actions' )
|
|
48
|
+
if( !actionsStr) {
|
|
49
|
+
logger.warn('[executive] No actions found in response — using fallback')
|
|
50
|
+
return buildFallbackOutput( state, recentActionTypes )
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try { actions = JSON.parse( actionsStr ) }
|
|
54
|
+
catch {
|
|
55
|
+
logger.warn('[executive] Failed to parse actions array — using fallback')
|
|
56
|
+
return buildFallbackOutput( state, recentActionTypes )
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const confidenceMatch = fullText.match(/"confidence"\s*:\s*([\d.]+)/)
|
|
60
|
+
confidence = confidenceMatch ? parseFloat( confidenceMatch[1]! ) : 0.5
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Parse tagged blocks from the extracted JSON text
|
|
64
|
+
const full = parseTaggedBlocks({ actions, reasoning: fullText, confidence }, state )
|
|
65
|
+
|
|
66
|
+
// [REPLY_TEXT] is a plain-text block that lives OUTSIDE the JSON code block.
|
|
67
|
+
// Search the full response text so we find it even when the LLM used a code fence.
|
|
68
|
+
const replyText = extractTextBlock( responseText, REPLY_TEXT_TAG )
|
|
69
|
+
if( replyText ) full.replyText = replyText
|
|
70
|
+
|
|
71
|
+
return full
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Parse the ideation (propose) pass into an IdeationOutput. Mirrors parseResponse's
|
|
76
|
+
* resilience: prefer a fenced ```json block, fall back to raw text, then to
|
|
77
|
+
* balanced-bracket extraction of the `candidates` array. Returns an EMPTY candidate set
|
|
78
|
+
* on total failure — the caller then proceeds as if no options were generated (the
|
|
79
|
+
* decision pass simply runs without an injected set), so a flaky propose pass degrades
|
|
80
|
+
* gracefully rather than breaking the tick. Pure: no state, no I/O.
|
|
81
|
+
*/
|
|
82
|
+
export function parseIdeation( responseText: string ): IdeationOutput {
|
|
83
|
+
const
|
|
84
|
+
codeBlocks = [ ...responseText.matchAll(/```(?:json)?\s*\n?([\s\S]*?)\n?```/g) ],
|
|
85
|
+
block = codeBlocks.find( m => m[1]!.includes('"candidates"') ),
|
|
86
|
+
text = block?.[1]?.trim() ?? responseText.trim()
|
|
87
|
+
|
|
88
|
+
const coerce = ( raw: unknown ): IdeationCandidate[] => {
|
|
89
|
+
if( !Array.isArray( raw ) ) return []
|
|
90
|
+
return raw
|
|
91
|
+
.map( c => {
|
|
92
|
+
const o = ( c ?? {} ) as Record<string, unknown>
|
|
93
|
+
return {
|
|
94
|
+
approach: String( o.approach ?? '' ).trim(),
|
|
95
|
+
description: String( o.description ?? '' ).trim(),
|
|
96
|
+
upside: String( o.upside ?? '' ).trim(),
|
|
97
|
+
risk: String( o.risk ?? '' ).trim(),
|
|
98
|
+
}
|
|
99
|
+
} )
|
|
100
|
+
.filter( c => c.approach.length > 0 || c.description.length > 0 )
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Strategy 1: direct JSON parse of the (block-or-raw) text.
|
|
104
|
+
try {
|
|
105
|
+
const parsed = JSON.parse( text ) as { candidates?: unknown }
|
|
106
|
+
const candidates = coerce( parsed.candidates )
|
|
107
|
+
if( candidates.length > 0 ) return { candidates }
|
|
108
|
+
}
|
|
109
|
+
catch { /* fall through to bracket extraction */ }
|
|
110
|
+
|
|
111
|
+
// Strategy 2: balanced-bracket extraction of the `candidates` array.
|
|
112
|
+
const arrStr = extractBalancedArray( text, 'candidates' )
|
|
113
|
+
if( arrStr ){
|
|
114
|
+
try { return { candidates: coerce( JSON.parse( arrStr ) ) } }
|
|
115
|
+
catch { /* fall through to empty */ }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
logger.warn('[executive] ideation parse failed — no candidates (deliberate pass proceeds without an injected set)')
|
|
119
|
+
return { candidates: [] }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extract the JSON array value for `key` from `text` using balanced-bracket
|
|
124
|
+
* counting. Properly skips characters inside JSON string literals.
|
|
125
|
+
*/
|
|
126
|
+
function extractBalancedArray( text: string, key: string ): string | null {
|
|
127
|
+
const keyMatch = text.match( new RegExp(`"${key}"\\s*:\\s*(\\[)`) )
|
|
128
|
+
if( !keyMatch || keyMatch.index === undefined ) return null
|
|
129
|
+
|
|
130
|
+
const start = keyMatch.index + keyMatch[0].length - 1
|
|
131
|
+
let
|
|
132
|
+
depth = 0,
|
|
133
|
+
inString = false,
|
|
134
|
+
escape = false
|
|
135
|
+
|
|
136
|
+
for( let i = start; i < text.length; i++ ){
|
|
137
|
+
const ch = text[i]!
|
|
138
|
+
if( escape ){
|
|
139
|
+
escape = false
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if( ch === '\\' && inString ){
|
|
144
|
+
escape = true
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if( ch === '"' ){
|
|
149
|
+
inString = !inString
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if( inString ) continue
|
|
154
|
+
|
|
155
|
+
if( ch === '[' || ch === '{' ) depth++
|
|
156
|
+
else if( ch === ']' || ch === '}' ){
|
|
157
|
+
depth--
|
|
158
|
+
|
|
159
|
+
if( depth === 0 )
|
|
160
|
+
return text.slice( start, i + 1 )
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Parse tagged JSON blocks from the reasoning text.
|
|
169
|
+
* The LLM embeds optional outputs as [TAG]...[/TAG] blocks.
|
|
170
|
+
*/
|
|
171
|
+
function parseTaggedBlocks(
|
|
172
|
+
minimal: ExecutiveOutputMinimal,
|
|
173
|
+
state: ReadonlySimulationState,
|
|
174
|
+
): ExecutiveOutputFull {
|
|
175
|
+
const
|
|
176
|
+
full: ExecutiveOutputFull = {
|
|
177
|
+
actions: minimal.actions,
|
|
178
|
+
reasoning: minimal.reasoning,
|
|
179
|
+
confidence: minimal.confidence,
|
|
180
|
+
},
|
|
181
|
+
text = minimal.reasoning,
|
|
182
|
+
|
|
183
|
+
// Diagnostic logging — REPLY_TEXT is detected in parseResponse() directly
|
|
184
|
+
taggedTypes = [
|
|
185
|
+
'PLANS',
|
|
186
|
+
'BELIEFS',
|
|
187
|
+
'INTROSPECTION',
|
|
188
|
+
'NARRATIVE',
|
|
189
|
+
'IDENTITY',
|
|
190
|
+
'KNOWN_ENTITIES',
|
|
191
|
+
'GOALS_NEW',
|
|
192
|
+
'GOALS_ABANDON',
|
|
193
|
+
'GOALS_REPRIORITIZE',
|
|
194
|
+
'EFFECTORS',
|
|
195
|
+
'SELF_OBS',
|
|
196
|
+
],
|
|
197
|
+
found = taggedTypes.filter( t => text.includes(`[${t}]`) )
|
|
198
|
+
if( found.length > 0 ){
|
|
199
|
+
const closed = found.map( t => `${t}: ${text.includes(`[/${t}]`) ? 'CLOSED' : 'UNCLOSED'}` ).join(', ')
|
|
200
|
+
logger.info(`[executive] TAGGED BLOCKS: ${closed}`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const parseJsonBlock = ( tag: string ): unknown | null => {
|
|
204
|
+
const block = extractBlock( text, tag )
|
|
205
|
+
if( !block ) return null
|
|
206
|
+
|
|
207
|
+
const trimmed = block.trim()
|
|
208
|
+
|
|
209
|
+
// Parse-then-repair (FN10). The common case is already-valid, correctly
|
|
210
|
+
// escaped JSON — try it verbatim first. The old code *unconditionally*
|
|
211
|
+
// ran a destructive un-escape (\" → ", \\n → literal newline, \\ → \)
|
|
212
|
+
// before parsing, which mangled valid JSON so JSON.parse threw and the
|
|
213
|
+
// block was silently dropped. Whether it broke depended on whether the
|
|
214
|
+
// content happened to contain escapable characters → non-deterministic,
|
|
215
|
+
// content-dependent loss of PLANS/BELIEFS/GOALS/NARRATIVE/… blocks.
|
|
216
|
+
try { return JSON.parse( trimmed ) }
|
|
217
|
+
catch { /* not directly parseable — fall through to best-effort repair */ }
|
|
218
|
+
|
|
219
|
+
// Repair pass only for the rarer case where the model double-escaped the
|
|
220
|
+
// payload. Best-effort: if it still doesn't parse, drop the optional block.
|
|
221
|
+
try {
|
|
222
|
+
const repaired = trimmed.replace(/\\"/g, '"')
|
|
223
|
+
.replace(/\\n/g, '\n')
|
|
224
|
+
.replace(/\\\\/g, '\\')
|
|
225
|
+
return JSON.parse( repaired )
|
|
226
|
+
}
|
|
227
|
+
catch { return null }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Plans
|
|
231
|
+
try {
|
|
232
|
+
const plansData = parseJsonBlock('PLANS') as { plans?: ExecutiveOutputFull['plans'] } | null
|
|
233
|
+
if( plansData?.plans ) full.plans = plansData.plans
|
|
234
|
+
}
|
|
235
|
+
catch { /* ignore parse errors for optional blocks */ }
|
|
236
|
+
|
|
237
|
+
// Beliefs
|
|
238
|
+
try {
|
|
239
|
+
const beliefsData = parseJsonBlock('BELIEFS') as { newBeliefs?: ExecutiveOutputFull['newBeliefs'] } | null
|
|
240
|
+
if( beliefsData?.newBeliefs ) full.newBeliefs = beliefsData.newBeliefs
|
|
241
|
+
}
|
|
242
|
+
catch { /* ignore */ }
|
|
243
|
+
|
|
244
|
+
// Introspection
|
|
245
|
+
try {
|
|
246
|
+
const introspectionData = parseJsonBlock('INTROSPECTION') as { introspection?: ExecutiveOutputFull['introspection'] } | null
|
|
247
|
+
if( introspectionData?.introspection ) full.introspection = introspectionData.introspection
|
|
248
|
+
}
|
|
249
|
+
catch { /* ignore */ }
|
|
250
|
+
|
|
251
|
+
// Narrative
|
|
252
|
+
try {
|
|
253
|
+
const narrativeData = parseJsonBlock('NARRATIVE') as {
|
|
254
|
+
narrative?: string; narrativeThemes?: string[]; currentSelfView?: string
|
|
255
|
+
} | null
|
|
256
|
+
if( narrativeData ){
|
|
257
|
+
if( narrativeData.narrative ) full.narrative = narrativeData.narrative
|
|
258
|
+
if( narrativeData.narrativeThemes ) full.narrativeThemes = narrativeData.narrativeThemes
|
|
259
|
+
if( narrativeData.currentSelfView ) full.currentSelfView = narrativeData.currentSelfView
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch { /* ignore */ }
|
|
263
|
+
|
|
264
|
+
// Identity updates
|
|
265
|
+
try {
|
|
266
|
+
const identityData = parseJsonBlock('IDENTITY') as { identityUpdates?: ExecutiveOutputFull['identityUpdates'] } | null
|
|
267
|
+
if( identityData?.identityUpdates ) full.identityUpdates = identityData.identityUpdates
|
|
268
|
+
|
|
269
|
+
const knownEntityData = parseJsonBlock('KNOWN_ENTITIES') as { knownEntityUpdates?: ExecutiveOutputFull['knownEntityUpdates'] } | null
|
|
270
|
+
if( knownEntityData?.knownEntityUpdates ) full.knownEntityUpdates = knownEntityData.knownEntityUpdates
|
|
271
|
+
}
|
|
272
|
+
catch { /* ignore */ }
|
|
273
|
+
|
|
274
|
+
// New goals
|
|
275
|
+
try {
|
|
276
|
+
const goalsNewData = parseJsonBlock('GOALS_NEW') as { newGoals?: ExecutiveOutputFull['newGoals'] } | null
|
|
277
|
+
if( goalsNewData?.newGoals ) full.newGoals = goalsNewData.newGoals
|
|
278
|
+
}
|
|
279
|
+
catch { /* ignore */ }
|
|
280
|
+
|
|
281
|
+
// Goals to abandon
|
|
282
|
+
try {
|
|
283
|
+
const goalsAbandonData = parseJsonBlock('GOALS_ABANDON') as { goalsToAbandon?: ExecutiveOutputFull['goalsToAbandon'] } | null
|
|
284
|
+
if( goalsAbandonData?.goalsToAbandon ) full.goalsToAbandon = goalsAbandonData.goalsToAbandon
|
|
285
|
+
}
|
|
286
|
+
catch { /* ignore */ }
|
|
287
|
+
|
|
288
|
+
// Goals to reprioritize
|
|
289
|
+
try {
|
|
290
|
+
const goalsReprioritizeData = parseJsonBlock('GOALS_REPRIORITIZE') as { goalsToReprioritize?: ExecutiveOutputFull['goalsToReprioritize'] } | null
|
|
291
|
+
if( goalsReprioritizeData?.goalsToReprioritize ) full.goalsToReprioritize = goalsReprioritizeData.goalsToReprioritize
|
|
292
|
+
}
|
|
293
|
+
catch { /* ignore */ }
|
|
294
|
+
|
|
295
|
+
// Self-observations
|
|
296
|
+
try {
|
|
297
|
+
const selfObsData = parseJsonBlock('SELF_OBS') as { selfObservations?: string[] } | null
|
|
298
|
+
if( selfObsData?.selfObservations ) full.selfObservations = selfObsData.selfObservations
|
|
299
|
+
}
|
|
300
|
+
catch { /* ignore */ }
|
|
301
|
+
|
|
302
|
+
// [ACK] and legacy [REPLY] blocks are no longer emitted by any engine.
|
|
303
|
+
// [ACK] — removed with the legacy master communication path (Phase 13.8).
|
|
304
|
+
// [REPLY] — replaced by [REPLY_TEXT] plain-text block in conversation facets.
|
|
305
|
+
// Both are preserved as @deprecated fields on ExecutiveOutputFull for compatibility.
|
|
306
|
+
|
|
307
|
+
return full
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function extractBlock( text: string, tag: string ): string | null {
|
|
311
|
+
const regex = new RegExp(`\\[${tag}\\]\\s*\\n?([\\s\\S]*?)\\n?\\[/${tag}\\]`, 'i')
|
|
312
|
+
const match = text.match( regex )
|
|
313
|
+
|
|
314
|
+
return match?.[1]?.trim() ?? null
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Extract plain text (not JSON) between [TAG] and [/TAG] markers.
|
|
319
|
+
* Used for [REPLY_TEXT] — content is streamed directly to the client,
|
|
320
|
+
* so it must be clean prose, not a JSON payload.
|
|
321
|
+
*/
|
|
322
|
+
function extractTextBlock( text: string, tag: string ): string | null {
|
|
323
|
+
const open = `[${tag}]`
|
|
324
|
+
const close = `[/${tag}]`
|
|
325
|
+
const openIdx = text.indexOf( open )
|
|
326
|
+
if( openIdx === -1 ) return null
|
|
327
|
+
const contentStart = openIdx + open.length
|
|
328
|
+
const closeIdx = text.indexOf( close, contentStart )
|
|
329
|
+
const content = closeIdx !== -1
|
|
330
|
+
? text.slice( contentStart, closeIdx )
|
|
331
|
+
: text.slice( contentStart ) // unclosed block — take everything after the open marker
|
|
332
|
+
|
|
333
|
+
return content.trim() || null
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Build a heuristic fallback output when LLM response parsing fails.
|
|
338
|
+
* Chooses actions based on current physiological state.
|
|
339
|
+
*/
|
|
340
|
+
export function buildFallbackOutput(
|
|
341
|
+
state: ReadonlySimulationState,
|
|
342
|
+
recentActionTypes: string[]
|
|
343
|
+
): ExecutiveOutputFull {
|
|
344
|
+
const energy = state.metrics.get('energy.level') ?? 100
|
|
345
|
+
const sleepPressure = state.metrics.get('sleep.pressure') ?? 0
|
|
346
|
+
const stressLoad = state.metrics.get('stress.load') ?? 0
|
|
347
|
+
|
|
348
|
+
let actionType = 'observe'
|
|
349
|
+
let actionReason = 'No urgent needs — observing surroundings.'
|
|
350
|
+
|
|
351
|
+
if( energy < 20 ){
|
|
352
|
+
actionType = 'replenish energy'
|
|
353
|
+
actionReason = 'Energy critically low — attempting to rest.'
|
|
354
|
+
}
|
|
355
|
+
else if( sleepPressure > 60 ){
|
|
356
|
+
actionType = 'enter deep rest to reduce fatigue'
|
|
357
|
+
actionReason = 'Sleep pressure high — attempting to sleep.'
|
|
358
|
+
}
|
|
359
|
+
else if( stressLoad > 70 ){
|
|
360
|
+
actionType = 'calm my mind and reduce tension'
|
|
361
|
+
actionReason = 'Stress elevated — attempting to meditate.'
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// Vary the fallback when low-effort actions have dominated recently
|
|
365
|
+
const monotonous = recentActionTypes.filter( t => t === 'reflect' || t === 'observe' ).length >= 3
|
|
366
|
+
if( monotonous) {
|
|
367
|
+
const alternatives = ['explore', 'learn', 'express_emotion', 'rest']
|
|
368
|
+
const tick = state.tick as number
|
|
369
|
+
|
|
370
|
+
actionType = alternatives[ tick % alternatives.length ]!
|
|
371
|
+
actionReason = 'Breaking monotony — choosing a varied action during LLM fallback.'
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
actions: [{ type: actionType, reasoning: actionReason, expectedOutcome: 'State improves' }],
|
|
377
|
+
reasoning: `Heuristic fallback — LLM unavailable. ${actionReason}`,
|
|
378
|
+
confidence: 0.4
|
|
379
|
+
}
|
|
380
|
+
}
|