fix(voice): throttle repeated pod alerts
This commit is contained in:
@@ -57,6 +57,8 @@ export interface GitState {
|
||||
gitUpdatedAt: Date | null;
|
||||
}
|
||||
|
||||
const GIT_STATE_TTL_MS = Number(process.env.GIT_STATE_TTL_MS ?? '120000');
|
||||
|
||||
/** Fetch latest git state per engineer for a pod from the engineer_states collection.
|
||||
* Returns a map keyed by engineer name (matches --name arg used in podman-agent.mjs). */
|
||||
export async function getGitStates(podId: string): Promise<Map<string, GitState>> {
|
||||
@@ -71,7 +73,17 @@ export async function getGitStates(podId: string): Promise<Map<string, GitState>
|
||||
}>('engineer_states');
|
||||
const docs = await col.find({ podId }).toArray();
|
||||
const map = new Map<string, GitState>();
|
||||
const now = Date.now();
|
||||
for (const doc of docs) {
|
||||
const updatedAt = doc.gitUpdatedAt ? new Date(doc.gitUpdatedAt) : null;
|
||||
if (
|
||||
updatedAt &&
|
||||
!Number.isNaN(updatedAt.getTime()) &&
|
||||
GIT_STATE_TTL_MS > 0 &&
|
||||
now - updatedAt.getTime() > GIT_STATE_TTL_MS
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
map.set(doc.name, {
|
||||
changedFiles: doc.changedFiles ?? [],
|
||||
branch: doc.branch ?? null,
|
||||
|
||||
@@ -16,7 +16,7 @@ export function shouldIntervene(collision: Collision, prior: RecalledCollision |
|
||||
|
||||
const cooldown = cooldownMs();
|
||||
const last = lastNudgeByPod.get(collision.podId) ?? 0;
|
||||
if (cooldown > 0 && Date.now() - last < cooldown && collision.severity !== 'critical') {
|
||||
if (cooldown > 0 && Date.now() - last < cooldown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,15 @@ import type {
|
||||
import { collections } from './db.js';
|
||||
import { enrichCollisionMemory } from './vectors.js';
|
||||
|
||||
function comparableFile(raw?: string): string {
|
||||
return (raw ?? '')
|
||||
.trim()
|
||||
.replace(/^(\?\?|[MADRCU!]{1,2})\s+/, '')
|
||||
.split(/[\\/]/)
|
||||
.pop()
|
||||
?.toLowerCase() ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Continual-learning memory: persist observations, collisions, interventions,
|
||||
* and outcomes to MongoDB so later sessions get sharper. MongoDB is mandatory —
|
||||
@@ -41,6 +50,31 @@ export async function recordIntervention(intervention: Intervention): Promise<vo
|
||||
);
|
||||
}
|
||||
|
||||
export async function hasRecentInterventionForCollision(
|
||||
collision: Collision,
|
||||
windowMs = Number(process.env.NUDGE_COOLDOWN_MS ?? '180000'),
|
||||
): Promise<boolean> {
|
||||
if (windowMs <= 0) return false;
|
||||
const c = await collections();
|
||||
const since = new Date(Date.now() - windowMs).toISOString();
|
||||
const recent = await c.collisions
|
||||
.find({ podId: collision.podId, detectedAt: { $gte: since } })
|
||||
.sort({ detectedAt: -1 })
|
||||
.limit(100)
|
||||
.toArray();
|
||||
|
||||
const targetFile = comparableFile(collision.file);
|
||||
for (const match of recent) {
|
||||
if (match.id === collision.id || comparableFile(match.file) !== targetFile) continue;
|
||||
const existing = await c.interventions.findOne({
|
||||
collisionId: match.id,
|
||||
createdAt: { $gte: since },
|
||||
});
|
||||
if (existing) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function updateInterventionStatus(
|
||||
interventionId: string,
|
||||
status: InterventionStatus,
|
||||
|
||||
Reference in New Issue
Block a user