← back to Norma
agents/moveon-agent/skills/report.js
119 lines
/**
* MoveOn Report Skill
*
* Gathers agent stats and recent actions, then pushes to Pulse.
*/
const { logAction, getActionSummary } = require('../../shared/audit-logger');
const { reportAgentStatus, pushSocialFeed } = require('../../shared/pulse-reporter');
const { query: dbQuery } = require('../../shared/db');
const AGENT = 'moveon-agent';
const PLATFORM = 'moveon';
/**
* @param {Object} body - Request body
* @param {number} [body.hours_back=24] - How many hours back to report
* @returns {Promise<Object>}
*/
module.exports = async function report(body) {
const hoursBack = body.hours_back || 24;
// Get action summary for the time window
const actionSummary = await getActionSummary(AGENT, hoursBack);
// Get pipeline stats
let pipelineStats = { total: 0, drafts: 0, posted: 0, active: 0, totalSignatures: 0 };
try {
const { rows } = await dbQuery(
`SELECT
COUNT(*)::int as total,
COUNT(*) FILTER (WHERE status = 'draft')::int as drafts,
COUNT(*) FILTER (WHERE status = 'posted')::int as posted,
COUNT(*) FILTER (WHERE status = 'active')::int as active,
COALESCE(SUM(signature_count), 0)::int as total_signatures
FROM petition_pipeline
WHERE platform = 'moveon'`
);
if (rows[0]) {
pipelineStats = {
total: rows[0].total,
drafts: rows[0].drafts,
posted: rows[0].posted,
active: rows[0].active,
totalSignatures: rows[0].total_signatures,
};
}
} catch (err) {
console.error(`[${AGENT}] Failed to get pipeline stats:`, err.message);
}
// Get top petitions by signatures
let topPetitions = [];
try {
const { rows } = await dbQuery(
`SELECT id, title, platform_url, signature_count, signature_goal, status, last_tracked_at
FROM petition_pipeline
WHERE platform = 'moveon' AND signature_count > 0
ORDER BY signature_count DESC
LIMIT 5`
);
topPetitions = rows;
} catch (err) {
console.error(`[${AGENT}] Failed to get top petitions:`, err.message);
}
const reportData = {
agent: AGENT,
platform: PLATFORM,
period_hours: hoursBack,
actions: actionSummary,
pipeline: pipelineStats,
top_petitions: topPetitions,
uptime: process.uptime(),
reported_at: new Date().toISOString(),
};
// Push to Pulse
const pulseResult = await reportAgentStatus(AGENT, {
state: 'running',
uptime: process.uptime(),
lastAction: actionSummary.length > 0 ? actionSummary[0] : null,
stats: {
pipeline: pipelineStats,
actionsInPeriod: actionSummary.reduce((sum, a) => sum + a.count, 0),
},
});
// Also push top petitions as social feed items
for (const petition of topPetitions) {
await pushSocialFeed({
agent: AGENT,
platform: PLATFORM,
type: 'petition',
title: petition.title,
url: petition.platform_url,
content: `${petition.signature_count} signatures${petition.signature_goal ? ` / goal: ${petition.signature_goal}` : ''}`,
metadata: {
signatures: petition.signature_count,
goal: petition.signature_goal,
status: petition.status,
},
});
}
// Log the report action
await logAction({
agent: AGENT,
actionType: 'report',
platform: PLATFORM,
responseData: reportData,
status: 'success',
});
return {
...reportData,
pulse_response: pulseResult,
};
};