x-dm-auto-chat — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited x-dm-auto-chat (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Full X DM automation Skill: inbox scan → conversation read → persona-based reply → send; also supports search-and-outreach. The calling Agent generates reply text based on persona; this Skill handles all mechanical operations.
All process output to user (progress updates, process notifications) follows the user's language.
Encapsulate "refresh DM list → identify pending replies → read context → reply with persona → send" and "search user → enter chat → send first message" into callable end-to-end capabilities.
[aria-label="Account menu"] present)"You are BrowserAct outreach team. Tone: friendly, concise, professional. Goal: invite creators to collaborate."If browser-act has been confirmed available in the current session → skip.
Invoke browser-act via Skill tool to load usage. If installation or configuration issues arise, follow its guidance to resolve then retry.
browser-act --session <name> navigate https://x.com/i/chat
browser-act --session <name> wait stable --timeout 15000
browser-act --session <name> eval "$(python scripts/check-page-state.py)"Return format:
{
"url": "https://x.com/i/chat/pin/recovery?from=%2Fi%2Fchat",
"logged_in": true,
"need_passcode": true,
"on_inbox": false,
"on_conversation": false,
"has_panel": false,
"has_composer": false,
"inbox_count": 0
}Decision matrix:
logged_in: false → inform user to log in first; wait; retry this stepneed_passcode: true → proceed to step 3 belowon_inbox: true and inbox_count > 0 → ready, enter business flowon_inbox: true but inbox_count === 0 → account has no DM conversations; outreach scenario can still proceed, pending-reply scenario has nothing to dobrowser-act --session <name> state — find indexes of 4 <input maxlength=1 pattern=[0-9]*> elements (usually 4 consecutive)browser-act --session <name> input <idx1> "<d1>", <idx2> "<d2>", <idx3> "<d3>", <idx4> "<d4>"browser-act --session <name> wait stable --timeout 10000check-page-state.py, confirm need_passcode: false and on_inbox: trueneed_passcode: true → inform user passcode may be wrong; terminateChoose Scenario A, Scenario B, or both. Each scenario is an ordered AI Workflow (not a single JS).
Flow: Scan inbox → Filter unread & latest peer messages → Per-conversation: read context → Generate reply with persona → Send → Next
Steps:
browser-act --session <name> eval "$(python scripts/scan-inbox-merged.py)"Returns items[], each containing conversation_id / conversation_url / peer_screen_name / peer_display_name / peer_can_dm / latest_message_preview / latest_message_from_self / unread, etc.
items, select conversations meeting all conditions:unread === true (has unread) or latest_message_from_self === false (peer's latest message not yet replied)peer_can_dm === true (recipient allows DM)is_muted !== true and is_deleted_by_viewer !== truea. Open conversation:
browser-act --session <name> navigate https://x.com<conversation_url>
browser-act --session <name> wait stable --timeout 15000b. If passcode re-triggered → re-unlock (usually won't re-trigger within same session)
c. Read context:
browser-act --session <name> eval "$(python scripts/read-conversation.py)"Returns messages[], each with direction (self/peer), text, timestamp_text, links, images.
d. (Optional) Load full history: If caller needs longer context, loop:
browser-act --session <name> eval "$(python scripts/scroll-load-history.py)"Until reached_top: true, then re-read with read-conversation.py.
e. Generate reply: Calling Agent combines persona, message history to generate reply text. Reply content is entirely the caller's decision; this Skill does not participate in generation. Suggested inputs:
messages.slice(-6))peer_display_name / peer_screen_name) for addressreply_text, length < 10,000 charactersf. Send reply:
browser-act --session <name> eval "$(python scripts/check-composer.py)" → record last_message_idbrowser-act --session <name> state — find <textarea placeholder=Message> index TA_IDXbrowser-act --session <name> input <TA_IDX> "<reply_text>" (must use CDP real keyboard, cannot use eval)browser-act --session <name> wait --selector '[data-testid="dm-composer-send-button"]' --state attached --timeout 5000browser-act --session <name> eval "document.querySelector('[data-testid=\"dm-composer-send-button\"]').click(); 'clicked'"browser-act --session <name> wait stable --timeout 15000browser-act --session <name> eval "$(python scripts/verify-sent.py '<reply_text>' --prev-last-id <last_message_id from step f1>)"sent: true and composer_cleared: true → success, record resultsent: false → record failure, do not retry (prevents duplicate sends); proceed to next conversationg. Random delay: sleep 8-15 seconds (avoid anti-abuse limits)
Flow: Search candidates → Filter sendable → Enter conversation → Generate first message → Send
Steps:
browser-act --session <name> eval "$(python scripts/search-users.py '<search_query>')"Returns users[], each with user_id / name / screen_name / can_dm / can_dm_reason / verification fields.
can_dm === true and !suspended and !protectedcan_dm_reason === "Allowed"screen_name is already in send history → skip (deduplication)sleep 10-20 seconds between each):a. Calculate conversation URL:
browser-act --session <name> eval "$(python scripts/open-conversation-by-user.py '<user_id>')"Returns conversation_url (e.g., /i/chat/{smaller_id}-{larger_id}).
b. Navigate to conversation:
browser-act --session <name> navigate https://x.com<conversation_url>
browser-act --session <name> wait stable --timeout 15000c. Handle passcode (may appear on first DM entry) → unlock
d. Verify composer ready:
browser-act --session <name> eval "$(python scripts/check-composer.py)"composer_ready: true → record last_message_id; false → skip this user
e. Generate first message: Calling Agent generates first outreach text first_text based on persona + target user info (screen_name / name / verification type). Suggested content:
f. Send: Follow the 7 sub-steps in "Scenario A step 3f", substituting first_text for reply_text.
g. Random delay: sleep 10-20 seconds
In addition to the Scenario A / B end-to-end flows, the following components can also be called directly:
browser-act --session <name> eval "$(python scripts/scan-inbox-merged.py)" Returns merged conversation list with peer screen_name + message preview + unread flag.
browser-act --session <name> eval "$(python scripts/fetch-inbox-api.py --cursor-id {cursor_id} --graph-snapshot-id {snap} --limit {N})"
browser-act --session <name> eval "$(python scripts/read-conversation.py)"
browser-act --session <name> eval "$(python scripts/scroll-load-history.py)"
browser-act --session <name> eval "$(python scripts/check-composer.py)"
browser-act --session <name> eval "$(python scripts/verify-sent.py '<expected_text>' --prev-last-id <last_id>)"
browser-act --session <name> eval "$(python scripts/search-users.py '<query>')"
browser-act --session <name> eval "$(python scripts/open-conversation-by-user.py '<user_id>')"
browser-act --session <name> eval "$(python scripts/check-page-state.py)"
End-to-end Scenario A:
sent: true rate >= 90% for each pending-reply conversationEnd-to-end Scenario B:
composer_ready: true)sent: true rate >= 90%Atomic components: see success criteria in each atomic Skill (scripts in this directory fully reuse the atomic implementations).
browser-act input (CDP real keyboard); eval setting value does not workcan_dm_reason enum, observed values): Allowed — can send; InboxClosed — recipient closed DM; other values (possibly Blocked, NotFollowing, etc.) treat as cannot send"30m" / "6:25 PM" / "May 8"); no ISO datetimepeer_* fields take only the first non-self member; fine-grained replies in group conversations are not supported{target, status, timestamp, error?} per item; resume from breakpoint on interruption--session x-dm) for the whole batch; passcode unlock and login state persist within the session, no need to re-unlock for each itemPath: {working-directory}/browser-act-skill-forge-memories/x-dm-automation-x-dm-auto-chat.memory.md (working directory is determined by the Agent running the Skill)
Before execution: If the file exists, read it first — it records unexpected situations encountered during past executions (e.g., a strategy has become ineffective, a selector changed, a rate threshold discovered); adjust strategy order accordingly.
After execution: If an unexpected situation is encountered (strategy became ineffective, page redesigned, anti-scraping upgraded, better path discovered, new can_dm_reason enum values), append a line: {YYYY-MM-DD}: {what happened} → {conclusion}
Normal execution does not write to the file. Do not record what keywords were used, which conversations were replied to, or how many messages were sent — those are task outputs, not experience.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.