From 40fc79645d3d235282bb0a11197fd4669c89235c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:51:57 +0000 Subject: [PATCH 1/3] Initial plan From 43c65c15d0691bbf0daef7bc361a06b056b52e88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:16:07 +0000 Subject: [PATCH 2/3] fix: address unresolved review feedback from PRs #26, #6, #4, #13 - xapi.ts: Use URLSearchParams for mentions URL with since_id support - xapi.ts: Add Array.isArray() guard before parseThread() in fetchThread - xapi.ts: Add Array.isArray() guard in searchTweets - xapi.ts: Change any to unknown in parseThread parameter type - agent.ts: Process mentions oldest-first and add safe iterator pruning - grok.ts: Replace any with typed response, fix as any cast - auto-label.yml: Add permissions: pull-requests: write - issue-triage.yml: Add permissions: issues: write - pr-checks.yml: Add permissions: pull-requests: write, fix syntax check - README.md: Fix double space and improve wording Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> --- .github/workflows/auto-label.yml | 2 ++ .github/workflows/issue-triage.yml | 2 ++ .github/workflows/pr-checks.yml | 4 +++- README.md | 2 +- src/services/agent.ts | 14 ++++++++++++-- src/services/grok.ts | 4 ++-- src/services/xapi.ts | 28 ++++++++++++++++++++++++---- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml index cab0666..4b3acba 100644 --- a/.github/workflows/auto-label.yml +++ b/.github/workflows/auto-label.yml @@ -2,6 +2,8 @@ name: Auto Label on: pull_request: types: [opened, reopened, synchronized] +permissions: + pull-requests: write jobs: label: runs-on: ubuntu-latest diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index 9379a8d..7ad9636 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -2,6 +2,8 @@ name: Issue Triage on: issues: types: [opened] +permissions: + issues: write jobs: triage: runs-on: ubuntu-latest diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 2464d70..7e2de71 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -2,6 +2,8 @@ name: PR Checks on: pull_request: branches: [main, master] +permissions: + pull-requests: write jobs: validate: runs-on: ubuntu-latest @@ -10,4 +12,4 @@ jobs: - name: Basic Syntax Check run: | echo "Running syntax validation..." - find . -name "*.js" -o -name "*.py" -o -name "*.ts" | xargs -I {} node -c {} || true + find . -name "*.js" -o -name "*.ts" | xargs -I {} node -c {} || true diff --git a/README.md b/README.md index 0dd0e21..e751f94 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MyXstack -This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. +This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts on thread context and reasoning, through Grok via the xMCP server. ## Phase 1: Gather prerequisites & accounts (1–2 hours) diff --git a/src/services/agent.ts b/src/services/agent.ts index ab40033..cfafc24 100644 --- a/src/services/agent.ts +++ b/src/services/agent.ts @@ -90,11 +90,21 @@ export class AutonomousAgent { console.log(`\nšŸ“¬ [${new Date().toLocaleTimeString()}] Found ${newMentions.length} new mention(s)!\n`); - // Process each mention - for (const mention of newMentions) { + // Process mentions oldest-first (X API returns newest first) + for (const mention of [...newMentions].reverse()) { await this.processMention(mention); this.processedMentions.add(mention.post.id); } + + // Prune oldest processed mentions to prevent unbounded growth + while (this.processedMentions.size > 1000) { + const iter = this.processedMentions.values(); + const { value, done } = iter.next(); + if (done) { + break; + } + this.processedMentions.delete(value); + } } catch (error) { console.error('āŒ Error in processing loop:', error); } finally { diff --git a/src/services/grok.ts b/src/services/grok.ts index 0fd6b2c..79f1746 100644 --- a/src/services/grok.ts +++ b/src/services/grok.ts @@ -58,7 +58,7 @@ export class GrokService { throw new Error(`Grok API error: ${response.status}`); } - const data: any = await response.json(); + const data = await response.json() as { choices: Array<{ message?: { content?: string } }> }; const analysisText = data.choices[0]?.message?.content || ''; // Use the root post ID from the thread, not the mention text @@ -115,7 +115,7 @@ export class GrokService { const parsed = JSON.parse(jsonMatch[0]); const action: AgentAction = { - type: parsed.action as any, + type: parsed.action as AgentAction['type'], target_post_id: mentionPostId, content: parsed.content, query: parsed.action === 'search' ? parsed.content : undefined, diff --git a/src/services/xapi.ts b/src/services/xapi.ts index f80be1e..1fa2f3c 100644 --- a/src/services/xapi.ts +++ b/src/services/xapi.ts @@ -44,8 +44,16 @@ export class XAPIClient { throw new Error('Failed to get user ID from response'); } + const params = new URLSearchParams({ + max_results: '10', + expansions: 'author_id', + 'tweet.fields': 'created_at,conversation_id,in_reply_to_user_id,referenced_tweets', + }); + if (this.lastMentionId) { + params.set('since_id', this.lastMentionId); + } const mentionsResponse = await this.makeXAPIRequest( - `https://api.twitter.com/2/users/${userId}/mentions?max_results=10&expansions=author_id&tweet.fields=created_at,conversation_id,in_reply_to_user_id,referenced_tweets`, + `https://api.twitter.com/2/users/${userId}/mentions?${params.toString()}`, 'GET' ); @@ -77,7 +85,16 @@ export class XAPIClient { 'GET' ); - return this.parseThread(response.data || []); + if (!response.data) { + return null; + } + + if (!Array.isArray(response.data)) { + console.warn('Unexpected response shape from X API (thread): data is not an array'); + return null; + } + + return this.parseThread(response.data); } catch (error) { console.error('Error fetching thread:', error); return null; @@ -127,7 +144,10 @@ export class XAPIClient { 'GET' ); - return (response.data || []).map((tweet: any) => this.parsePost(tweet)); + if (!Array.isArray(response.data)) { + return []; + } + return response.data.map((tweet: any) => this.parsePost(tweet)); } catch (error) { console.error('Error searching tweets:', error); return []; @@ -181,7 +201,7 @@ export class XAPIClient { }; } - private parseThread(tweets: { created_at: string; [key: string]: any }[]): XThread | null { + private parseThread(tweets: { created_at: string; [key: string]: unknown }[]): XThread | null { if (tweets.length === 0) return null; const sorted = tweets.sort((a, b) => From 9de3bafd25c695259fdfcff2dca4832a9b506ae3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:17:00 +0000 Subject: [PATCH 3/3] fix: move iterator creation outside while loop in agent.ts pruning Co-authored-by: groupthinking <154503486+groupthinking@users.noreply.github.com> --- src/services/agent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/agent.ts b/src/services/agent.ts index cfafc24..62e0fc0 100644 --- a/src/services/agent.ts +++ b/src/services/agent.ts @@ -97,8 +97,8 @@ export class AutonomousAgent { } // Prune oldest processed mentions to prevent unbounded growth + const iter = this.processedMentions.values(); while (this.processedMentions.size > 1000) { - const iter = this.processedMentions.values(); const { value, done } = iter.next(); if (done) { break;