@herjarsa/omo-meta-governor 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/README.md +43 -0
- package/dist/closed-loop-learning.d.ts +32 -0
- package/dist/config.d.ts +50 -0
- package/dist/decision-handler.d.ts +35 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +1049 -0
- package/dist/memory-aggregator.d.ts +124 -0
- package/dist/orchestrator.d.ts +33 -0
- package/dist/plugin.d.ts +23 -0
- package/dist/post-repair-recorder.d.ts +47 -0
- package/dist/scoring-engine.d.ts +31 -0
- package/dist/token-predictor.d.ts +29 -0
- package/dist/types.d.ts +476 -0
- package/package.json +38 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1049 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/memory-aggregator.ts
|
|
3
|
+
var DEFAULTS = {
|
|
4
|
+
limits: { maxLessons: 10, maxSlots: 20, maxTasks: 10 },
|
|
5
|
+
timeouts: { agentmemoryMs: 2000, magicContextMs: 1000, boulderStateMs: 1000 }
|
|
6
|
+
};
|
|
7
|
+
async function aggregateRead(input, backends) {
|
|
8
|
+
const start = performance.now();
|
|
9
|
+
const limits = { ...DEFAULTS.limits, ...input.limits };
|
|
10
|
+
const timeouts = { ...DEFAULTS.timeouts, ...input.timeouts };
|
|
11
|
+
const [agentResult, magicResult, boulderResult] = await Promise.allSettled([
|
|
12
|
+
readAgentmemory(input, backends.agentmemory, limits, timeouts.agentmemoryMs),
|
|
13
|
+
readMagicContext(input, backends.magicContext, limits, timeouts.magicContextMs),
|
|
14
|
+
readBoulderState(input, backends.boulderState, limits, timeouts.boulderStateMs)
|
|
15
|
+
]);
|
|
16
|
+
const degradedSources = [];
|
|
17
|
+
const errorMessages = {};
|
|
18
|
+
const agentmemory = agentResult.status === "fulfilled" ? agentResult.value : (pushDegraded(degradedSources, errorMessages, "agentmemory", errorMessage(agentResult.reason)), DEGRADED.agentmemory);
|
|
19
|
+
const magicContext = magicResult.status === "fulfilled" ? magicResult.value : (pushDegraded(degradedSources, errorMessages, "magicContext", errorMessage(magicResult.reason)), DEGRADED.magicContext);
|
|
20
|
+
const boulderState = boulderResult.status === "fulfilled" ? boulderResult.value : (pushDegraded(degradedSources, errorMessages, "boulderState", errorMessage(boulderResult.reason)), DEGRADED.boulderState);
|
|
21
|
+
return {
|
|
22
|
+
query: input.query,
|
|
23
|
+
timestampISO: new Date().toISOString(),
|
|
24
|
+
agentmemory,
|
|
25
|
+
magicContext,
|
|
26
|
+
boulderState,
|
|
27
|
+
degradedSources,
|
|
28
|
+
durationMs: performance.now() - start,
|
|
29
|
+
errorMessages
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function readAgentmemory(input, backend, limits, timeoutMs) {
|
|
33
|
+
const search = await withTimeout(backend.smartSearch({ query: input.query, limit: limits.maxLessons }), timeoutMs, "agentmemory");
|
|
34
|
+
const lessons = sortByConfidence(search.lessons).slice(0, limits.maxLessons).map((l) => ({
|
|
35
|
+
id: l.id,
|
|
36
|
+
title: l.title,
|
|
37
|
+
advice: "info",
|
|
38
|
+
confidence: l.confidence,
|
|
39
|
+
concepts: l.concepts
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
available: true,
|
|
43
|
+
lessons
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async function readMagicContext(input, backend, limits, timeoutMs) {
|
|
47
|
+
const slots = await withTimeout(backend.slotList({ directory: input.directory }), timeoutMs, "magicContext");
|
|
48
|
+
return {
|
|
49
|
+
available: true,
|
|
50
|
+
slots: slots.filter((s) => s.label.startsWith("meta_governor:") || isRelevant(s, input.query)).sort((a, b) => a.label.localeCompare(b.label)).slice(0, limits.maxSlots).map((s) => ({ label: s.label, content: s.content }))
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function readBoulderState(input, backend, limits, timeoutMs) {
|
|
54
|
+
const tasks = await withTimeout(backend.boulderRead({ directory: input.directory, sessionID: input.sessionID, query: input.query }), timeoutMs, "boulderState");
|
|
55
|
+
const sorted = tasks.sort((a, b) => a.priority - b.priority || b.updatedAtMs - a.updatedAtMs).slice(0, limits.maxTasks);
|
|
56
|
+
return {
|
|
57
|
+
available: true,
|
|
58
|
+
tasks: sorted.map((t) => ({ id: t.id, status: t.status, title: t.title })),
|
|
59
|
+
planProgress: computePlanProgress(tasks)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function sortByConfidence(lessons) {
|
|
63
|
+
return [...lessons].sort((a, b) => b.confidence - a.confidence);
|
|
64
|
+
}
|
|
65
|
+
function isRelevant(slot, query) {
|
|
66
|
+
const q = query.toLowerCase();
|
|
67
|
+
return slot.label.toLowerCase().includes(q) || slot.content.toLowerCase().includes(q);
|
|
68
|
+
}
|
|
69
|
+
function computePlanProgress(tasks) {
|
|
70
|
+
if (tasks.length === 0)
|
|
71
|
+
return 0;
|
|
72
|
+
const done = tasks.filter((t) => t.status === "done").length;
|
|
73
|
+
return done / tasks.length;
|
|
74
|
+
}
|
|
75
|
+
function errorMessage(err) {
|
|
76
|
+
if (err instanceof Error)
|
|
77
|
+
return err.message;
|
|
78
|
+
if (typeof err === "string")
|
|
79
|
+
return err;
|
|
80
|
+
return "unknown error";
|
|
81
|
+
}
|
|
82
|
+
function pushDegraded(list, errorMessages, source, reason) {
|
|
83
|
+
list.push(source);
|
|
84
|
+
errorMessages[source] = reason;
|
|
85
|
+
}
|
|
86
|
+
var DEGRADED = {
|
|
87
|
+
agentmemory: { available: false, lessons: [] },
|
|
88
|
+
magicContext: { available: false, slots: [] },
|
|
89
|
+
boulderState: { available: false, tasks: [], planProgress: 0 }
|
|
90
|
+
};
|
|
91
|
+
async function withTimeout(p, ms, label) {
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const t = setTimeout(() => reject(new Error(`${label} timeout after ${ms}ms`)), ms);
|
|
94
|
+
p.then((v) => {
|
|
95
|
+
clearTimeout(t);
|
|
96
|
+
resolve(v);
|
|
97
|
+
}, (e) => {
|
|
98
|
+
clearTimeout(t);
|
|
99
|
+
reject(e);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/token-predictor.ts
|
|
105
|
+
function defaultTokenPredictorConfig() {
|
|
106
|
+
return {
|
|
107
|
+
compactBurnRateThreshold: 500,
|
|
108
|
+
compactUsageThreshold: 0.85,
|
|
109
|
+
switchModelUsageThreshold: 0.95,
|
|
110
|
+
delegateConsecutiveHighBurn: 5,
|
|
111
|
+
windowSize: 10
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function calculateBurnRate(recentTurnTokens) {
|
|
115
|
+
if (recentTurnTokens.length === 0)
|
|
116
|
+
return 0;
|
|
117
|
+
const sum = recentTurnTokens.reduce((a, b) => a + b, 0);
|
|
118
|
+
return sum / recentTurnTokens.length;
|
|
119
|
+
}
|
|
120
|
+
function countConsecutiveHighBurn(recentTurnTokens, threshold) {
|
|
121
|
+
let count = 0;
|
|
122
|
+
for (let i = recentTurnTokens.length - 1;i >= 0; i--) {
|
|
123
|
+
if (recentTurnTokens[i] >= threshold) {
|
|
124
|
+
count++;
|
|
125
|
+
} else {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return count;
|
|
130
|
+
}
|
|
131
|
+
function predictOverflowTime(currentUsage, modelLimit, burnRate, timestampISO) {
|
|
132
|
+
const budgetLeft = modelLimit - currentUsage;
|
|
133
|
+
if (budgetLeft <= 0)
|
|
134
|
+
return timestampISO;
|
|
135
|
+
if (burnRate <= 0)
|
|
136
|
+
return null;
|
|
137
|
+
const turnsLeft = Math.floor(budgetLeft / burnRate);
|
|
138
|
+
if (turnsLeft <= 0)
|
|
139
|
+
return timestampISO;
|
|
140
|
+
const secondsLeft = turnsLeft * 2;
|
|
141
|
+
const overflowDate = new Date(new Date(timestampISO).getTime() + secondsLeft * 1000);
|
|
142
|
+
return overflowDate.toISOString();
|
|
143
|
+
}
|
|
144
|
+
function predict(input) {
|
|
145
|
+
const { currentUsage, modelLimit, recentTurnTokens, timestampISO, config } = input;
|
|
146
|
+
const windowTokens = recentTurnTokens.slice(-config.windowSize);
|
|
147
|
+
const burnRate = calculateBurnRate(windowTokens);
|
|
148
|
+
const budgetLeft = modelLimit - currentUsage;
|
|
149
|
+
const usageRatio = modelLimit > 0 ? currentUsage / modelLimit : 1;
|
|
150
|
+
const confidence = Math.min(0.95, 0.3 + windowTokens.length / config.windowSize * 0.65);
|
|
151
|
+
let recommendation = "no-action";
|
|
152
|
+
let reason = "within normal parameters";
|
|
153
|
+
if (usageRatio >= config.switchModelUsageThreshold) {
|
|
154
|
+
recommendation = "switch-model";
|
|
155
|
+
reason = `context usage ${(usageRatio * 100).toFixed(1)}% exceeds switch threshold ${(config.switchModelUsageThreshold * 100).toFixed(1)}%`;
|
|
156
|
+
}
|
|
157
|
+
if (usageRatio >= config.compactUsageThreshold || burnRate >= config.compactBurnRateThreshold) {
|
|
158
|
+
if (recommendation === "no-action") {
|
|
159
|
+
recommendation = "compact-now";
|
|
160
|
+
reason = usageRatio >= config.compactUsageThreshold ? `context usage ${(usageRatio * 100).toFixed(1)}% exceeds compact threshold ${(config.compactUsageThreshold * 100).toFixed(1)}%` : `burn rate ${burnRate.toFixed(0)} tokens/turn exceeds threshold ${config.compactBurnRateThreshold}`;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const consecutiveHighBurn = countConsecutiveHighBurn(windowTokens, config.compactBurnRateThreshold);
|
|
164
|
+
if (consecutiveHighBurn >= config.delegateConsecutiveHighBurn && recommendation === "no-action") {
|
|
165
|
+
recommendation = "delegate-to-subagent";
|
|
166
|
+
reason = `${consecutiveHighBurn} consecutive high-burn turns (threshold: ${config.delegateConsecutiveHighBurn})`;
|
|
167
|
+
}
|
|
168
|
+
const willOverflowAt = predictOverflowTime(currentUsage, modelLimit, burnRate, timestampISO);
|
|
169
|
+
return {
|
|
170
|
+
currentUsage,
|
|
171
|
+
burnRate,
|
|
172
|
+
budgetLeft,
|
|
173
|
+
willOverflowAt,
|
|
174
|
+
recommendation,
|
|
175
|
+
confidence,
|
|
176
|
+
modelLimit,
|
|
177
|
+
windowRemaining: Math.max(0, Math.floor(budgetLeft / Math.max(burnRate, 1))),
|
|
178
|
+
input: { ...input },
|
|
179
|
+
computedAtISO: timestampISO,
|
|
180
|
+
turnsAnalyzed: windowTokens.length
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/scoring-engine.ts
|
|
185
|
+
var DEFAULT_WEIGHTS = {
|
|
186
|
+
"oracle-verified": 0.25,
|
|
187
|
+
"no-progress-detector": 0.2,
|
|
188
|
+
"deviation-detector": 0.2,
|
|
189
|
+
"iteration-budget": 0.15,
|
|
190
|
+
"lesson-recall": 0.1,
|
|
191
|
+
"token-predictor": 0.1
|
|
192
|
+
};
|
|
193
|
+
var defaultScoringConfig = () => ({
|
|
194
|
+
continueThreshold: 0.3,
|
|
195
|
+
warnThreshold: 0.3,
|
|
196
|
+
escalateThreshold: 0.6,
|
|
197
|
+
stopThreshold: 0.8,
|
|
198
|
+
paralysisThreshold: 3,
|
|
199
|
+
defaultEscalationTarget: "oracle"
|
|
200
|
+
});
|
|
201
|
+
function scoreSignals(ctx) {
|
|
202
|
+
const contributions = [];
|
|
203
|
+
contributions.push({
|
|
204
|
+
source: "oracle-verified",
|
|
205
|
+
rawScore: ctx.oracleVerified ? 0.6 : 0,
|
|
206
|
+
weight: DEFAULT_WEIGHTS["oracle-verified"],
|
|
207
|
+
weightedScore: ctx.oracleVerified ? 0.6 * DEFAULT_WEIGHTS["oracle-verified"] : 0,
|
|
208
|
+
description: ctx.oracleVerified ? "Oracle verification passed" : "No Oracle verification"
|
|
209
|
+
});
|
|
210
|
+
contributions.push({
|
|
211
|
+
source: "no-progress-detector",
|
|
212
|
+
rawScore: ctx.noProgress ? -0.8 : 0,
|
|
213
|
+
weight: DEFAULT_WEIGHTS["no-progress-detector"],
|
|
214
|
+
weightedScore: ctx.noProgress ? -0.8 * DEFAULT_WEIGHTS["no-progress-detector"] : 0,
|
|
215
|
+
description: ctx.noProgress ? "No progress detected in last turn" : "Progress detected"
|
|
216
|
+
});
|
|
217
|
+
const deviationScore = scoreDeviations(ctx.deviations);
|
|
218
|
+
contributions.push({
|
|
219
|
+
source: "deviation-detector",
|
|
220
|
+
rawScore: deviationScore,
|
|
221
|
+
weight: DEFAULT_WEIGHTS["deviation-detector"],
|
|
222
|
+
weightedScore: deviationScore * DEFAULT_WEIGHTS["deviation-detector"],
|
|
223
|
+
description: ctx.deviations.length > 0 ? `${ctx.deviations.length} deviation(s) detected (worst: ${ctx.deviations[0].severity})` : "No deviations detected"
|
|
224
|
+
});
|
|
225
|
+
const iterationScore = scoreIterationBudget(ctx.iterationRatio);
|
|
226
|
+
contributions.push({
|
|
227
|
+
source: "iteration-budget",
|
|
228
|
+
rawScore: iterationScore,
|
|
229
|
+
weight: DEFAULT_WEIGHTS["iteration-budget"],
|
|
230
|
+
weightedScore: iterationScore * DEFAULT_WEIGHTS["iteration-budget"],
|
|
231
|
+
description: `Iteration ratio: ${ctx.iterationRatio.toFixed(2)} (${ctx.ambient.iteration}/${ctx.ambient.maxIterations})`
|
|
232
|
+
});
|
|
233
|
+
const lessonScore = scoreLessons(ctx.lessonsRelevant);
|
|
234
|
+
contributions.push({
|
|
235
|
+
source: "lesson-recall",
|
|
236
|
+
rawScore: lessonScore,
|
|
237
|
+
weight: DEFAULT_WEIGHTS["lesson-recall"],
|
|
238
|
+
weightedScore: lessonScore * DEFAULT_WEIGHTS["lesson-recall"],
|
|
239
|
+
description: ctx.lessonsRelevant.length > 0 ? `${ctx.lessonsRelevant.length} relevant lesson(s) (avg confidence: ${avgConfidence(ctx.lessonsRelevant).toFixed(2)})` : "No relevant lessons"
|
|
240
|
+
});
|
|
241
|
+
return contributions;
|
|
242
|
+
}
|
|
243
|
+
function scoreDeviations(deviations) {
|
|
244
|
+
if (deviations.length === 0)
|
|
245
|
+
return 0;
|
|
246
|
+
const severityMap = {
|
|
247
|
+
grave: -0.9,
|
|
248
|
+
media: -0.5,
|
|
249
|
+
leve: -0.2
|
|
250
|
+
};
|
|
251
|
+
let worst = 0;
|
|
252
|
+
for (const d of deviations) {
|
|
253
|
+
const s = severityMap[d.severity] ?? -0.3;
|
|
254
|
+
if (s < worst)
|
|
255
|
+
worst = s;
|
|
256
|
+
}
|
|
257
|
+
const amplification = Math.min(deviations.length * 0.05, 0.2);
|
|
258
|
+
return Math.max(worst - amplification, -1);
|
|
259
|
+
}
|
|
260
|
+
function scoreIterationBudget(ratio) {
|
|
261
|
+
if (ratio <= 0.5)
|
|
262
|
+
return -ratio * 0.6;
|
|
263
|
+
return -0.3 - (ratio - 0.5) * 1;
|
|
264
|
+
}
|
|
265
|
+
function scoreLessons(lessons) {
|
|
266
|
+
if (lessons.length === 0)
|
|
267
|
+
return 0;
|
|
268
|
+
let totalScore = 0;
|
|
269
|
+
for (const lesson of lessons) {
|
|
270
|
+
const adviceScore = {
|
|
271
|
+
continue: 0.3,
|
|
272
|
+
info: 0,
|
|
273
|
+
warn: -0.3,
|
|
274
|
+
stop: -0.7
|
|
275
|
+
};
|
|
276
|
+
totalScore += (adviceScore[lesson.advice] ?? 0) * lesson.confidence;
|
|
277
|
+
}
|
|
278
|
+
return Math.max(Math.min(totalScore / lessons.length, 1), -1);
|
|
279
|
+
}
|
|
280
|
+
function avgConfidence(lessons) {
|
|
281
|
+
if (lessons.length === 0)
|
|
282
|
+
return 0;
|
|
283
|
+
return lessons.reduce((sum, l) => sum + l.confidence, 0) / lessons.length;
|
|
284
|
+
}
|
|
285
|
+
function mapScoreToAction(score, config) {
|
|
286
|
+
if (score >= config.continueThreshold)
|
|
287
|
+
return "continue";
|
|
288
|
+
if (score <= -config.stopThreshold)
|
|
289
|
+
return "stop";
|
|
290
|
+
if (score <= -config.escalateThreshold)
|
|
291
|
+
return "escalate";
|
|
292
|
+
if (score <= -config.warnThreshold)
|
|
293
|
+
return "warn";
|
|
294
|
+
return "continue";
|
|
295
|
+
}
|
|
296
|
+
function selectEscalationTarget(ctx, config) {
|
|
297
|
+
if (!ctx.oracleVerified)
|
|
298
|
+
return "oracle";
|
|
299
|
+
if (ctx.deviations.some((d) => d.severity === "grave"))
|
|
300
|
+
return "user";
|
|
301
|
+
return config.defaultEscalationTarget;
|
|
302
|
+
}
|
|
303
|
+
function buildReasoning(score, action, contributions, paralysisOverride) {
|
|
304
|
+
if (paralysisOverride) {
|
|
305
|
+
return `Paralysis detected: forced continue despite score ${score.toFixed(3)} (too many consecutive stops)`;
|
|
306
|
+
}
|
|
307
|
+
const sorted = [...contributions].sort((a, b) => a.weightedScore - b.weightedScore);
|
|
308
|
+
const worst = sorted[0];
|
|
309
|
+
const best = sorted[sorted.length - 1];
|
|
310
|
+
const parts = [];
|
|
311
|
+
if (worst.weightedScore < 0) {
|
|
312
|
+
parts.push(`primary concern: ${worst.description}`);
|
|
313
|
+
}
|
|
314
|
+
if (best.weightedScore > 0) {
|
|
315
|
+
parts.push(`positive signal: ${best.description}`);
|
|
316
|
+
}
|
|
317
|
+
const actionLabel = {
|
|
318
|
+
continue: "Continue",
|
|
319
|
+
warn: "Warn",
|
|
320
|
+
escalate: "Escalate",
|
|
321
|
+
stop: "Stop"
|
|
322
|
+
};
|
|
323
|
+
return `${actionLabel[action]} (score: ${score.toFixed(3)}): ${parts.join("; ") || "balanced signals"}`;
|
|
324
|
+
}
|
|
325
|
+
function score(ctx, config) {
|
|
326
|
+
const resolvedConfig = { ...defaultScoringConfig(), ...config };
|
|
327
|
+
const contributions = scoreSignals(ctx);
|
|
328
|
+
const rawScore = contributions.reduce((sum, c) => sum + c.weightedScore, 0);
|
|
329
|
+
const clampedScore = Math.max(Math.min(rawScore, 1), -1);
|
|
330
|
+
const paralysisOverride = ctx.slotMemory.consecutiveStops >= resolvedConfig.paralysisThreshold && clampedScore <= -resolvedConfig.warnThreshold;
|
|
331
|
+
const action = paralysisOverride ? "continue" : mapScoreToAction(clampedScore, resolvedConfig);
|
|
332
|
+
const evidence = [];
|
|
333
|
+
if (action !== "continue" || Math.abs(clampedScore) < resolvedConfig.continueThreshold) {
|
|
334
|
+
for (const c of contributions) {
|
|
335
|
+
if (Math.abs(c.weightedScore) > 0.01) {
|
|
336
|
+
evidence.push({
|
|
337
|
+
source: c.source,
|
|
338
|
+
value: c.description,
|
|
339
|
+
confidence: Math.abs(c.rawScore),
|
|
340
|
+
weight: c.weight
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (evidence.length === 0 && action !== "continue") {
|
|
345
|
+
evidence.push({
|
|
346
|
+
source: "ambient",
|
|
347
|
+
value: buildReasoning(clampedScore, action, contributions, paralysisOverride),
|
|
348
|
+
confidence: 0.5,
|
|
349
|
+
weight: 0.1
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const shouldEscalateTo = action === "escalate" ? selectEscalationTarget(ctx, resolvedConfig) : null;
|
|
354
|
+
const decision = {
|
|
355
|
+
action,
|
|
356
|
+
score: clampedScore,
|
|
357
|
+
reasoning: buildReasoning(clampedScore, action, contributions, paralysisOverride),
|
|
358
|
+
evidence,
|
|
359
|
+
shouldEscalateTo
|
|
360
|
+
};
|
|
361
|
+
return {
|
|
362
|
+
decision,
|
|
363
|
+
contributions,
|
|
364
|
+
rawScore: clampedScore,
|
|
365
|
+
paralysisOverride,
|
|
366
|
+
computedAtISO: new Date().toISOString()
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/decision-handler.ts
|
|
371
|
+
var defaultDecisionHandlerConfig = () => ({
|
|
372
|
+
enabled: true,
|
|
373
|
+
maxHistoryPerSession: 50,
|
|
374
|
+
forceContinueAfterStops: 3,
|
|
375
|
+
warnMessageTemplate: "[MetaGovernor] Score {score}: {reasoning}. Evidence: {evidenceCount} signal(s).",
|
|
376
|
+
escalateMessageTemplate: "[MetaGovernor] Escalating to {target}: {reasoning}",
|
|
377
|
+
stopMessageTemplate: "[MetaGovernor] STOP \u2014 {reasoning}. Evidence: {evidenceCount} signal(s)."
|
|
378
|
+
});
|
|
379
|
+
function formatMessage(template, vars) {
|
|
380
|
+
let result = template;
|
|
381
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
382
|
+
result = result.replaceAll(`{${key}}`, value);
|
|
383
|
+
}
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
function evidenceCount(evidence) {
|
|
387
|
+
return evidence.length;
|
|
388
|
+
}
|
|
389
|
+
function handleDecision(input, config) {
|
|
390
|
+
const resolvedConfig = { ...defaultDecisionHandlerConfig(), ...config };
|
|
391
|
+
if (!resolvedConfig.enabled) {
|
|
392
|
+
return {
|
|
393
|
+
action: "continue",
|
|
394
|
+
message: null,
|
|
395
|
+
historyEntry: {
|
|
396
|
+
decision: input.scoringResult.decision,
|
|
397
|
+
action: "continue",
|
|
398
|
+
timestampISO: new Date().toISOString(),
|
|
399
|
+
sessionID: input.sessionID,
|
|
400
|
+
reasoning: "Decision handler disabled \u2014 pass-through continue"
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
const decision = input.scoringResult.decision;
|
|
405
|
+
const score2 = decision.score;
|
|
406
|
+
const reasoning = decision.reasoning;
|
|
407
|
+
const evCount = evidenceCount(decision.evidence);
|
|
408
|
+
const target = decision.shouldEscalateTo ?? resolvedConfig.defaultEscalationTarget ?? "oracle";
|
|
409
|
+
if (input.scoringResult.paralysisOverride) {
|
|
410
|
+
const message = formatMessage(resolvedConfig.warnMessageTemplate, {
|
|
411
|
+
score: score2.toFixed(3),
|
|
412
|
+
reasoning: `Paralysis detected \u2014 forced continue. ${reasoning}`,
|
|
413
|
+
evidenceCount: String(evCount)
|
|
414
|
+
});
|
|
415
|
+
return {
|
|
416
|
+
action: "continue",
|
|
417
|
+
message,
|
|
418
|
+
historyEntry: {
|
|
419
|
+
decision,
|
|
420
|
+
action: "continue",
|
|
421
|
+
timestampISO: new Date().toISOString(),
|
|
422
|
+
sessionID: input.sessionID,
|
|
423
|
+
reasoning: `Paralysis override: ${reasoning}`
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
switch (decision.action) {
|
|
428
|
+
case "continue": {
|
|
429
|
+
return {
|
|
430
|
+
action: "continue",
|
|
431
|
+
message: null,
|
|
432
|
+
historyEntry: {
|
|
433
|
+
decision,
|
|
434
|
+
action: "continue",
|
|
435
|
+
timestampISO: new Date().toISOString(),
|
|
436
|
+
sessionID: input.sessionID,
|
|
437
|
+
reasoning
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
case "warn": {
|
|
442
|
+
const message = formatMessage(resolvedConfig.warnMessageTemplate, {
|
|
443
|
+
score: score2.toFixed(3),
|
|
444
|
+
reasoning,
|
|
445
|
+
evidenceCount: String(evCount)
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
action: "warn",
|
|
449
|
+
message,
|
|
450
|
+
historyEntry: {
|
|
451
|
+
decision,
|
|
452
|
+
action: "warn",
|
|
453
|
+
timestampISO: new Date().toISOString(),
|
|
454
|
+
sessionID: input.sessionID,
|
|
455
|
+
reasoning
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
case "escalate": {
|
|
460
|
+
const message = formatMessage(resolvedConfig.escalateMessageTemplate, {
|
|
461
|
+
target,
|
|
462
|
+
reasoning
|
|
463
|
+
});
|
|
464
|
+
return {
|
|
465
|
+
action: "escalate",
|
|
466
|
+
message,
|
|
467
|
+
historyEntry: {
|
|
468
|
+
decision,
|
|
469
|
+
action: "escalate",
|
|
470
|
+
timestampISO: new Date().toISOString(),
|
|
471
|
+
sessionID: input.sessionID,
|
|
472
|
+
reasoning: `Escalate to ${target}: ${reasoning}`
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
case "stop": {
|
|
477
|
+
const message = formatMessage(resolvedConfig.stopMessageTemplate, {
|
|
478
|
+
reasoning,
|
|
479
|
+
evidenceCount: String(evCount)
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
action: "stop",
|
|
483
|
+
message,
|
|
484
|
+
historyEntry: {
|
|
485
|
+
decision,
|
|
486
|
+
action: "stop",
|
|
487
|
+
timestampISO: new Date().toISOString(),
|
|
488
|
+
sessionID: input.sessionID,
|
|
489
|
+
reasoning
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function trimHistory(history, maxSize) {
|
|
496
|
+
if (history.length <= maxSize) {
|
|
497
|
+
return { trimmed: [...history], dropped: 0 };
|
|
498
|
+
}
|
|
499
|
+
const dropped = history.length - maxSize;
|
|
500
|
+
return { trimmed: history.slice(-maxSize), dropped };
|
|
501
|
+
}
|
|
502
|
+
function countConsecutiveStops(history) {
|
|
503
|
+
let count = 0;
|
|
504
|
+
for (let i = history.length - 1;i >= 0; i--) {
|
|
505
|
+
if (history[i].action === "stop") {
|
|
506
|
+
count++;
|
|
507
|
+
} else {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return count;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/closed-loop-learning.ts
|
|
515
|
+
var SEVERITY_ORDER = {
|
|
516
|
+
leve: 0,
|
|
517
|
+
media: 1,
|
|
518
|
+
grave: 2
|
|
519
|
+
};
|
|
520
|
+
function generateDecisionId(sessionID, action, timestamp) {
|
|
521
|
+
const hash = `${sessionID}-${action}-${timestamp}`.split("").reduce((a, c) => (a << 5) - a + c.charCodeAt(0) | 0, 0);
|
|
522
|
+
return `D-${Math.abs(hash).toString(36)}`;
|
|
523
|
+
}
|
|
524
|
+
function severityMeetsThreshold(deviations, threshold) {
|
|
525
|
+
const minOrder = SEVERITY_ORDER[threshold] ?? 0;
|
|
526
|
+
return deviations.some((d) => (SEVERITY_ORDER[d.severity] ?? 0) >= minOrder);
|
|
527
|
+
}
|
|
528
|
+
function extractConcepts(deviations) {
|
|
529
|
+
const concepts = new Set;
|
|
530
|
+
for (const d of deviations) {
|
|
531
|
+
concepts.add(d.category);
|
|
532
|
+
concepts.add(d.severity);
|
|
533
|
+
}
|
|
534
|
+
return [...concepts];
|
|
535
|
+
}
|
|
536
|
+
function buildLessonContent(decision, deviations) {
|
|
537
|
+
const deviationSummary = deviations.map((d) => `[${d.severity}] ${d.category}: ${d.detail}`).join("; ");
|
|
538
|
+
return `Action "${decision.action}" (score ${decision.score.toFixed(2)}) after deviations: ${deviationSummary}. Reasoning: ${decision.reasoning}`;
|
|
539
|
+
}
|
|
540
|
+
async function observeAndLearn(input, backend) {
|
|
541
|
+
const { decision, config, sessionID, directory, filesChanged } = input;
|
|
542
|
+
const now = new Date().toISOString();
|
|
543
|
+
if (!config.enabled) {
|
|
544
|
+
return { lessonSaved: null, decisionSaved: null, reason: "closed-loop learning disabled" };
|
|
545
|
+
}
|
|
546
|
+
if (decision.evidence.length === 0 && decision.action === "continue") {
|
|
547
|
+
return { lessonSaved: null, decisionSaved: null, reason: "no deviations to learn from" };
|
|
548
|
+
}
|
|
549
|
+
let lessonSaved = null;
|
|
550
|
+
let decisionSaved = null;
|
|
551
|
+
if (config.saveDecisions) {
|
|
552
|
+
const decisionRecord = {
|
|
553
|
+
id: generateDecisionId(sessionID, decision.action, now),
|
|
554
|
+
timestampISO: now,
|
|
555
|
+
action: decision.action,
|
|
556
|
+
score: decision.score,
|
|
557
|
+
reasoning: decision.reasoning,
|
|
558
|
+
sessionID,
|
|
559
|
+
directory,
|
|
560
|
+
deviations: decision.evidence.filter((e) => e.source === "deviation-detector").map((e) => ({
|
|
561
|
+
severity: "media",
|
|
562
|
+
category: e.source,
|
|
563
|
+
detail: e.value
|
|
564
|
+
}))
|
|
565
|
+
};
|
|
566
|
+
try {
|
|
567
|
+
await backend.saveMemory({
|
|
568
|
+
content: `Decision: ${decision.action} (score ${decision.score.toFixed(2)}). ${decision.reasoning}`,
|
|
569
|
+
concepts: ["meta-governor", "decision", decision.action],
|
|
570
|
+
type: "fact",
|
|
571
|
+
files: [...filesChanged]
|
|
572
|
+
});
|
|
573
|
+
decisionSaved = decisionRecord;
|
|
574
|
+
} catch {}
|
|
575
|
+
}
|
|
576
|
+
const deviationsFromEvidence = decision.evidence.filter((e) => e.source === "deviation-detector").map((e) => ({
|
|
577
|
+
severity: "media",
|
|
578
|
+
category: e.source,
|
|
579
|
+
detail: e.value
|
|
580
|
+
}));
|
|
581
|
+
if (severityMeetsThreshold(deviationsFromEvidence, config.minSeverityToLearn)) {
|
|
582
|
+
const concepts = extractConcepts(deviationsFromEvidence);
|
|
583
|
+
const content = buildLessonContent(decision, deviationsFromEvidence);
|
|
584
|
+
try {
|
|
585
|
+
const result = await backend.saveLesson({
|
|
586
|
+
content,
|
|
587
|
+
context: `session:${sessionID} dir:${directory}`,
|
|
588
|
+
confidence: Math.max(0.3, Math.min(0.8, Math.abs(decision.score))),
|
|
589
|
+
tags: concepts
|
|
590
|
+
});
|
|
591
|
+
lessonSaved = {
|
|
592
|
+
id: result.id,
|
|
593
|
+
title: `${decision.action} after ${deviationsFromEvidence[0]?.category ?? "deviation"}`,
|
|
594
|
+
content,
|
|
595
|
+
type: "pattern",
|
|
596
|
+
concepts,
|
|
597
|
+
confidence: Math.max(0.3, Math.min(0.8, Math.abs(decision.score))),
|
|
598
|
+
files: [...filesChanged],
|
|
599
|
+
sessionID
|
|
600
|
+
};
|
|
601
|
+
} catch {}
|
|
602
|
+
}
|
|
603
|
+
const reasons = [];
|
|
604
|
+
if (decisionSaved)
|
|
605
|
+
reasons.push("decision saved");
|
|
606
|
+
if (lessonSaved)
|
|
607
|
+
reasons.push("lesson saved");
|
|
608
|
+
if (!decisionSaved && !lessonSaved) {
|
|
609
|
+
if (!severityMeetsThreshold(deviationsFromEvidence, config.minSeverityToLearn)) {
|
|
610
|
+
reasons.push("severity below threshold");
|
|
611
|
+
} else {
|
|
612
|
+
reasons.push("no saveable content");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
lessonSaved,
|
|
617
|
+
decisionSaved,
|
|
618
|
+
reason: reasons.join("; ") || "no action taken"
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function defaultClosedLoopConfig() {
|
|
622
|
+
return {
|
|
623
|
+
enabled: true,
|
|
624
|
+
minSeverityToLearn: "media",
|
|
625
|
+
maxLessonsPerSession: 20,
|
|
626
|
+
saveDecisions: true
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/orchestrator.ts
|
|
631
|
+
var defaultOrchestratorConfig = () => ({
|
|
632
|
+
enabled: true,
|
|
633
|
+
memory: { enabled: true, query: "", timeoutMs: 3000 },
|
|
634
|
+
tokenPredictor: {},
|
|
635
|
+
scoring: {},
|
|
636
|
+
decision: {},
|
|
637
|
+
closedLoop: {}
|
|
638
|
+
});
|
|
639
|
+
var EMPTY_MEMORY_READ = {
|
|
640
|
+
query: "",
|
|
641
|
+
timestampISO: new Date().toISOString(),
|
|
642
|
+
agentmemory: { available: false, lessons: [] },
|
|
643
|
+
magicContext: { available: false, slots: [] },
|
|
644
|
+
boulderState: { available: false, tasks: [], planProgress: 0 },
|
|
645
|
+
degradedSources: ["agentmemory", "magicContext", "boulderState"]
|
|
646
|
+
};
|
|
647
|
+
var EMPTY_SLOT_MEMORY = {
|
|
648
|
+
consecutiveStops: 0,
|
|
649
|
+
consecutiveContinues: 0,
|
|
650
|
+
lastUpdatedISO: new Date().toISOString()
|
|
651
|
+
};
|
|
652
|
+
var NO_OP_DECISION = {
|
|
653
|
+
action: "continue",
|
|
654
|
+
message: null,
|
|
655
|
+
historyEntry: {
|
|
656
|
+
decision: {
|
|
657
|
+
action: "continue",
|
|
658
|
+
score: 0,
|
|
659
|
+
reasoning: "no decision made",
|
|
660
|
+
evidence: [],
|
|
661
|
+
shouldEscalateTo: null
|
|
662
|
+
},
|
|
663
|
+
action: "continue",
|
|
664
|
+
timestampISO: new Date().toISOString(),
|
|
665
|
+
sessionID: "",
|
|
666
|
+
reasoning: "no decision made"
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
function buildDecisionContext(input, memoryRead = EMPTY_MEMORY_READ) {
|
|
670
|
+
const iterationRatio = input.maxIterations > 0 ? input.iteration / input.maxIterations : 0;
|
|
671
|
+
const slotMemory = {
|
|
672
|
+
...EMPTY_SLOT_MEMORY,
|
|
673
|
+
consecutiveStops: input.consecutiveStops ?? 0
|
|
674
|
+
};
|
|
675
|
+
return {
|
|
676
|
+
oracleVerified: input.oracleVerified,
|
|
677
|
+
noProgress: input.noProgress,
|
|
678
|
+
deviations: input.deviations,
|
|
679
|
+
iterationRatio,
|
|
680
|
+
lessonsRelevant: memoryRead.agentmemory.lessons,
|
|
681
|
+
slotMemory,
|
|
682
|
+
ambient: {
|
|
683
|
+
sessionID: input.sessionID,
|
|
684
|
+
directory: ".",
|
|
685
|
+
mode: "simple",
|
|
686
|
+
agentName: input.agentName ?? "unknown",
|
|
687
|
+
iteration: input.iteration,
|
|
688
|
+
maxIterations: input.maxIterations
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
async function runMetaGovernor(input, config = {}) {
|
|
693
|
+
const mergedConfig = {
|
|
694
|
+
...defaultOrchestratorConfig(),
|
|
695
|
+
...config
|
|
696
|
+
};
|
|
697
|
+
if (!mergedConfig.enabled) {
|
|
698
|
+
return {
|
|
699
|
+
memoryRead: EMPTY_MEMORY_READ,
|
|
700
|
+
tokenPrediction: createNoopPrediction(input),
|
|
701
|
+
scoringResult: {
|
|
702
|
+
decision: {
|
|
703
|
+
action: "continue",
|
|
704
|
+
score: 0,
|
|
705
|
+
reasoning: "MetaGovernor disabled",
|
|
706
|
+
evidence: [],
|
|
707
|
+
shouldEscalateTo: null
|
|
708
|
+
},
|
|
709
|
+
contributions: [],
|
|
710
|
+
rawScore: 0,
|
|
711
|
+
paralysisOverride: false,
|
|
712
|
+
computedAtISO: new Date().toISOString()
|
|
713
|
+
},
|
|
714
|
+
decision: NO_OP_DECISION,
|
|
715
|
+
lessonSaved: null,
|
|
716
|
+
decisionHistory: [],
|
|
717
|
+
skipped: true,
|
|
718
|
+
skipReason: "disabled"
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
let memoryRead = EMPTY_MEMORY_READ;
|
|
722
|
+
if (mergedConfig.memory.enabled) {
|
|
723
|
+
try {
|
|
724
|
+
const backends = {
|
|
725
|
+
agentmemory: input.backends.agentmemory,
|
|
726
|
+
magicContext: input.backends.magicContext,
|
|
727
|
+
boulderState: input.backends.boulderState
|
|
728
|
+
};
|
|
729
|
+
memoryRead = await aggregateRead({
|
|
730
|
+
directory: ".",
|
|
731
|
+
sessionID: input.sessionID,
|
|
732
|
+
query: mergedConfig.memory.query || input.toolName
|
|
733
|
+
}, backends);
|
|
734
|
+
} catch {}
|
|
735
|
+
}
|
|
736
|
+
let tokenPrediction;
|
|
737
|
+
try {
|
|
738
|
+
tokenPrediction = predict({
|
|
739
|
+
currentUsage: input.recentTurnTokens.reduce((a, b) => a + b, 0),
|
|
740
|
+
modelLimit: 200000,
|
|
741
|
+
recentTurnTokens: input.recentTurnTokens,
|
|
742
|
+
timestampISO: new Date().toISOString(),
|
|
743
|
+
providerID: input.providerID ?? "",
|
|
744
|
+
modelID: input.modelID ?? "",
|
|
745
|
+
config: mergedConfig.tokenPredictor
|
|
746
|
+
});
|
|
747
|
+
} catch {
|
|
748
|
+
tokenPrediction = createNoopPrediction(input);
|
|
749
|
+
}
|
|
750
|
+
const scoringResult = score(buildDecisionContext(input, memoryRead), mergedConfig.scoring);
|
|
751
|
+
const decisionInput = {
|
|
752
|
+
sessionID: input.sessionID,
|
|
753
|
+
scoringResult
|
|
754
|
+
};
|
|
755
|
+
const decision = handleDecision(decisionInput, mergedConfig.decision);
|
|
756
|
+
let lessonSaved = null;
|
|
757
|
+
try {
|
|
758
|
+
const learnConfig = mergedConfig.closedLoop;
|
|
759
|
+
if (learnConfig.enabled !== false) {
|
|
760
|
+
lessonSaved = await observeAndLearn({
|
|
761
|
+
decision: decision.historyEntry.decision,
|
|
762
|
+
memoryRead,
|
|
763
|
+
config: { ...defaultClosedLoopConfig(), ...mergedConfig.closedLoop },
|
|
764
|
+
sessionID: input.sessionID,
|
|
765
|
+
directory: ".",
|
|
766
|
+
filesChanged: []
|
|
767
|
+
}, input.writeBackend);
|
|
768
|
+
}
|
|
769
|
+
} catch {}
|
|
770
|
+
return {
|
|
771
|
+
memoryRead,
|
|
772
|
+
tokenPrediction,
|
|
773
|
+
scoringResult,
|
|
774
|
+
decision,
|
|
775
|
+
lessonSaved,
|
|
776
|
+
decisionHistory: [decision.historyEntry],
|
|
777
|
+
skipped: false
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function createNoopPrediction(input) {
|
|
781
|
+
const totalTokens = input.recentTurnTokens.reduce((a, b) => a + b, 0);
|
|
782
|
+
return {
|
|
783
|
+
burnRate: 0,
|
|
784
|
+
budgetLeft: 200000,
|
|
785
|
+
currentUsage: totalTokens,
|
|
786
|
+
modelLimit: 200000,
|
|
787
|
+
willOverflowAt: null,
|
|
788
|
+
recommendation: "no-action",
|
|
789
|
+
confidence: 1,
|
|
790
|
+
windowRemaining: 200000,
|
|
791
|
+
input: {
|
|
792
|
+
currentUsage: totalTokens,
|
|
793
|
+
modelLimit: 200000,
|
|
794
|
+
recentTurnTokens: input.recentTurnTokens,
|
|
795
|
+
timestampISO: new Date().toISOString(),
|
|
796
|
+
providerID: input.providerID ?? "",
|
|
797
|
+
modelID: input.modelID ?? "",
|
|
798
|
+
config: {
|
|
799
|
+
compactBurnRateThreshold: 500,
|
|
800
|
+
compactUsageThreshold: 0.85,
|
|
801
|
+
switchModelUsageThreshold: 0.95,
|
|
802
|
+
delegateConsecutiveHighBurn: 5,
|
|
803
|
+
windowSize: 10
|
|
804
|
+
}
|
|
805
|
+
},
|
|
806
|
+
computedAtISO: new Date().toISOString(),
|
|
807
|
+
turnsAnalyzed: input.recentTurnTokens.length
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/config.ts
|
|
812
|
+
function loadOrchestratorConfig(pluginConfig) {
|
|
813
|
+
const full = {
|
|
814
|
+
enabled: false,
|
|
815
|
+
...pluginConfig
|
|
816
|
+
};
|
|
817
|
+
const baseScoring = defaultScoringConfig();
|
|
818
|
+
const baseDecision = defaultDecisionHandlerConfig();
|
|
819
|
+
const baseClosedLoop = defaultClosedLoopConfig();
|
|
820
|
+
return {
|
|
821
|
+
enabled: full.enabled === true,
|
|
822
|
+
memory: {
|
|
823
|
+
enabled: true,
|
|
824
|
+
query: full.memory?.query ?? "meta_governor_context",
|
|
825
|
+
timeoutMs: full.memory?.agentmemoryTimeoutMs ?? 2000
|
|
826
|
+
},
|
|
827
|
+
tokenPredictor: {
|
|
828
|
+
compactBurnRateThreshold: full.tokenPredictor?.compactBurnRateThreshold ?? 500,
|
|
829
|
+
compactUsageThreshold: full.tokenPredictor?.compactUsageThreshold ?? 0.85,
|
|
830
|
+
switchModelUsageThreshold: full.tokenPredictor?.switchModelUsageThreshold ?? 0.95,
|
|
831
|
+
delegateConsecutiveHighBurn: full.tokenPredictor?.delegateConsecutiveHighBurn ?? 5
|
|
832
|
+
},
|
|
833
|
+
scoring: {
|
|
834
|
+
...baseScoring,
|
|
835
|
+
...full.scoring?.continueThreshold !== undefined ? { continueThreshold: full.scoring.continueThreshold } : {},
|
|
836
|
+
...full.scoring?.warnThreshold !== undefined ? { warnThreshold: full.scoring.warnThreshold } : {},
|
|
837
|
+
...full.scoring?.escalateThreshold !== undefined ? { escalateThreshold: full.scoring.escalateThreshold } : {},
|
|
838
|
+
...full.scoring?.stopThreshold !== undefined ? { stopThreshold: full.scoring.stopThreshold } : {}
|
|
839
|
+
},
|
|
840
|
+
closedLoop: {
|
|
841
|
+
...baseClosedLoop,
|
|
842
|
+
...full.closedLoop?.saveDecisions !== undefined ? { saveDecisions: full.closedLoop.saveDecisions } : {}
|
|
843
|
+
},
|
|
844
|
+
decision: {
|
|
845
|
+
...baseDecision,
|
|
846
|
+
...full.decision?.maxHistoryPerSession !== undefined ? { maxHistoryPerSession: full.decision.maxHistoryPerSession } : {},
|
|
847
|
+
...full.decision?.forceContinueAfterStops !== undefined ? { forceContinueAfterStops: full.decision.forceContinueAfterStops } : {}
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function isMetaGovernorEnabled(config) {
|
|
852
|
+
return config?.enabled === true;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// src/plugin.ts
|
|
856
|
+
function createMetaGovernorPlugin(deps = {}) {
|
|
857
|
+
const plugin = async (input, options) => {
|
|
858
|
+
const rawConfig = options?.meta_governor ?? {};
|
|
859
|
+
const config = loadOrchestratorConfig(rawConfig);
|
|
860
|
+
if (!config.enabled) {
|
|
861
|
+
return {};
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
"tool.execute.after": async (toolInput, toolOutput) => {
|
|
865
|
+
if (!config.enabled)
|
|
866
|
+
return;
|
|
867
|
+
const orchestratorInput = {
|
|
868
|
+
sessionID: toolInput.sessionID,
|
|
869
|
+
toolName: toolInput.tool,
|
|
870
|
+
toolOutput: toolOutput.output,
|
|
871
|
+
iteration: 0,
|
|
872
|
+
maxIterations: 10,
|
|
873
|
+
oracleVerified: false,
|
|
874
|
+
noProgress: false,
|
|
875
|
+
filesChanged: 0,
|
|
876
|
+
recentTurnTokens: [],
|
|
877
|
+
deviations: [],
|
|
878
|
+
backends: deps.backends ?? {
|
|
879
|
+
agentmemory: {
|
|
880
|
+
smartSearch: async () => ({ lessons: [], crystals: [] })
|
|
881
|
+
},
|
|
882
|
+
magicContext: {
|
|
883
|
+
slotList: async () => []
|
|
884
|
+
},
|
|
885
|
+
boulderState: {
|
|
886
|
+
boulderRead: async () => []
|
|
887
|
+
}
|
|
888
|
+
},
|
|
889
|
+
writeBackend: deps.writeBackend ?? {
|
|
890
|
+
saveMemory: async () => ({ id: "" }),
|
|
891
|
+
saveLesson: async () => ({ id: "" })
|
|
892
|
+
},
|
|
893
|
+
config,
|
|
894
|
+
...deps.providerID ? { providerID: deps.providerID() } : {},
|
|
895
|
+
...deps.modelID ? { modelID: deps.modelID() } : {}
|
|
896
|
+
};
|
|
897
|
+
try {
|
|
898
|
+
await runMetaGovernor(orchestratorInput);
|
|
899
|
+
} catch (err) {
|
|
900
|
+
if (typeof console !== "undefined") {
|
|
901
|
+
console.error("[meta-governor] orchestrator error:", err);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
return plugin;
|
|
908
|
+
}
|
|
909
|
+
// src/post-repair-recorder.ts
|
|
910
|
+
function buildDecisionFromRecovery(outcome) {
|
|
911
|
+
const deviation = {
|
|
912
|
+
severity: outcome.success ? "leve" : "grave",
|
|
913
|
+
category: `recovery:${outcome.fixStrategy}`,
|
|
914
|
+
detail: `${outcome.errorCode}: ${outcome.context ?? "no context"}`
|
|
915
|
+
};
|
|
916
|
+
return {
|
|
917
|
+
action: outcome.success ? "continue" : "warn",
|
|
918
|
+
score: outcome.success ? 0.5 : -0.5,
|
|
919
|
+
reasoning: `Recovery ${outcome.success ? "succeeded" : "failed"}: ${outcome.fixStrategy} for ${outcome.errorCode}`,
|
|
920
|
+
evidence: [
|
|
921
|
+
{
|
|
922
|
+
source: "deviation-detector",
|
|
923
|
+
value: deviation.detail,
|
|
924
|
+
confidence: 1,
|
|
925
|
+
weight: outcome.success ? 0.3 : 0.8
|
|
926
|
+
}
|
|
927
|
+
],
|
|
928
|
+
shouldEscalateTo: outcome.success ? null : "oracle"
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
var SEVERITY_ORDER2 = {
|
|
932
|
+
leve: 0,
|
|
933
|
+
media: 1,
|
|
934
|
+
grave: 2
|
|
935
|
+
};
|
|
936
|
+
function severityMeetsThreshold2(deviations, threshold) {
|
|
937
|
+
const minOrder = SEVERITY_ORDER2[threshold] ?? 0;
|
|
938
|
+
return deviations.some((d) => (SEVERITY_ORDER2[d.severity] ?? 0) >= minOrder);
|
|
939
|
+
}
|
|
940
|
+
async function recordRecovery(outcome, writeBackend, options) {
|
|
941
|
+
if (!writeBackend) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
const config = options?.config ?? defaultClosedLoopConfig();
|
|
945
|
+
const decision = buildDecisionFromRecovery(outcome);
|
|
946
|
+
if (!config.enabled) {
|
|
947
|
+
return { lessonSaved: null, decisionSaved: null, reason: "closed-loop learning disabled" };
|
|
948
|
+
}
|
|
949
|
+
const deviations = [{
|
|
950
|
+
severity: outcome.success ? "leve" : "grave",
|
|
951
|
+
category: `recovery:${outcome.fixStrategy}`,
|
|
952
|
+
detail: `${outcome.errorCode}: ${outcome.context ?? "no context"}`
|
|
953
|
+
}];
|
|
954
|
+
let lessonSaved = null;
|
|
955
|
+
let decisionSaved = null;
|
|
956
|
+
if (config.saveDecisions) {
|
|
957
|
+
const decisionRecord = {
|
|
958
|
+
id: `D-${Math.abs(`${outcome.sessionID}-${decision.action}-${Date.now()}`.split("").reduce((a, c) => (a << 5) - a + c.charCodeAt(0) | 0, 0)).toString(36)}`,
|
|
959
|
+
timestampISO: new Date().toISOString(),
|
|
960
|
+
action: decision.action,
|
|
961
|
+
score: decision.score,
|
|
962
|
+
reasoning: decision.reasoning,
|
|
963
|
+
sessionID: outcome.sessionID,
|
|
964
|
+
directory: outcome.directory,
|
|
965
|
+
deviations: deviations.map((d) => ({
|
|
966
|
+
severity: d.severity,
|
|
967
|
+
category: d.category,
|
|
968
|
+
detail: d.detail
|
|
969
|
+
}))
|
|
970
|
+
};
|
|
971
|
+
try {
|
|
972
|
+
await writeBackend.saveMemory({
|
|
973
|
+
content: `Decision: ${decision.action} (score ${decision.score.toFixed(2)}). ${decision.reasoning}`,
|
|
974
|
+
concepts: ["meta-governor", "decision", decision.action, ...deviations.map((d) => d.category)],
|
|
975
|
+
type: "fact",
|
|
976
|
+
files: [...outcome.filesChanged ?? []]
|
|
977
|
+
});
|
|
978
|
+
decisionSaved = decisionRecord;
|
|
979
|
+
} catch {}
|
|
980
|
+
}
|
|
981
|
+
if (severityMeetsThreshold2(deviations, config.minSeverityToLearn)) {
|
|
982
|
+
const concepts = [...new Set(deviations.flatMap((d) => [d.category, d.severity]))];
|
|
983
|
+
const deviationSummary = deviations.map((d) => `[${d.severity}] ${d.category}: ${d.detail}`).join("; ");
|
|
984
|
+
const content = `Action "${decision.action}" (score ${decision.score.toFixed(2)}) after deviations: ${deviationSummary}. Reasoning: ${decision.reasoning}`;
|
|
985
|
+
try {
|
|
986
|
+
const result = await writeBackend.saveLesson({
|
|
987
|
+
content,
|
|
988
|
+
context: `session:${outcome.sessionID} dir:${outcome.directory}`,
|
|
989
|
+
confidence: Math.max(0.3, Math.min(0.8, Math.abs(decision.score))),
|
|
990
|
+
tags: concepts
|
|
991
|
+
});
|
|
992
|
+
lessonSaved = {
|
|
993
|
+
id: result.id,
|
|
994
|
+
title: `${decision.action} after ${deviations[0]?.category ?? "recovery"}`,
|
|
995
|
+
content,
|
|
996
|
+
type: "pattern",
|
|
997
|
+
concepts,
|
|
998
|
+
confidence: Math.max(0.3, Math.min(0.8, Math.abs(decision.score))),
|
|
999
|
+
files: [...outcome.filesChanged ?? []],
|
|
1000
|
+
sessionID: outcome.sessionID
|
|
1001
|
+
};
|
|
1002
|
+
} catch {}
|
|
1003
|
+
}
|
|
1004
|
+
const reasons = [];
|
|
1005
|
+
if (decisionSaved)
|
|
1006
|
+
reasons.push("decision saved");
|
|
1007
|
+
if (lessonSaved) {
|
|
1008
|
+
reasons.push("lesson saved");
|
|
1009
|
+
} else if (!severityMeetsThreshold2(deviations, config.minSeverityToLearn)) {
|
|
1010
|
+
reasons.push("severity below threshold");
|
|
1011
|
+
}
|
|
1012
|
+
if (!decisionSaved && !lessonSaved && !reasons.length) {
|
|
1013
|
+
reasons.push("no saveable content");
|
|
1014
|
+
}
|
|
1015
|
+
return {
|
|
1016
|
+
lessonSaved,
|
|
1017
|
+
decisionSaved,
|
|
1018
|
+
reason: reasons.join("; ") || "no action taken"
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/index.ts
|
|
1023
|
+
var pluginModule = {
|
|
1024
|
+
id: "omo-meta-governor",
|
|
1025
|
+
server: createMetaGovernorPlugin()
|
|
1026
|
+
};
|
|
1027
|
+
var src_default = pluginModule;
|
|
1028
|
+
export {
|
|
1029
|
+
trimHistory,
|
|
1030
|
+
score,
|
|
1031
|
+
runMetaGovernor,
|
|
1032
|
+
recordRecovery,
|
|
1033
|
+
predict,
|
|
1034
|
+
observeAndLearn,
|
|
1035
|
+
loadOrchestratorConfig,
|
|
1036
|
+
isMetaGovernorEnabled,
|
|
1037
|
+
handleDecision,
|
|
1038
|
+
defaultTokenPredictorConfig,
|
|
1039
|
+
defaultScoringConfig,
|
|
1040
|
+
defaultOrchestratorConfig,
|
|
1041
|
+
defaultDecisionHandlerConfig,
|
|
1042
|
+
defaultClosedLoopConfig,
|
|
1043
|
+
src_default as default,
|
|
1044
|
+
createMetaGovernorPlugin,
|
|
1045
|
+
countConsecutiveStops,
|
|
1046
|
+
calculateBurnRate,
|
|
1047
|
+
buildDecisionContext,
|
|
1048
|
+
aggregateRead
|
|
1049
|
+
};
|