Releases: arvindrk/twitter-agent
v1.1.1
v1.1.1 — 2026-05-07
Bug Fixes
Outbound engagement: home feed replaces static search queries
Previously the outbound engagement pipeline seeded candidates from a fixed list of
STATIC_QUERIES via searchTweets. This produced shallow, repetitive candidate sets
with no personalisation. The pipeline now calls getHomeFeed(100) — the authenticated
user's reverse-chronological home timeline — which surfaces tweets from accounts the
user already follows and is more representative of the account's actual network.
Database: enum-to-text cast for Neon HTTP driver
getAlreadyActedPairs, getCooledDownAuthorIds, and getFollowedAuthorIds all compare
against the outbound_action PostgreSQL enum column. The Neon HTTP driver fails to infer
the correct type for parameterized enum values, causing every batch DB query to throw
"Failed query" and crash the run. Fixed by adding an explicit ::text cast on the
action column in all three queries.
Outbound agent: camelCase field mapping for XDK responses
SearchRecentData was reading author_id and public_metrics directly from API
responses, but the XDK client transforms all field names to camelCase before returning.
Updated field names to authorId and publicMetrics to match the actual response shape.
Breaking Changes / Removals
Reply action permanently disabled for outbound engagement
The X API v2 returns HTTP 403 Forbidden with the message
"Reply to this conversation is not allowed because you have not been mentioned or
otherwise engaged by the author" for any automated reply to a user who has not
previously interacted with the account. Outbound replies cannot succeed under this
constraint.
Removed entirely:
replyfield from the outbound engagement agent Zod schema and system prompt- 280-character retry block in the agent (was only needed for reply content)
isReplySafepost-processing step in the outbound agentreplyToTweetcall and reply execution block in the outbound service"reply"fromgetAlreadyActedPairsquery and the fully-acted candidate filterrepliescap fromCAPSrepliedcounter from the service return type and log linereplySettingsfromSearchedTweetinterface andgetHomeFeedfield requests
The reply action remains fully available for inbound engagement (webhook-driven
replies to mentions, which are explicitly allowed by the API).
Reliability
xAI "Service Unavailable" no longer crashes outbound runs
Transient xAI API outages ("Failed after 3 attempts. Last error: Service Unavailable")
previously propagated as unhandled exceptions, marking the entire cron run as failed
and preventing any subsequent attempt until the next schedule tick. The agent call is
now wrapped in a try-catch: on any error the run logs a warning and returns
{ liked: 0, retweeted: 0, followed: 0, skipped: 0 } instead of throwing. The next
scheduled run will retry automatically.
Tests
- Removed stale agent tests: "blocks reply containing AI-disclosure text" and
"retries and accepts short reply when initial reply exceeds 280 chars" — both tests
exercised logic that no longer exists. - Updated
CandidateTweetandOutboundDecisionlocal types in agent and service
test files to match the stripped schema. - Removed
replyToTweetfrom thexmodule mock andRunResulttype in service tests. - Caps test updated:
likes ≤ 10, retweets ≤ 3, follows ≤ 3(reply cap removed). - Cooldown test description updated to reflect that only
followis suppressed for
cooled-down authors (reply is gone).
v1.1.0
v1.1.0
New Features
Engagement System
- Added
runEngagementagent (src/agents/engagement.ts) — replies to incoming mentions using thread context and a configurable close/probe stance viagrok-4-1-fast-non-reasoning - Added
engagement_logtable withclaim/markEngagementDone/markEngagementFailedhelpers for idempotent, at-most-once reply processing - Added X webhook routes (
GET /webhook/xCRC challenge,POST /webhook/xevent dispatch) with full test coverage - Added
scripts/subscribe-webhook.ts— one-shot script to register the webhook URL and subscribe the authed user via X AAA
X Client Extensions
replyToTweet(text, replyToId)— posts a reply scoped to a conversation threadfetchThreadContext(tweetId)— fetches parent tweet chain up to 5 levels deep for conversation context
Bug Fixes
markEngagementFailednow swallows secondary DB errors to prevent double-throw on error paths- Removed
max(280)from Zod schema; tweet truncation now happens in code, preventing validation rejections on edge-case posts - Strengthened CRC challenge test to assert correct HMAC response shape
Test Coverage
- Added
researcher.test.ts— covers return value, message forwarding, empty text, and error propagation - Added
engagement.test.ts— covers reply generation, thread formatting, and stance behavior - Extended
writer.test.tsandscheduler.test.tswith empty-array and error-propagation edge cases app.test.ts— added mock call verification forclaimPost,markPublished,markFailed,publishTweet; refactored tomakeDbPostfactory withbeforeEachreset to eliminate state leaksx.test.ts— addedfetchThreadContextcoverage and fixed env teardown- Test runner now reports per-file line/function coverage via
--coverage
Chore
- Documented
X_USER_IDandX_BEARER_TOKENin.env.example(required for webhook subscription)
v1.0.1
v1.0.1
Bug Fixes
resetStalePosts()now called at the start of every scan cycle — posts stuck inprocessingwere never recoveredgetPostsDuelimit raised from 5 to 10 posts per scanDATABASE_URLguarded with explicitenv()check- Catch blocks narrowed to
err: unknownwithinstanceof Errorguards throughout - Unused
textfield dropped frompublishTweetreturn value - Scheduler system prompt: corrected account description
- Researcher: improved logging clarity for query availability
Testing & CI/CD
- 33 tests across 5 files — zero AI tokens used
- Covers: route auth, pipeline merge logic, tweet validation, agent output shape, HTTP edge cases
- CI workflow runs typecheck + tests on every push and PR; deploy gates on CI green
- Fixed CI test isolation: Bun 1.3.x shares
mock.modulestate across files; runner now spawns each file in its own process - CI badge added to README
Refactoring
- Extracted
rejectThreadPost()inapp.tsto remove duplication sanitizeContentexported from writer for testabilitylap()timing helper extracted,.jsimport extensions normalized- Scripts moved to
scripts/directory
Community
- Added
CODE_OF_CONDUCT.md,CONTRIBUTING.md,SECURITY.md
v1.0.0
Changelog
v1.0.0 — Initial Release
Core Pipeline
- Three-stage autonomous pipeline: Researcher → Writer → Scheduler
- Researcher uses
grok-4-latestvia Responses API withwebSearch+xSearchtools (multi-step, up to 10 steps) - Writer uses
grok-4-latestvia Chat Completions to produce 4–6 structured posts - Scheduler uses
grok-4-1-fastto assign ISO 8601 posting times
X (Twitter) Integration
- OAuth1 client via
@xdevplatform/xdk - Tweet poster with validation and error handling
POST /test/postendpoint for direct publishing
HTTP Server
- Hono server on port 3010
GET /cron/daily— triggers full pipeline async (202 response)POST /cron/execute-post— publishes a scheduled post by ID; supports scan mode for cron-job.org polling
Database
- Neon/Postgres with Drizzle ORM
- Schema for scheduled posts
getPostsDuequery withORDER BY scheduled_at, limited to 5 rows per tick
Infrastructure
- Dockerized with
Dockerfile+docker-compose - GitHub Actions workflow for automated deploy to AWS EC2 via ECR
- Nginx reverse proxy config
- External cron triggers via cron-job.org (daily pipeline + publish-due scan)
DX
dotenvxfor env injection;.env.exampleprovided- Timestamped console logging across pipeline and agents
npx tsc --noEmitcompile check- Test scripts for each agent and cron endpoint