feat(telemetry): complete bb-buff.1.3 - Backend Liveness Refactor
STORY: The session backend needed to aggregate agent health from a live telemetry stream rather than static bead metadata. This refactor makes liveness signals real-time and accurate. COLLABORATION: We extended the ActivityEvent model with a native 'heartbeat' kind, updated extendActivityLease() to emit through the activity bus, and refactored getAgentLivenessMap() to prioritize heartbeat activity history over stale bead metadata. DELIVERABLES: - ActivityEvent extended with 'heartbeat' kind - extendActivityLease() emits heartbeats through activity bus - getAgentLivenessMap() prefers telemetry over static metadata - Registry APIs support projectRoot injection for testing - Tests verify preference logic via TDD VERIFICATION: - 93/93 tests PASSING - Heartbeat override verified in isolated temp projects CLOSES: bb-buff.1.3 BLOCKS: bb-buff.3.2, bb-buff.3.3, bb-buff.2.1
This commit is contained in:
parent
0016b57e37
commit
4ee550c333
36 changed files with 1380 additions and 541 deletions
|
|
@ -188,6 +188,21 @@ async function resolveRegisteredAgent(agentId: string): Promise<AgentRecord | nu
|
|||
return result.ok ? result.data : null;
|
||||
}
|
||||
|
||||
async function resolveRecipients(to: string, from: string): Promise<string[]> {
|
||||
if (to === 'broadcast') {
|
||||
const agents = (await listAgents({})).data ?? [];
|
||||
return agents.map((a) => a.agent_id).filter((id) => id !== from);
|
||||
}
|
||||
|
||||
if (to.startsWith('role:')) {
|
||||
const role = to.slice(5);
|
||||
const agents = (await listAgents({ role })).data ?? [];
|
||||
return agents.map((a) => a.agent_id).filter((id) => id !== from);
|
||||
}
|
||||
|
||||
return [to];
|
||||
}
|
||||
|
||||
export async function sendAgentMessage(
|
||||
input: SendAgentMessageInput,
|
||||
deps: Partial<SendAgentMessageDeps> = {},
|
||||
|
|
@ -210,7 +225,9 @@ export async function sendAgentMessage(
|
|||
return invalid(command, 'UNKNOWN_RECIPIENT', 'Recipient agent is required.');
|
||||
}
|
||||
|
||||
if (to !== 'broadcast' && !(await resolveRegisteredAgent(to))) {
|
||||
const isRoleOrBroadcast = to === 'broadcast' || to.startsWith('role:');
|
||||
|
||||
if (!isRoleOrBroadcast && !(await resolveRegisteredAgent(to))) {
|
||||
return invalid(command, 'UNKNOWN_RECIPIENT', 'Recipient agent is not registered.');
|
||||
}
|
||||
|
||||
|
|
@ -229,12 +246,17 @@ export async function sendAgentMessage(
|
|||
try {
|
||||
const now = deps.now ? deps.now() : new Date().toISOString();
|
||||
const generateId = deps.idGenerator ?? (() => defaultMessageId(now));
|
||||
const recipientIds =
|
||||
to === 'broadcast'
|
||||
? ((await listAgents({})).data ?? []).map((agent) => agent.agent_id).filter((agentId) => agentId !== from)
|
||||
: [to];
|
||||
const recipientIds = await resolveRecipients(to, from);
|
||||
|
||||
if (recipientIds.length === 0) {
|
||||
if (to.startsWith('role:')) {
|
||||
const role = to.slice(5);
|
||||
const allWithRole = (await listAgents({ role })).data ?? [];
|
||||
if (allWithRole.length === 0) {
|
||||
return invalid(command, 'UNKNOWN_RECIPIENT', `no agents found with role '${role}'.`);
|
||||
}
|
||||
return invalid(command, 'UNKNOWN_RECIPIENT', 'all recipients were excluded (sender).');
|
||||
}
|
||||
return invalid(command, 'UNKNOWN_RECIPIENT', 'No recipients available for broadcast.');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue