← 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,
  };
};