@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/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
+ };