kevinbadi/ai-os-skills · Archived

social-inbox-agent

AI-powered social inbox agent — responds to inbound DMs on Instagram, Twitter, LinkedIn, and YouTube as {{PERSONA_NAME}} via Late API + Gemini, with PostgreSQL message tracking

First seen Apr 8, 2026

Installation

$ npx skills add kevinbadi/ai-os-skills --skill social-inbox-agent

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from kevinbadi/ai-os-skills · top by installs.

npx skills add kevinbadi/ai-os-skills

Browse all from kevinbadi/ai-os-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 55
License MIT
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,156 B
  • docs SUMMARY.md 204 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4 installs

SKILL.md

What It Does

Automatically responds to inbound DMs across all connected social platforms. Uses PostgreSQL as the single source of truth for all message tracking — no file-based state, no race conditions, no self-reply loops.

How to Run

# Process all unreplied messages
node skills/social-inbox-agent/scripts/dm-response-agent.js

# Dry run — see what would be sent without sending
node skills/social-inbox-agent/scripts/dm-response-agent.js --dry-run

# Limit to N conversations
node skills/social-inbox-agent/scripts/dm-response-agent.js --limit 5

# Filter to specific platform
node skills/social-inbox-agent/scripts/dm-response-agent.js --platform instagram

Requires env vars: LATEAPIKEY, GEMINIAPIKEY, DATABASE_URL

Message Workflow

1. FETCH — Get all conversations from Late API (GET /v1/inbox/conversations)
2. For each conversation:
   a. FETCH messages (GET /v1/inbox/conversations/{id}/messages)
   b. SYNC — Store all incoming messages in PostgreSQL (social_inbox table)
   c. SAFETY CHECKS — Run all anti-spam guards (see below)
   d. FIND — Identify the latest unreplied incoming message
   e. GENERATE — Create reply via Gemini with conversation context
   f. SEND — Post reply via Late API
   g. TRACK — Atomically store outgoing reply + mark incoming as replied in DB

Database Tracking (social_inbox table)

Column Purpose
latemessageid Unique Late API message ID (unique constraint)
lateconversationid Groups messages by conversation
direction incoming or outgoing
replied Boolean — has this incoming message been replied to?
replied_at Timestamp of when we replied
reply_text The reply we sent (or [SKIPPED])
message_text The original message content
sender_name Who sent it
platform instagram, twitter, linkedin, youtube

Key DB queries the agent uses:

  • Is this message already replied?SELECT replied FROM socialinbox WHERE latemessage_id = $1
  • Is this text our own reply?SELECT id FROM socialinbox WHERE direction = 'outgoing' AND messagetext = $1
  • Spam ratio?SELECT direction, COUNT(*) FROM socialinbox WHERE lateconversation_id = $1 GROUP BY direction

Safety Rules (Anti-Spam)

These were added after a 200-message spam incident on 2026-03-14:

  1. ONE reply per conversation per run — even if 50 unreplied messages exist, sends exactly ONE reply to the latest
  2. Never double-message — if the last message in the conversation is ours (outgoing), skip entirely
  3. Never reply to self — outgoing messages tracked in DB; if conversation's lastMessage matches our DB outgoing text, skip
  4. Spam ratio guard — if outgoing > 2× incoming (and > 3 total), skip the conversation
  5. Pagination-aware — Late API caps at 100 messages; agent cross-references conversation.lastMessage with DB to detect messages beyond the pagination window
  6. Atomic tracking — reply send + DB update in a single transaction; no partial states

Beyond-Pagination Logic

When a conversation has 100+ messages (e.g., after a spam incident), the messages endpoint won't return the newest ones. The agent handles this:

  1. Compares conversation.updatedTime vs last visible message timestamp
  2. If conversation was updated >30s after last visible message → newer message exists
  3. Checks DB: is conversation.lastMessage text one of our outgoing replies? → skip
  4. If not our reply → creates a synthetic incoming record with a text-based ID (base64 of message content), generates reply, tracks in DB
  5. Text-based synthetic IDs ensure the same incoming message always deduplicates, even when updatedTime changes from our own replies

Response Strategy

Loads persona from onboarding-config.json (phase1.*):

  • Persona name, backstory, voice, brand info, target audience, primary CTA

Response Rules

  • Keep replies short (1-3 sentences for simple, up to 5 for detailed questions)
  • No markdown formatting — this is a DM, not a document
  • Max 1-2 emojis per message, only if natural
  • Answer questions first, then guide toward offer if relevant
  • Don't be salesy or pushy — be genuinely helpful
  • Never break character or mention being AI
  • Skip low-effort messages (just emojis, etc.) with [SKIP]

Setup Requirements

  • PostgreSQL with socialinbox table (unique constraint on latemessage_id)
  • Late API account with connected social platforms
  • Gemini API key for reply generation
  • Platform account IDs in onboarding-config.jsonphase2.platforms.*.late_id

Cron Configuration

Designed to run every 1 minute via cron. Safe for concurrent/isolated runs because all state is in PostgreSQL, not local files.

Schedule: * * * * *
Target: isolated
Command: node skills/social-inbox-agent/scripts/dm-response-agent.js --limit 10