beadboard/tests/lib/agent-liveness.test.ts
zenchantlive eec1d6e28f feat(protocol): deliver 'War Room' UI with Incursion Engine
We've transformed the Social-Dense Hub into a high-fidelity operational surface.
- BACKEND: Implemented Global Incursion Engine in agent-sessions.ts (N^2 overlap detection) and added the 60m 'Idle' state.
- API: Enriched the sessions payload with full metadata and active conflict arrays.
- HEADER: Delivered 4-state agent stations (Active/Stale/Evicted/Idle) with real-time 'time-ago' timers.
- FEED: Implemented the 'Fire Map' visuals:
  * Global Incursion Ticker: High-visibility alerts for agent collisions.
  * Local Conflict Badges: Pulsing pills on affected task cards.
- Refactored components for React-static compliance and strict TypeScript safety.

This commit completes the visibility track, allowing the human supervisor to monitor agent presence and friction in real-time.

OPERATIVE: silver-castle
SESSION: 2026-02-14-1430
2026-02-14 11:36:32 -08:00

112 lines
2.9 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import {
registerAgent,
extendActivityLease,
deriveLiveness,
agentFilePath,
} from '../../src/lib/agent-registry';
async function withTempUserProfile(run: () => Promise<void>): Promise<void> {
const previous = process.env.USERPROFILE;
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'beadboard-agent-liveness-'));
process.env.USERPROFILE = tempDir;
try {
await run();
} finally {
if (previous === undefined) {
delete process.env.USERPROFILE;
} else {
process.env.USERPROFILE = previous;
}
await fs.rm(tempDir, { recursive: true, force: true });
}
}
test('extendActivityLease updates last_seen_at and increments version', async () => {
await withTempUserProfile(async () => {
const start = '2026-02-14T10:00:00.000Z';
const next = '2026-02-14T10:05:00.000Z';
await registerAgent(
{ name: 'active-agent', role: 'infra' },
{ now: () => start }
);
const result = await extendActivityLease(
{ agent: 'active-agent' },
{ now: () => next }
);
assert.equal(result.ok, true);
assert.equal(result.data?.last_seen_at, next);
assert.equal(result.data?.version, 2);
const raw = await fs.readFile(agentFilePath('active-agent'), 'utf8');
const parsed = JSON.parse(raw);
assert.equal(parsed.last_seen_at, next);
});
});
test('deriveLiveness follows threshold rules (15m/30m default)', () => {
const now = new Date('2026-02-14T12:00:00Z');
// Active: 14 mins ago
assert.equal(
deriveLiveness('2026-02-14T11:46:00Z', now),
'active'
);
// Stale: Exactly 15 mins ago
assert.equal(
deriveLiveness('2026-02-14T11:45:00Z', now),
'stale'
);
// Stale: 29 mins ago
assert.equal(
deriveLiveness('2026-02-14T11:31:00Z', now),
'stale'
);
// Evicted: Exactly 30 mins ago
assert.equal(
deriveLiveness('2026-02-14T11:30:00Z', now),
'evicted'
);
// Evicted: 1 hour ago
// Note: Since we added Idle at 60m, let's test 59m for Evicted and 60m for Idle
assert.equal(
deriveLiveness('2026-02-14T11:01:00Z', now),
'evicted'
);
// Idle: Exactly 60 mins ago
assert.equal(
deriveLiveness('2026-02-14T11:00:00Z', now),
'idle'
);
// Idle: 2 hours ago
assert.equal(
deriveLiveness('2026-02-14T10:00:00Z', now),
'idle'
);
});
test('deriveLiveness respects custom staleMinutes', () => {
const now = new Date('2026-02-14T12:00:00Z');
const customThreshold = 5; // 5m stale, 10m evicted
assert.equal(deriveLiveness('2026-02-14T11:56:00Z', now, customThreshold), 'active');
assert.equal(deriveLiveness('2026-02-14T11:55:00Z', now, customThreshold), 'stale');
assert.equal(deriveLiveness('2026-02-14T11:51:00Z', now, customThreshold), 'stale');
assert.equal(deriveLiveness('2026-02-14T11:50:00Z', now, customThreshold), 'evicted');
});