@framers/agentos 0.10.22 → 0.10.24
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 +1 -1
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +3 -2
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +25 -10
- package/dist/api/agent.js.map +1 -1
- package/dist/api/embedText.d.ts.map +1 -1
- package/dist/api/embedText.js +53 -17
- package/dist/api/embedText.js.map +1 -1
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +51 -7
- package/dist/api/generateImage.js.map +1 -1
- package/dist/api/generateText.d.ts +50 -7
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +265 -35
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +25 -1
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +108 -3
- package/dist/api/model.js.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts +34 -2
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.js +119 -33
- package/dist/api/runtime/SelfImprovementSessionManager.js.map +1 -1
- package/dist/api/runtime/global-default.d.ts +9 -1
- package/dist/api/runtime/global-default.d.ts.map +1 -1
- package/dist/api/runtime/global-default.js.map +1 -1
- package/dist/api/runtime/provider-defaults.d.ts.map +1 -1
- package/dist/api/runtime/provider-defaults.js +10 -4
- package/dist/api/runtime/provider-defaults.js.map +1 -1
- package/dist/api/runtime/tool-emulation/history.d.ts +16 -0
- package/dist/api/runtime/tool-emulation/history.d.ts.map +1 -0
- package/dist/api/runtime/tool-emulation/history.js +89 -0
- package/dist/api/runtime/tool-emulation/history.js.map +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/index.js +1 -0
- package/dist/api/runtime/tool-emulation/index.js.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.d.ts +6 -0
- package/dist/api/runtime/tool-emulation/loop.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.js +1 -0
- package/dist/api/runtime/tool-emulation/loop.js.map +1 -1
- package/dist/api/sessionTranscript.d.ts +34 -0
- package/dist/api/sessionTranscript.d.ts.map +1 -1
- package/dist/api/sessionTranscript.js +61 -1
- package/dist/api/sessionTranscript.js.map +1 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +120 -24
- package/dist/api/streamText.js.map +1 -1
- package/dist/api/types/AgentOSInput.d.ts +11 -0
- package/dist/api/types/AgentOSInput.d.ts.map +1 -1
- package/dist/api/types.d.ts +14 -6
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts +13 -4
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.js +32 -23
- package/dist/cognition/emergent/AdaptPersonalityTool.js.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts +18 -6
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.js.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts +10 -3
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.js +3 -2
- package/dist/cognition/emergent/SelfEvaluateTool.js.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts +51 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.js +84 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.js.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts +8 -4
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.js +17 -5
- package/dist/cognition/substrate/ConversationHistoryManager.js.map +1 -1
- package/dist/cognition/substrate/GMI.d.ts +92 -5
- package/dist/cognition/substrate/GMI.d.ts.map +1 -1
- package/dist/cognition/substrate/GMI.js +291 -65
- package/dist/cognition/substrate/GMI.js.map +1 -1
- package/dist/cognition/substrate/GMIManager.d.ts +12 -0
- package/dist/cognition/substrate/GMIManager.d.ts.map +1 -1
- package/dist/cognition/substrate/GMIManager.js +22 -16
- package/dist/cognition/substrate/GMIManager.js.map +1 -1
- package/dist/cognition/substrate/IGMI.d.ts +23 -0
- package/dist/cognition/substrate/IGMI.d.ts.map +1 -1
- package/dist/cognition/substrate/IGMI.js.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.d.ts +183 -28
- package/dist/cognition/substrate/MetapromptExecutor.d.ts.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.js +431 -108
- package/dist/cognition/substrate/MetapromptExecutor.js.map +1 -1
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts +5 -0
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.js +30 -0
- package/dist/cognition/substrate/personas/PersonaValidation.js.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts +14 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.js +45 -12
- package/dist/cognition/substrate/personas/SoulLoader.js.map +1 -1
- package/dist/cognition/substrate/personas/definitions/default_assistant_persona.json +3 -35
- package/dist/cognition/substrate/personas/hexaco.d.ts +34 -0
- package/dist/cognition/substrate/personas/hexaco.d.ts.map +1 -0
- package/dist/cognition/substrate/personas/hexaco.js +66 -0
- package/dist/cognition/substrate/personas/hexaco.js.map +1 -0
- package/dist/cognition/substrate/userFeedback.d.ts +69 -0
- package/dist/cognition/substrate/userFeedback.d.ts.map +1 -0
- package/dist/cognition/substrate/userFeedback.js +107 -0
- package/dist/cognition/substrate/userFeedback.js.map +1 -0
- package/dist/core/conversation/ConversationMessage.d.ts +9 -0
- package/dist/core/conversation/ConversationMessage.d.ts.map +1 -1
- package/dist/core/conversation/ConversationMessage.js.map +1 -1
- package/dist/core/llm/IPromptEngine.d.ts +9 -0
- package/dist/core/llm/IPromptEngine.d.ts.map +1 -1
- package/dist/core/llm/IPromptEngine.js.map +1 -1
- package/dist/core/llm/PromptEngine.d.ts +30 -1
- package/dist/core/llm/PromptEngine.d.ts.map +1 -1
- package/dist/core/llm/PromptEngine.js +229 -92
- package/dist/core/llm/PromptEngine.js.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts +20 -0
- package/dist/core/llm/providers/AIModelProviderManager.d.ts.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.js +35 -5
- package/dist/core/llm/providers/AIModelProviderManager.js.map +1 -1
- package/dist/core/llm/providers/IProvider.d.ts +28 -8
- package/dist/core/llm/providers/IProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts +135 -49
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +323 -114
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js +15 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js +51 -24
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts +25 -6
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.js +108 -90
- package/dist/core/llm/providers/implementations/GeminiProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts +11 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.js +32 -2
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OllamaProvider.js +7 -7
- package/dist/core/llm/providers/implementations/OllamaProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts +53 -2
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.js +84 -17
- package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js +8 -3
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts +3 -3
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.js +25 -13
- package/dist/core/llm/providers/implementations/TogetherProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts +15 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js +12 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.d.ts +24 -9
- package/dist/core/llm/providers/model-cache-capabilities.d.ts.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.js +59 -44
- package/dist/core/llm/providers/model-cache-capabilities.js.map +1 -1
- package/dist/core/llm/providers/model-effort.d.ts +14 -0
- package/dist/core/llm/providers/model-effort.d.ts.map +1 -1
- package/dist/core/llm/providers/model-effort.js +22 -0
- package/dist/core/llm/providers/model-effort.js.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts +13 -6
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.js +14 -7
- package/dist/core/llm/providers/model-forced-tool-choice.js.map +1 -1
- package/dist/core/llm/providers/model-thinking.d.ts +64 -13
- package/dist/core/llm/providers/model-thinking.d.ts.map +1 -1
- package/dist/core/llm/providers/model-thinking.js +52 -12
- package/dist/core/llm/providers/model-thinking.js.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.d.ts +26 -7
- package/dist/core/llm/providers/openrouter-only-params.d.ts.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.js +58 -9
- package/dist/core/llm/providers/openrouter-only-params.js.map +1 -1
- package/dist/core/llm/providers/url-secrets.d.ts +42 -0
- package/dist/core/llm/providers/url-secrets.d.ts.map +1 -0
- package/dist/core/llm/providers/url-secrets.js +64 -0
- package/dist/core/llm/providers/url-secrets.js.map +1 -0
- package/dist/core/llm/streaming/StreamingBatcher.d.ts +4 -3
- package/dist/core/llm/streaming/StreamingBatcher.d.ts.map +1 -1
- package/dist/core/llm/streaming/StreamingBatcher.js +8 -3
- package/dist/core/llm/streaming/StreamingBatcher.js.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts +4 -0
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.js +26 -0
- package/dist/core/safety/LLMProviderHealthRegistry.js.map +1 -1
- package/dist/core/utils/sha256.d.ts +19 -0
- package/dist/core/utils/sha256.d.ts.map +1 -0
- package/dist/core/utils/sha256.js +101 -0
- package/dist/core/utils/sha256.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration/pipeline/query/types.js +3 -3
- package/dist/orchestration/pipeline/query/types.js.map +1 -1
- package/dist/orchestration/planning/ProviderAssignmentEngine.js +2 -2
- package/dist/orchestration/planning/ProviderAssignmentEngine.js.map +1 -1
- package/package.json +1 -1
|
@@ -13,15 +13,42 @@
|
|
|
13
13
|
*
|
|
14
14
|
* @module cognitive_substrate/MetapromptExecutor
|
|
15
15
|
*/
|
|
16
|
-
import { GMIMood,
|
|
16
|
+
import { GMIMood, } from './IGMI.js';
|
|
17
17
|
import { GMIEventType } from './GMIEvent.js';
|
|
18
18
|
import { GMIError, GMIErrorCode, createGMIErrorFromError } from '../../core/utils/errors.js';
|
|
19
|
+
/** Longest time {@link MetapromptExecutor.drain} waits by default, in milliseconds. */
|
|
20
|
+
const DEFAULT_DRAIN_TIMEOUT_MS = 5000;
|
|
21
|
+
/**
|
|
22
|
+
* Longest a queued unit of metaprompt work may run before the queue moves on
|
|
23
|
+
* without it, in milliseconds. Each metaprompt LLM call also carries it as its
|
|
24
|
+
* request timeout, so a stalled provider call is normally cut off first.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_RUN_TIMEOUT_MS = 120000;
|
|
27
|
+
/**
|
|
28
|
+
* Working-memory keys that hold the GMI's own state. A metaprompt changes
|
|
29
|
+
* mood, user and task context through its update fields, which the GMI
|
|
30
|
+
* persists under these keys; an imprint that wrote them would bypass that
|
|
31
|
+
* path (and race it after a deadline) or fire a manual trigger.
|
|
32
|
+
*/
|
|
33
|
+
function isGmiStateKey(key) {
|
|
34
|
+
return (key === 'currentGmiMood' ||
|
|
35
|
+
key === 'currentUserContext' ||
|
|
36
|
+
key === 'currentTaskContext' ||
|
|
37
|
+
key === 'gmi_sentiment_history' ||
|
|
38
|
+
key.startsWith('manual_trigger_') ||
|
|
39
|
+
key.startsWith('metaprompt_turn_counter_'));
|
|
40
|
+
}
|
|
19
41
|
/**
|
|
20
42
|
* Handles metaprompt trigger checking, execution, and state application.
|
|
21
43
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
44
|
+
* The metaprompts that fire on one turn are queued as one background batch.
|
|
45
|
+
* Batches and manual self-reflection cycles run one at a time, in the order
|
|
46
|
+
* they were queued, so two of them never race on the mood and context they
|
|
47
|
+
* update, and the turn that triggered a batch does not wait for its LLM calls.
|
|
48
|
+
* Metaprompt work never changes the GMI's lifecycle state; that state belongs
|
|
49
|
+
* to the turn loop.
|
|
50
|
+
*
|
|
51
|
+
* Owns the per-metaprompt turn counters used by `turn_interval` triggers.
|
|
25
52
|
*
|
|
26
53
|
* All GMI state mutations flow back through callbacks so the executor never
|
|
27
54
|
* directly mutates GMI internals.
|
|
@@ -36,6 +63,29 @@ export class MetapromptExecutor {
|
|
|
36
63
|
this.config = config;
|
|
37
64
|
/** Per-metaprompt turn counters for `turn_interval` triggers. */
|
|
38
65
|
this.metaPromptTriggerCounters = new Map();
|
|
66
|
+
/**
|
|
67
|
+
* Tail of the metaprompt work queue. Every batch and self-reflection cycle
|
|
68
|
+
* chains onto it, so only one runs at a time. It never rejects.
|
|
69
|
+
*/
|
|
70
|
+
this.queueTail = Promise.resolve();
|
|
71
|
+
/** Number of batches and self-reflection cycles that are queued or running. */
|
|
72
|
+
this.inFlight = 0;
|
|
73
|
+
/** Metaprompt ids already reported as unable to fire, so each is reported once. */
|
|
74
|
+
this.invalidTriggerWarnings = new Set();
|
|
75
|
+
/** Set by {@link MetapromptExecutor.close}: no further work starts, and late results are discarded. */
|
|
76
|
+
this.closed = false;
|
|
77
|
+
/** User turns counted so far, to tell whether a queued event batch still belongs to the latest turn. */
|
|
78
|
+
this.countedTurns = 0;
|
|
79
|
+
/** Deadline for each queued unit of work, in milliseconds (see DEFAULT_RUN_TIMEOUT_MS). */
|
|
80
|
+
this.runTimeoutMs = DEFAULT_RUN_TIMEOUT_MS;
|
|
81
|
+
/**
|
|
82
|
+
* The last memory imprint write queued for each key. Writes to one key run
|
|
83
|
+
* in the order they were made: a unit that passes its deadline mid-write
|
|
84
|
+
* releases the queue, but a later unit's write to the same key waits for
|
|
85
|
+
* that write, so it is never overwritten by it. Writes to other keys do
|
|
86
|
+
* not wait, so one stalled write cannot hold back the rest.
|
|
87
|
+
*/
|
|
88
|
+
this.writeTails = new Map();
|
|
39
89
|
/** Turns elapsed since the last self-reflection. */
|
|
40
90
|
this.turnsSinceLastReflection = 0;
|
|
41
91
|
this.selfReflectionIntervalTurns = 5; // Will be overridden by initialize
|
|
@@ -44,62 +94,199 @@ export class MetapromptExecutor {
|
|
|
44
94
|
// Public API
|
|
45
95
|
// ---------------------------------------------------------------------------
|
|
46
96
|
/**
|
|
47
|
-
* Checks all metaprompt triggers and
|
|
97
|
+
* Checks all metaprompt triggers and queues any that fire.
|
|
48
98
|
*
|
|
49
99
|
* Iterates through the persona's metaprompt definitions, evaluating each
|
|
50
100
|
* trigger type:
|
|
51
|
-
* - `turn_interval`:
|
|
101
|
+
* - `turn_interval`: counts user turns and fires on every `intervalTurns`-th
|
|
102
|
+
* one, so an interval of N fires on user turns N, 2N, 3N and so on.
|
|
52
103
|
* - `event_based`: fires if the event type is in the pending events set.
|
|
53
104
|
* - `manual`: fires if a flag was set in working memory.
|
|
54
105
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
106
|
+
* The metaprompts that fire are queued as one background batch and this
|
|
107
|
+
* method returns without waiting for their LLM calls. Batches run one at a
|
|
108
|
+
* time; inside a batch the metaprompts run in parallel via
|
|
109
|
+
* `Promise.allSettled`. Execution errors are logged to the reasoning trace
|
|
110
|
+
* and never reach the turn.
|
|
57
111
|
*
|
|
58
112
|
* @param turnId - The current turn identifier (for tracing).
|
|
113
|
+
* @param options - `countTurn` says whether this turn counts toward
|
|
114
|
+
* `turn_interval` triggers. Pass `false` for turns that are not user
|
|
115
|
+
* messages, such as tool continuations and system turns. Defaults to `true`.
|
|
59
116
|
*/
|
|
60
|
-
async checkAndTriggerMetaprompts(turnId) {
|
|
117
|
+
async checkAndTriggerMetaprompts(turnId, options = {}) {
|
|
61
118
|
const persona = this.config.getPersona();
|
|
62
119
|
if (!persona.metaPrompts || persona.metaPrompts.length === 0) {
|
|
63
120
|
return;
|
|
64
121
|
}
|
|
122
|
+
const countTurn = options.countTurn !== false;
|
|
123
|
+
if (countTurn)
|
|
124
|
+
this.countedTurns += 1;
|
|
125
|
+
const queuedAtTurn = this.countedTurns;
|
|
65
126
|
const triggeredMetaPrompts = [];
|
|
66
127
|
const pendingEvents = this.config.getPendingEvents();
|
|
67
128
|
for (const metaPrompt of persona.metaPrompts) {
|
|
68
|
-
|
|
129
|
+
const trigger = metaPrompt.trigger;
|
|
130
|
+
if (!trigger)
|
|
69
131
|
continue;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
132
|
+
switch (trigger.type) {
|
|
133
|
+
case 'turn_interval':
|
|
134
|
+
if (countTurn && (await this.advanceTurnInterval(metaPrompt, trigger.intervalTurns))) {
|
|
135
|
+
triggeredMetaPrompts.push(metaPrompt);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
case 'event_based': {
|
|
139
|
+
const eventName = trigger.eventName;
|
|
140
|
+
if (pendingEvents.has(eventName)) {
|
|
141
|
+
triggeredMetaPrompts.push(metaPrompt);
|
|
142
|
+
pendingEvents.delete(eventName);
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
75
145
|
}
|
|
76
|
-
|
|
77
|
-
await this.
|
|
146
|
+
case 'manual': {
|
|
147
|
+
const manualFlag = await this.config.workingMemory.get(`manual_trigger_${metaPrompt.id}`);
|
|
148
|
+
if (manualFlag) {
|
|
149
|
+
triggeredMetaPrompts.push(metaPrompt);
|
|
150
|
+
await this.config.workingMemory.delete(`manual_trigger_${metaPrompt.id}`);
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
78
153
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
pendingEvents.delete(eventName);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
else if (metaPrompt.trigger.type === 'manual') {
|
|
88
|
-
const manualFlag = await this.config.workingMemory.get(`manual_trigger_${metaPrompt.id}`);
|
|
89
|
-
if (manualFlag) {
|
|
90
|
-
triggeredMetaPrompts.push(metaPrompt);
|
|
91
|
-
await this.config.workingMemory.delete(`manual_trigger_${metaPrompt.id}`);
|
|
154
|
+
default: {
|
|
155
|
+
// Persona JSON is cast rather than type-checked, so other trigger
|
|
156
|
+
// types reach this point at runtime. Nothing ever fires them.
|
|
157
|
+
const unsupportedType = String(trigger.type);
|
|
158
|
+
this.warnInvalidTrigger(metaPrompt, `trigger type '${unsupportedType}' is not supported (expected turn_interval, event_based or manual)`);
|
|
92
159
|
}
|
|
93
160
|
}
|
|
94
161
|
}
|
|
95
162
|
if (triggeredMetaPrompts.length > 0) {
|
|
96
163
|
this.config.addTraceEntry('SELF_REFLECTION_TRIGGERED', `${triggeredMetaPrompts.length} metaprompt(s) triggered`, { ids: triggeredMetaPrompts.map((m) => m.id), turnId });
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
164
|
+
// Queue the batch and return: the turn does not wait for metaprompt LLM calls.
|
|
165
|
+
void this.enqueue('Metaprompt execution', (run) => {
|
|
166
|
+
// An event-based metaprompt reacts to the turn that raised the event
|
|
167
|
+
// (a frustrated or confused message). If a newer user turn has been
|
|
168
|
+
// counted while this batch waited behind a slow one, that evidence is
|
|
169
|
+
// gone, so it is dropped rather than applied to a later mood.
|
|
170
|
+
const current = this.countedTurns === queuedAtTurn;
|
|
171
|
+
const runnable = current
|
|
172
|
+
? triggeredMetaPrompts
|
|
173
|
+
: triggeredMetaPrompts.filter((mp) => mp.trigger?.type !== 'event_based');
|
|
174
|
+
if (runnable.length < triggeredMetaPrompts.length) {
|
|
175
|
+
this.config.addTraceEntry('DEBUG', 'Skipped event-based metaprompts whose triggering turn has passed.', {
|
|
176
|
+
ids: triggeredMetaPrompts.filter((mp) => !runnable.includes(mp)).map((mp) => mp.id),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return this.executeMetaprompts(runnable, run);
|
|
100
180
|
});
|
|
101
181
|
}
|
|
102
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* Reports whether a metaprompt batch or self-reflection cycle is queued or running.
|
|
185
|
+
*
|
|
186
|
+
* @returns `true` while any metaprompt work is pending.
|
|
187
|
+
*/
|
|
188
|
+
isRunning() {
|
|
189
|
+
// A unit released at its deadline can leave a memory write running.
|
|
190
|
+
return this.inFlight > 0 || this.writeTails.size > 0;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolves once every queued metaprompt batch and self-reflection cycle has
|
|
194
|
+
* settled, including work queued while waiting. Never rejects.
|
|
195
|
+
*/
|
|
196
|
+
async whenIdle() {
|
|
197
|
+
let tail;
|
|
198
|
+
do {
|
|
199
|
+
tail = this.queueTail;
|
|
200
|
+
await tail;
|
|
201
|
+
// Memory writes a unit started before its deadline outlive the unit.
|
|
202
|
+
await Promise.all(this.writeTails.values());
|
|
203
|
+
} while (tail !== this.queueTail || this.writeTails.size > 0);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Waits for queued and running metaprompt work to settle, for at most
|
|
207
|
+
* `timeoutMs`. The GMI calls this during shutdown so that a batch still in
|
|
208
|
+
* flight can store its updates before working memory closes, while a
|
|
209
|
+
* provider call that never settles cannot hold shutdown open.
|
|
210
|
+
*
|
|
211
|
+
* @param timeoutMs - Longest wait, in milliseconds.
|
|
212
|
+
* @returns `true` when all work settled, `false` when the wait timed out.
|
|
213
|
+
*/
|
|
214
|
+
async drain(timeoutMs = DEFAULT_DRAIN_TIMEOUT_MS) {
|
|
215
|
+
if (!this.isRunning())
|
|
216
|
+
return true;
|
|
217
|
+
let resolveTimedOut;
|
|
218
|
+
const timedOut = new Promise((resolve) => {
|
|
219
|
+
resolveTimedOut = resolve;
|
|
220
|
+
});
|
|
221
|
+
const timer = setTimeout(() => resolveTimedOut(false), timeoutMs);
|
|
222
|
+
// Waiting on a hung provider call must not keep the process alive.
|
|
223
|
+
timer.unref?.();
|
|
224
|
+
try {
|
|
225
|
+
return await Promise.race([this.whenIdle().then(() => true), timedOut]);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Stops the queue for good, as the GMI shuts down: work that has not started
|
|
233
|
+
* is skipped, and results of work still running are discarded instead of
|
|
234
|
+
* applied to memories that are closing.
|
|
235
|
+
*/
|
|
236
|
+
close() {
|
|
237
|
+
this.closed = true;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Chains one unit of metaprompt work onto the queue, so it starts only after
|
|
241
|
+
* all earlier work has settled. A failure is logged to the console and the
|
|
242
|
+
* reasoning trace, and the queue moves on to the next unit.
|
|
243
|
+
*
|
|
244
|
+
* @param label - Name used in failure logs (for example 'Metaprompt execution').
|
|
245
|
+
* @param task - The work to run.
|
|
246
|
+
* @returns A promise that settles when this unit finishes. It never rejects.
|
|
247
|
+
*/
|
|
248
|
+
enqueue(label, task) {
|
|
249
|
+
this.inFlight += 1;
|
|
250
|
+
let overdue = false;
|
|
251
|
+
const run = { isStale: () => overdue || this.closed };
|
|
252
|
+
const unit = async () => {
|
|
253
|
+
if (this.closed)
|
|
254
|
+
return;
|
|
255
|
+
// A provider call that never settles would hold the queue forever;
|
|
256
|
+
// past the deadline the queue moves on, and the unit's late results
|
|
257
|
+
// are discarded (run.isStale()).
|
|
258
|
+
const timeoutMs = this.runTimeoutMs;
|
|
259
|
+
let timer;
|
|
260
|
+
const deadline = new Promise((resolve) => {
|
|
261
|
+
timer = setTimeout(() => resolve('timeout'), timeoutMs);
|
|
262
|
+
timer.unref?.();
|
|
263
|
+
});
|
|
264
|
+
try {
|
|
265
|
+
const outcome = await Promise.race([task(run).then(() => 'done'), deadline]);
|
|
266
|
+
if (outcome === 'timeout') {
|
|
267
|
+
overdue = true;
|
|
268
|
+
this.config.addTraceEntry('WARNING', `${label} did not finish within ${timeoutMs}ms; its results will be discarded.`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
if (timer)
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const settled = this.queueTail
|
|
277
|
+
.then(unit)
|
|
278
|
+
.catch((error) => {
|
|
279
|
+
console.error(`GMI (ID: ${this.config.getGmiId()}): ${label} failed:`, error);
|
|
280
|
+
this.config.addTraceEntry('ERROR', `${label} failed`, {
|
|
281
|
+
error: error instanceof Error ? error.message : String(error),
|
|
282
|
+
});
|
|
283
|
+
})
|
|
284
|
+
.finally(() => {
|
|
285
|
+
this.inFlight -= 1;
|
|
286
|
+
});
|
|
287
|
+
this.queueTail = settled;
|
|
288
|
+
return settled;
|
|
289
|
+
}
|
|
103
290
|
/**
|
|
104
291
|
* Gets the turn counter for a specific metaprompt.
|
|
105
292
|
*
|
|
@@ -124,9 +311,7 @@ export class MetapromptExecutor {
|
|
|
124
311
|
*/
|
|
125
312
|
async incrementMetapromptTurnCounter(metapromptId) {
|
|
126
313
|
const current = await this.getMetapromptTurnCounter(metapromptId);
|
|
127
|
-
|
|
128
|
-
this.metaPromptTriggerCounters.set(metapromptId, newValue);
|
|
129
|
-
await this.config.workingMemory.set(`metaprompt_turn_counter_${metapromptId}`, newValue);
|
|
314
|
+
await this.setMetapromptTurnCounter(metapromptId, current + 1);
|
|
130
315
|
}
|
|
131
316
|
/**
|
|
132
317
|
* Resets the turn counter for a specific metaprompt to zero.
|
|
@@ -134,25 +319,85 @@ export class MetapromptExecutor {
|
|
|
134
319
|
* @param metapromptId - The metaprompt identifier.
|
|
135
320
|
*/
|
|
136
321
|
async resetMetapromptTurnCounter(metapromptId) {
|
|
137
|
-
this.
|
|
138
|
-
await this.config.workingMemory.set(`metaprompt_turn_counter_${metapromptId}`, 0);
|
|
322
|
+
await this.setMetapromptTurnCounter(metapromptId, 0);
|
|
139
323
|
}
|
|
140
324
|
/**
|
|
141
|
-
*
|
|
325
|
+
* Sets the turn counter for a specific metaprompt, in memory and in working
|
|
326
|
+
* memory, where it survives across GMI instances.
|
|
142
327
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
328
|
+
* @param metapromptId - The metaprompt identifier.
|
|
329
|
+
* @param value - The number of counted user turns since the metaprompt last fired.
|
|
330
|
+
*/
|
|
331
|
+
async setMetapromptTurnCounter(metapromptId, value) {
|
|
332
|
+
this.metaPromptTriggerCounters.set(metapromptId, value);
|
|
333
|
+
await this.config.workingMemory.set(`metaprompt_turn_counter_${metapromptId}`, value);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Counts one user turn toward a `turn_interval` metaprompt and reports
|
|
337
|
+
* whether the metaprompt fires on it.
|
|
338
|
+
*
|
|
339
|
+
* The counter holds the user turns counted since the metaprompt last fired.
|
|
340
|
+
* It is incremented first, so an interval of N fires on user turns N, 2N,
|
|
341
|
+
* 3N and so on, and it resets to zero when the metaprompt fires. A counter
|
|
342
|
+
* already above the interval fires on the next counted turn.
|
|
343
|
+
*
|
|
344
|
+
* Persona JSON is not type-checked, so the interval is validated here: a
|
|
345
|
+
* value that is not a finite number of at least 1 never fires and is
|
|
346
|
+
* reported once through `warnInvalidTrigger`.
|
|
347
|
+
*
|
|
348
|
+
* @param metaPrompt - The metaprompt whose counter advances.
|
|
349
|
+
* @param intervalTurns - The trigger's `intervalTurns`, as read from the persona.
|
|
350
|
+
* @returns `true` when the metaprompt fires on this turn.
|
|
351
|
+
*/
|
|
352
|
+
async advanceTurnInterval(metaPrompt, intervalTurns) {
|
|
353
|
+
if (typeof intervalTurns !== 'number' || !Number.isFinite(intervalTurns) || intervalTurns < 1) {
|
|
354
|
+
this.warnInvalidTrigger(metaPrompt, `intervalTurns must be a number >= 1 (got ${String(intervalTurns)})`);
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
const counted = (await this.getMetapromptTurnCounter(metaPrompt.id)) + 1;
|
|
358
|
+
if (counted >= Math.floor(intervalTurns)) {
|
|
359
|
+
await this.resetMetapromptTurnCounter(metaPrompt.id);
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
await this.setMetapromptTurnCounter(metaPrompt.id, counted);
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Reports a metaprompt whose trigger can never fire. Each metaprompt id is
|
|
367
|
+
* reported once per executor, with one WARNING trace entry and one console
|
|
368
|
+
* warning, so a misconfigured persona does not flood the trace every turn.
|
|
369
|
+
*
|
|
370
|
+
* @param metaPrompt - The metaprompt that will not run.
|
|
371
|
+
* @param reason - Why its trigger cannot fire.
|
|
372
|
+
*/
|
|
373
|
+
warnInvalidTrigger(metaPrompt, reason) {
|
|
374
|
+
if (this.invalidTriggerWarnings.has(metaPrompt.id))
|
|
375
|
+
return;
|
|
376
|
+
this.invalidTriggerWarnings.add(metaPrompt.id);
|
|
377
|
+
const message = `Metaprompt '${metaPrompt.id}' will not run: ${reason}.`;
|
|
378
|
+
console.warn(`GMI (ID: ${this.config.getGmiId()}): ${message}`);
|
|
379
|
+
this.config.addTraceEntry('WARNING', message, {
|
|
380
|
+
metapromptId: metaPrompt.id,
|
|
381
|
+
trigger: metaPrompt.trigger,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Executes one batch of metaprompts in parallel using `Promise.allSettled`.
|
|
386
|
+
*
|
|
387
|
+
* A failed metaprompt is recorded as an ERROR trace entry and does not stop
|
|
388
|
+
* the others. The batch never changes the GMI's lifecycle state: it runs in
|
|
389
|
+
* the background, so the turn that triggered it may have finished and a
|
|
390
|
+
* later turn may be in progress. {@link MetapromptExecutor.checkAndTriggerMetaprompts}
|
|
391
|
+
* queues batches so that they run one at a time.
|
|
145
392
|
*
|
|
146
393
|
* @param metaPrompts - The metaprompt definitions to execute.
|
|
147
394
|
*/
|
|
148
|
-
async executeMetaprompts(metaPrompts) {
|
|
395
|
+
async executeMetaprompts(metaPrompts, run) {
|
|
149
396
|
if (metaPrompts.length === 0)
|
|
150
397
|
return;
|
|
151
|
-
const previousState = this.config.getState();
|
|
152
|
-
this.config.setState(GMIPrimeState.REFLECTING);
|
|
153
398
|
this.config.addTraceEntry('SELF_REFLECTION_START', `Executing ${metaPrompts.length} metaprompt(s)`, { ids: metaPrompts.map((m) => m.id) });
|
|
154
399
|
try {
|
|
155
|
-
const results = await Promise.allSettled(metaPrompts.map((mp) => this.executeMetapromptHandler(mp)));
|
|
400
|
+
const results = await Promise.allSettled(metaPrompts.map((mp) => this.executeMetapromptHandler(mp, run)));
|
|
156
401
|
results.forEach((result, idx) => {
|
|
157
402
|
if (result.status === 'rejected') {
|
|
158
403
|
this.config.addTraceEntry('ERROR', `Metaprompt '${metaPrompts[idx].id}' failed: ${result.reason}`, { error: result.reason });
|
|
@@ -164,11 +409,6 @@ export class MetapromptExecutor {
|
|
|
164
409
|
this.config.addTraceEntry('ERROR', gmiError.message, gmiError.toPlainObject());
|
|
165
410
|
}
|
|
166
411
|
finally {
|
|
167
|
-
const disallowedStates = new Set([
|
|
168
|
-
GMIPrimeState.IDLE,
|
|
169
|
-
GMIPrimeState.INITIALIZING,
|
|
170
|
-
]);
|
|
171
|
-
this.config.setState(disallowedStates.has(previousState) ? GMIPrimeState.READY : previousState);
|
|
172
412
|
this.config.addTraceEntry('SELF_REFLECTION_COMPLETE', 'Metaprompt execution cycle complete');
|
|
173
413
|
}
|
|
174
414
|
}
|
|
@@ -180,22 +420,22 @@ export class MetapromptExecutor {
|
|
|
180
420
|
*
|
|
181
421
|
* @param metaPrompt - The metaprompt definition to execute.
|
|
182
422
|
*/
|
|
183
|
-
async executeMetapromptHandler(metaPrompt) {
|
|
423
|
+
async executeMetapromptHandler(metaPrompt, run) {
|
|
184
424
|
switch (metaPrompt.id) {
|
|
185
425
|
case 'gmi_self_trait_adjustment':
|
|
186
|
-
return this.handleTraitAdjustment(metaPrompt);
|
|
426
|
+
return this.handleTraitAdjustment(metaPrompt, run);
|
|
187
427
|
case 'gmi_frustration_recovery':
|
|
188
|
-
return this.handleFrustrationRecovery(metaPrompt);
|
|
428
|
+
return this.handleFrustrationRecovery(metaPrompt, run);
|
|
189
429
|
case 'gmi_confusion_clarification':
|
|
190
|
-
return this.handleConfusionClarification(metaPrompt);
|
|
430
|
+
return this.handleConfusionClarification(metaPrompt, run);
|
|
191
431
|
case 'gmi_satisfaction_reinforcement':
|
|
192
|
-
return this.handleSatisfactionReinforcement(metaPrompt);
|
|
432
|
+
return this.handleSatisfactionReinforcement(metaPrompt, run);
|
|
193
433
|
case 'gmi_error_recovery':
|
|
194
|
-
return this.handleErrorRecovery(metaPrompt);
|
|
434
|
+
return this.handleErrorRecovery(metaPrompt, run);
|
|
195
435
|
case 'gmi_engagement_boost':
|
|
196
|
-
return this.handleEngagementBoost(metaPrompt);
|
|
436
|
+
return this.handleEngagementBoost(metaPrompt, run);
|
|
197
437
|
default:
|
|
198
|
-
return this.handleGenericMetaprompt(metaPrompt);
|
|
438
|
+
return this.handleGenericMetaprompt(metaPrompt, run);
|
|
199
439
|
}
|
|
200
440
|
}
|
|
201
441
|
/**
|
|
@@ -206,7 +446,7 @@ export class MetapromptExecutor {
|
|
|
206
446
|
*
|
|
207
447
|
* @param metaPrompt - The metaprompt definition.
|
|
208
448
|
*/
|
|
209
|
-
async handleTraitAdjustment(metaPrompt) {
|
|
449
|
+
async handleTraitAdjustment(metaPrompt, run) {
|
|
210
450
|
const evidenceHistory = this.config.getConversationHistory().slice(-10);
|
|
211
451
|
const evidenceTrace = this.config.getReasoningTraceEntries().slice(-20);
|
|
212
452
|
const evidence = {
|
|
@@ -222,15 +462,15 @@ export class MetapromptExecutor {
|
|
|
222
462
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
223
463
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
224
464
|
};
|
|
225
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
226
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
465
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
466
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
227
467
|
}
|
|
228
468
|
/**
|
|
229
469
|
* Handler for the frustration recovery metaprompt.
|
|
230
470
|
*
|
|
231
471
|
* @param metaPrompt - The metaprompt definition.
|
|
232
472
|
*/
|
|
233
|
-
async handleFrustrationRecovery(metaPrompt) {
|
|
473
|
+
async handleFrustrationRecovery(metaPrompt, run) {
|
|
234
474
|
const sentimentHistory = await this.config.workingMemory.get('gmi_sentiment_history');
|
|
235
475
|
const recentErrors = this.config.getReasoningTraceEntries()
|
|
236
476
|
.slice(-10)
|
|
@@ -245,15 +485,15 @@ export class MetapromptExecutor {
|
|
|
245
485
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
246
486
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
247
487
|
};
|
|
248
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
249
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
488
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
489
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
250
490
|
}
|
|
251
491
|
/**
|
|
252
492
|
* Handler for the confusion clarification metaprompt.
|
|
253
493
|
*
|
|
254
494
|
* @param metaPrompt - The metaprompt definition.
|
|
255
495
|
*/
|
|
256
|
-
async handleConfusionClarification(metaPrompt) {
|
|
496
|
+
async handleConfusionClarification(metaPrompt, run) {
|
|
257
497
|
const sentimentHistory = await this.config.workingMemory.get('gmi_sentiment_history');
|
|
258
498
|
const lastConfusionEvent = this.config.getEventHistory()
|
|
259
499
|
.slice()
|
|
@@ -270,15 +510,15 @@ export class MetapromptExecutor {
|
|
|
270
510
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
271
511
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
272
512
|
};
|
|
273
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
274
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
513
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
514
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
275
515
|
}
|
|
276
516
|
/**
|
|
277
517
|
* Handler for the satisfaction reinforcement metaprompt.
|
|
278
518
|
*
|
|
279
519
|
* @param metaPrompt - The metaprompt definition.
|
|
280
520
|
*/
|
|
281
|
-
async handleSatisfactionReinforcement(metaPrompt) {
|
|
521
|
+
async handleSatisfactionReinforcement(metaPrompt, run) {
|
|
282
522
|
const sentimentHistory = await this.config.workingMemory.get('gmi_sentiment_history');
|
|
283
523
|
const variables = {
|
|
284
524
|
current_sentiment: this.config.getUserContext().currentSentiment || 'positive',
|
|
@@ -289,15 +529,15 @@ export class MetapromptExecutor {
|
|
|
289
529
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
290
530
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
291
531
|
};
|
|
292
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
293
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
532
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
533
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
294
534
|
}
|
|
295
535
|
/**
|
|
296
536
|
* Handler for the error recovery metaprompt.
|
|
297
537
|
*
|
|
298
538
|
* @param metaPrompt - The metaprompt definition.
|
|
299
539
|
*/
|
|
300
|
-
async handleErrorRecovery(metaPrompt) {
|
|
540
|
+
async handleErrorRecovery(metaPrompt, run) {
|
|
301
541
|
const recentErrors = this.config.getReasoningTraceEntries()
|
|
302
542
|
.slice(-10)
|
|
303
543
|
.filter((e) => e.type === 'ERROR');
|
|
@@ -308,15 +548,15 @@ export class MetapromptExecutor {
|
|
|
308
548
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
309
549
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
310
550
|
};
|
|
311
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
312
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
551
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
552
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
313
553
|
}
|
|
314
554
|
/**
|
|
315
555
|
* Handler for the engagement boost metaprompt.
|
|
316
556
|
*
|
|
317
557
|
* @param metaPrompt - The metaprompt definition.
|
|
318
558
|
*/
|
|
319
|
-
async handleEngagementBoost(metaPrompt) {
|
|
559
|
+
async handleEngagementBoost(metaPrompt, run) {
|
|
320
560
|
const sentimentHistory = await this.config.workingMemory.get('gmi_sentiment_history');
|
|
321
561
|
const variables = {
|
|
322
562
|
consecutive_neutral: (sentimentHistory?.consecutiveConfusion || 4).toString(),
|
|
@@ -325,8 +565,8 @@ export class MetapromptExecutor {
|
|
|
325
565
|
user_skill: this.config.getUserContext().skillLevel || 'unknown',
|
|
326
566
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
327
567
|
};
|
|
328
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
329
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
568
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
569
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
330
570
|
}
|
|
331
571
|
/**
|
|
332
572
|
* Generic handler for custom metaprompts that don't have a dedicated handler.
|
|
@@ -335,7 +575,7 @@ export class MetapromptExecutor {
|
|
|
335
575
|
*
|
|
336
576
|
* @param metaPrompt - The metaprompt definition.
|
|
337
577
|
*/
|
|
338
|
-
async handleGenericMetaprompt(metaPrompt) {
|
|
578
|
+
async handleGenericMetaprompt(metaPrompt, run) {
|
|
339
579
|
const variables = {
|
|
340
580
|
recent_conversation: JSON.stringify(this.config.getConversationHistory().slice(-5)),
|
|
341
581
|
recent_reasoning: JSON.stringify(this.config.getReasoningTraceEntries().slice(-10)),
|
|
@@ -344,22 +584,24 @@ export class MetapromptExecutor {
|
|
|
344
584
|
task_complexity: this.config.getTaskContext().complexity || 'unknown',
|
|
345
585
|
current_sentiment: this.config.getUserContext().currentSentiment || 'neutral',
|
|
346
586
|
};
|
|
347
|
-
const response = await this.executeMetapromptWithVariables(metaPrompt, variables);
|
|
348
|
-
await this.applyMetapromptUpdates(response, metaPrompt.id);
|
|
587
|
+
const response = await this.executeMetapromptWithVariables(metaPrompt, variables, run);
|
|
588
|
+
await this.applyMetapromptUpdates(response, metaPrompt.id, run);
|
|
349
589
|
}
|
|
350
590
|
/**
|
|
351
591
|
* Executes a metaprompt template with variable substitution and LLM call.
|
|
352
592
|
*
|
|
353
593
|
* 1. Extracts the template string from the metaprompt definition.
|
|
354
594
|
* 2. Substitutes `{{variable}}` placeholders with provided values.
|
|
355
|
-
* 3.
|
|
356
|
-
*
|
|
595
|
+
* 3. Resolves the model and provider: the metaprompt's own, then the
|
|
596
|
+
* persona defaults, then the GMI defaults.
|
|
597
|
+
* 4. Calls the LLM with JSON response format.
|
|
598
|
+
* 5. Parses the JSON response with LLM-based recovery via IUtilityAI.
|
|
357
599
|
*
|
|
358
600
|
* @param metaPrompt - The metaprompt definition.
|
|
359
601
|
* @param variables - Key-value pairs to substitute into the template.
|
|
360
602
|
* @returns The parsed JSON response from the LLM.
|
|
361
603
|
*/
|
|
362
|
-
async executeMetapromptWithVariables(metaPrompt, variables) {
|
|
604
|
+
async executeMetapromptWithVariables(metaPrompt, variables, run) {
|
|
363
605
|
let template;
|
|
364
606
|
if (typeof metaPrompt.promptTemplate === 'string') {
|
|
365
607
|
template = metaPrompt.promptTemplate;
|
|
@@ -371,27 +613,39 @@ export class MetapromptExecutor {
|
|
|
371
613
|
for (const [key, value] of Object.entries(variables)) {
|
|
372
614
|
finalPrompt = finalPrompt.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
|
|
373
615
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
throw new GMIError('No model or provider specified for metaprompt', GMIErrorCode.CONFIGURATION_ERROR);
|
|
379
|
-
}
|
|
616
|
+
// Same resolution as the self-reflection path: the metaprompt's own model
|
|
617
|
+
// and provider, then the persona defaults, then the GMI's configured
|
|
618
|
+
// defaults. Throws a CONFIGURATION_ERROR GMIError when nothing resolves.
|
|
619
|
+
const { modelId, providerId } = this.config.getModelAndProvider(metaPrompt.modelId, metaPrompt.providerId);
|
|
380
620
|
this.config.addTraceEntry('DEBUG', `Executing metaprompt '${metaPrompt.id}'`, { modelId, providerId });
|
|
381
621
|
const completionOptions = {
|
|
382
622
|
temperature: metaPrompt.temperature ?? 0.3,
|
|
383
623
|
maxTokens: metaPrompt.maxOutputTokens ?? 512,
|
|
384
624
|
responseFormat: { type: 'json_object' },
|
|
625
|
+
// Bounds a stalled provider call before the queue's own deadline does.
|
|
626
|
+
requestTimeout: this.runTimeoutMs,
|
|
385
627
|
};
|
|
386
628
|
const provider = this.config.llmProviderManager.getProvider(providerId);
|
|
387
629
|
if (!provider) {
|
|
388
630
|
throw new GMIError(`Provider '${providerId}' not found for metaprompt '${metaPrompt.id}'.`, GMIErrorCode.LLM_PROVIDER_UNAVAILABLE);
|
|
389
631
|
}
|
|
632
|
+
// A run can go stale while its handler awaits memory reads; it then
|
|
633
|
+
// starts no provider call whose answer would be discarded.
|
|
634
|
+
if (run?.isStale()) {
|
|
635
|
+
this.config.addTraceEntry('WARNING', `Discarded late results of metaprompt '${metaPrompt.id}'.`);
|
|
636
|
+
return null;
|
|
637
|
+
}
|
|
390
638
|
const result = await provider.generateCompletion(modelId, [{ role: 'user', content: finalPrompt }], completionOptions);
|
|
391
639
|
const responseContent = result.choices?.[0]?.message?.content;
|
|
392
640
|
if (!responseContent || typeof responseContent !== 'string') {
|
|
393
641
|
throw new GMIError(`Metaprompt '${metaPrompt.id}' returned no valid content.`, GMIErrorCode.LLM_PROVIDER_ERROR, { response: result });
|
|
394
642
|
}
|
|
643
|
+
// A run that went stale while the provider answered is discarded anyway,
|
|
644
|
+
// so no JSON repair call is made on its behalf.
|
|
645
|
+
if (run?.isStale()) {
|
|
646
|
+
this.config.addTraceEntry('WARNING', `Discarded late results of metaprompt '${metaPrompt.id}'.`);
|
|
647
|
+
return null;
|
|
648
|
+
}
|
|
395
649
|
const parseOptions = {
|
|
396
650
|
attemptFixWithLLM: true,
|
|
397
651
|
llmModelIdForFix: modelId,
|
|
@@ -408,10 +662,20 @@ export class MetapromptExecutor {
|
|
|
408
662
|
*
|
|
409
663
|
* @param updates - The parsed updates from the metaprompt LLM response.
|
|
410
664
|
* @param metapromptId - The ID of the metaprompt that produced these updates.
|
|
665
|
+
* @param run - The queued unit applying them; once it is stale, nothing
|
|
666
|
+
* more is applied.
|
|
667
|
+
* @returns Keys of the memory imprints stored.
|
|
411
668
|
*/
|
|
412
|
-
async applyMetapromptUpdates(updates, metapromptId) {
|
|
669
|
+
async applyMetapromptUpdates(updates, metapromptId, run) {
|
|
670
|
+
const storedImprintKeys = [];
|
|
413
671
|
if (!updates)
|
|
414
|
-
return;
|
|
672
|
+
return storedImprintKeys;
|
|
673
|
+
if (run?.isStale()) {
|
|
674
|
+
// The queue moved on without this unit, or the GMI shut down: its
|
|
675
|
+
// results would land on state that no longer matches its evidence.
|
|
676
|
+
this.config.addTraceEntry('WARNING', `Discarded late results of metaprompt '${metapromptId}'.`);
|
|
677
|
+
return storedImprintKeys;
|
|
678
|
+
}
|
|
415
679
|
let stateChanged = false;
|
|
416
680
|
// Mood update — GMIMood enum values are lowercase (e.g., 'focused'),
|
|
417
681
|
// but LLM responses may return either case. Normalize to lowercase for comparison.
|
|
@@ -440,13 +704,27 @@ export class MetapromptExecutor {
|
|
|
440
704
|
// Memory imprints
|
|
441
705
|
if (updates.newMemoryImprints && Array.isArray(updates.newMemoryImprints)) {
|
|
442
706
|
for (const imprint of updates.newMemoryImprints) {
|
|
707
|
+
// A write can outlast the run's deadline. Once the queue has moved
|
|
708
|
+
// on or the GMI has shut down, the remaining imprints would overwrite
|
|
709
|
+
// newer values or reach a closed memory, so they are dropped.
|
|
710
|
+
if (run?.isStale()) {
|
|
711
|
+
this.config.addTraceEntry('WARNING', `Discarded late results of metaprompt '${metapromptId}'.`);
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
if (imprint.key && isGmiStateKey(imprint.key)) {
|
|
715
|
+
this.config.addTraceEntry('WARNING', `Skipped memory imprint '${imprint.key}' from metaprompt '${metapromptId}': the key holds GMI state.`);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
443
718
|
if (imprint.key) {
|
|
444
|
-
await this.config.workingMemory.set(imprint.key, imprint.value);
|
|
719
|
+
const stored = await this.writeInOrder(run, imprint.key, () => this.config.workingMemory.set(imprint.key, imprint.value));
|
|
720
|
+
if (!stored) {
|
|
721
|
+
this.config.addTraceEntry('WARNING', `Discarded late results of metaprompt '${metapromptId}'.`);
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
storedImprintKeys.push(imprint.key);
|
|
725
|
+
stateChanged = true;
|
|
445
726
|
}
|
|
446
727
|
}
|
|
447
|
-
if (updates.newMemoryImprints.length > 0) {
|
|
448
|
-
stateChanged = true;
|
|
449
|
-
}
|
|
450
728
|
}
|
|
451
729
|
// Log state change
|
|
452
730
|
if (stateChanged) {
|
|
@@ -457,13 +735,43 @@ export class MetapromptExecutor {
|
|
|
457
735
|
rationale: updates.adjustmentRationale || updates.recoveryStrategy || updates.clarificationStrategy || updates.engagementStrategy || updates.mitigationStrategy,
|
|
458
736
|
});
|
|
459
737
|
}
|
|
738
|
+
return storedImprintKeys;
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* Runs one memory write after every write to the same key queued before
|
|
742
|
+
* it, unless `run` has gone stale by the time its turn comes.
|
|
743
|
+
*
|
|
744
|
+
* @param run The unit making the write.
|
|
745
|
+
* @param key The working-memory key written.
|
|
746
|
+
* @param write The write itself.
|
|
747
|
+
* @returns Whether the write ran.
|
|
748
|
+
*/
|
|
749
|
+
writeInOrder(run, key, write) {
|
|
750
|
+
const previous = this.writeTails.get(key) ?? Promise.resolve();
|
|
751
|
+
const result = previous.then(async () => {
|
|
752
|
+
if (run?.isStale())
|
|
753
|
+
return false;
|
|
754
|
+
await write();
|
|
755
|
+
return true;
|
|
756
|
+
});
|
|
757
|
+
const tail = result.then(() => undefined, () => undefined);
|
|
758
|
+
this.writeTails.set(key, tail);
|
|
759
|
+
// Forget the key once its last queued write settles.
|
|
760
|
+
void tail.then(() => {
|
|
761
|
+
if (this.writeTails.get(key) === tail)
|
|
762
|
+
this.writeTails.delete(key);
|
|
763
|
+
});
|
|
764
|
+
return result;
|
|
460
765
|
}
|
|
461
766
|
/**
|
|
462
767
|
* Triggers and processes a full self-reflection cycle using the
|
|
463
768
|
* `gmi_self_trait_adjustment` metaprompt.
|
|
464
769
|
*
|
|
465
|
-
* Performs the same work as `handleTraitAdjustment
|
|
466
|
-
*
|
|
770
|
+
* Performs the same work as `handleTraitAdjustment`, behind two guards: the
|
|
771
|
+
* cycle is skipped when the persona defines no such metaprompt, and when a
|
|
772
|
+
* metaprompt batch or another cycle is already queued or running. The cycle
|
|
773
|
+
* runs on the metaprompt queue and never changes the GMI's lifecycle state.
|
|
774
|
+
* The returned promise settles when the cycle has finished.
|
|
467
775
|
*/
|
|
468
776
|
async triggerAndProcessSelfReflection() {
|
|
469
777
|
const persona = this.config.getPersona();
|
|
@@ -472,12 +780,20 @@ export class MetapromptExecutor {
|
|
|
472
780
|
this.config.addTraceEntry('SELF_REFLECTION_SKIPPED', "Self-reflection disabled or no 'gmi_self_trait_adjustment' meta-prompt.");
|
|
473
781
|
return;
|
|
474
782
|
}
|
|
475
|
-
if (this.
|
|
783
|
+
if (this.isRunning()) {
|
|
476
784
|
this.config.addTraceEntry('SELF_REFLECTION_SKIPPED', 'Self-reflection already in progress.');
|
|
477
785
|
return;
|
|
478
786
|
}
|
|
479
|
-
|
|
480
|
-
|
|
787
|
+
await this.enqueue('Self-reflection', (run) => this.runSelfReflectionCycle(reflectionMetaPromptDef, run));
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Runs one self-reflection cycle for the `gmi_self_trait_adjustment`
|
|
791
|
+
* metaprompt: gathers evidence, calls the LLM, and applies the parsed
|
|
792
|
+
* updates. Errors are recorded in the reasoning trace and not rethrown.
|
|
793
|
+
*
|
|
794
|
+
* @param reflectionMetaPromptDef - The `gmi_self_trait_adjustment` definition.
|
|
795
|
+
*/
|
|
796
|
+
async runSelfReflectionCycle(reflectionMetaPromptDef, run) {
|
|
481
797
|
this.config.addTraceEntry('SELF_REFLECTION_START', 'Starting self-reflection cycle.');
|
|
482
798
|
try {
|
|
483
799
|
const evidence = {
|
|
@@ -505,16 +821,27 @@ export class MetapromptExecutor {
|
|
|
505
821
|
if (!provider) {
|
|
506
822
|
throw new GMIError(`Provider '${providerId}' not found for self-reflection.`, GMIErrorCode.LLM_PROVIDER_UNAVAILABLE);
|
|
507
823
|
}
|
|
824
|
+
if (run?.isStale()) {
|
|
825
|
+
this.config.addTraceEntry('WARNING', "Discarded late results of metaprompt 'gmi_self_trait_adjustment'.");
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
508
828
|
const llmResponse = await provider.generateCompletion(modelId, [{ role: 'user', content: metaPromptText }], {
|
|
509
829
|
maxTokens: reflectionMetaPromptDef.maxOutputTokens || 512,
|
|
510
830
|
temperature: reflectionMetaPromptDef.temperature || 0.3,
|
|
511
831
|
responseFormat: { type: 'json_object' },
|
|
832
|
+
requestTimeout: this.runTimeoutMs,
|
|
512
833
|
});
|
|
513
834
|
const responseContent = llmResponse.choices?.[0]?.message?.content;
|
|
514
835
|
if (!responseContent || typeof responseContent !== 'string') {
|
|
515
836
|
throw new GMIError('Self-reflection LLM call returned no valid content.', GMIErrorCode.LLM_PROVIDER_ERROR, { response: llmResponse });
|
|
516
837
|
}
|
|
517
838
|
this.config.addTraceEntry('SELF_REFLECTION_DETAIL', 'LLM response for reflection received.', { preview: responseContent.substring(0, 100) });
|
|
839
|
+
// A cycle that went stale while the provider answered is discarded
|
|
840
|
+
// anyway, so no JSON repair call is made on its behalf.
|
|
841
|
+
if (run?.isStale()) {
|
|
842
|
+
this.config.addTraceEntry('WARNING', "Discarded late results of metaprompt 'gmi_self_trait_adjustment'.");
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
518
845
|
const parseOptions = {
|
|
519
846
|
attemptFixWithLLM: true,
|
|
520
847
|
llmModelIdForFix: reflectionMetaPromptDef.modelId || modelId,
|
|
@@ -526,10 +853,11 @@ export class MetapromptExecutor {
|
|
|
526
853
|
}
|
|
527
854
|
this.config.addTraceEntry('SELF_REFLECTION_DETAIL', 'Parsed trait update suggestions.', { suggestions: parsedUpdates });
|
|
528
855
|
// Apply updates via callbacks
|
|
529
|
-
await this.applyMetapromptUpdates(parsedUpdates, 'gmi_self_trait_adjustment');
|
|
530
|
-
//
|
|
531
|
-
|
|
532
|
-
|
|
856
|
+
const storedImprintKeys = await this.applyMetapromptUpdates(parsedUpdates, 'gmi_self_trait_adjustment', run);
|
|
857
|
+
// Trace only the imprints that were stored: a late or stale run
|
|
858
|
+
// stores none, and later reflections read this trace as evidence.
|
|
859
|
+
if (storedImprintKeys.length > 0) {
|
|
860
|
+
this.config.addTraceEntry('STATE_CHANGE', 'New memory imprints added from self-reflection.', { imprints: storedImprintKeys });
|
|
533
861
|
}
|
|
534
862
|
if (parsedUpdates.adjustmentRationale) {
|
|
535
863
|
this.config.addTraceEntry('SELF_REFLECTION_DETAIL', 'Self-reflection rationale recorded.', { rationale: parsedUpdates.adjustmentRationale });
|
|
@@ -541,11 +869,6 @@ export class MetapromptExecutor {
|
|
|
541
869
|
console.error(`GMI (ID: ${this.config.getGmiId()}) self-reflection error:`, gmiError);
|
|
542
870
|
}
|
|
543
871
|
finally {
|
|
544
|
-
const disallowedStates = new Set([
|
|
545
|
-
GMIPrimeState.IDLE,
|
|
546
|
-
GMIPrimeState.INITIALIZING,
|
|
547
|
-
]);
|
|
548
|
-
this.config.setState(disallowedStates.has(previousState) ? GMIPrimeState.READY : previousState);
|
|
549
872
|
this.config.addTraceEntry('SELF_REFLECTION_COMPLETE', 'Self-reflection cycle finished.');
|
|
550
873
|
}
|
|
551
874
|
}
|