Skip to content

Commit d3081b4

Browse files
nearestnaborsclaude
andcommitted
Split x_conversations to return brief message to agent
- x_conversations now returns only "X conversations awaiting reply" - x_get_conversations (app-only) returns full JSON data - UI fetches data via x_get_conversations, agent sees minimal output - This prevents agent from summarizing conversation data Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6cf07d0 commit d3081b4

3 files changed

Lines changed: 106 additions & 52 deletions

File tree

src/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { fileURLToPath } from 'url';
2525
import { xAuthStatus } from './tools/auth-status.js';
2626
import { xDraftTweet } from './tools/draft-tweet.js';
2727
import { xPostTweet } from './tools/post-tweet.js';
28-
import { xConversations } from './tools/conversations.js';
28+
import { xConversations, xGetConversations } from './tools/conversations.js';
2929
import { xDismissConversation } from './tools/dismiss-conversation.js';
3030

3131
const __filename = fileURLToPath(import.meta.url);
@@ -165,7 +165,7 @@ const toolHandlers: Record<string, ToolHandler> = {
165165
x_draft_tweet: xDraftTweet,
166166
x_post_tweet: xPostTweet,
167167
x_conversations: xConversations,
168-
x_get_conversations: xConversations, // Same handler, UI calls this to get data
168+
x_get_conversations: xGetConversations, // UI-only, returns full data
169169
x_dismiss_conversation: xDismissConversation,
170170
};
171171

src/tools/conversations.ts

Lines changed: 98 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ interface ConversationItem {
8282
* Tool: x_conversations
8383
* Fetches unreplied mentions and returns data for the conversation list UI
8484
*/
85-
export async function xConversations(): Promise<unknown> {
85+
type FetchResult =
86+
| { success: true; data: { conversations: ConversationItem[]; username: string } }
87+
| { success: false; content: unknown };
88+
89+
// Internal function that fetches and returns conversation data
90+
async function fetchConversations(): Promise<FetchResult> {
8691
// Clean up expired dismissals
8792
const prunedDismissals = pruneExpiredDismissals();
8893
if (prunedDismissals > 0) {
@@ -94,12 +99,15 @@ export async function xConversations(): Promise<unknown> {
9499
// Check if username is set
95100
if (!username) {
96101
return {
97-
content: [
98-
{
99-
type: 'text',
100-
text: `I need your X username to find your conversations.\n\nPlease check your auth status with x_auth_status first - your username should be detected automatically after authentication.`,
101-
},
102-
],
102+
success: false,
103+
content: {
104+
content: [
105+
{
106+
type: 'text',
107+
text: `I need your X username to find your conversations.\n\nPlease check your auth status with x_auth_status first - your username should be detected automatically after authentication.`,
108+
},
109+
],
110+
},
103111
};
104112
}
105113

@@ -127,19 +135,12 @@ export async function xConversations(): Promise<unknown> {
127135
});
128136

129137
if (mentions.length === 0) {
130-
// Return empty conversation data for UI
131-
const emptyData = {
132-
conversations: [],
133-
username,
134-
};
135-
136138
return {
137-
content: [
138-
{
139-
type: 'text',
140-
text: JSON.stringify(emptyData),
141-
},
142-
],
139+
success: true,
140+
data: {
141+
conversations: [],
142+
username,
143+
},
143144
};
144145
}
145146

@@ -269,33 +270,29 @@ export async function xConversations(): Promise<unknown> {
269270
// Update last checked timestamp
270271
updateLastChecked();
271272

272-
// Return JSON data for the conversation-list UI app
273-
const conversationsData = {
274-
conversations,
275-
username,
276-
};
277-
278273
return {
279-
content: [
280-
{
281-
type: 'text',
282-
text: JSON.stringify(conversationsData),
283-
},
284-
],
274+
success: true,
275+
data: {
276+
conversations,
277+
username,
278+
},
285279
};
286280
} catch (error) {
287281
console.error('[ASSA] Error fetching conversations:', error);
288282

289283
// Check for auth errors using AuthRequiredError
290284
if (error instanceof AuthRequiredError) {
291285
return {
292-
content: [
293-
{
294-
type: 'text',
295-
text: `X authorization required. Please use the x_auth_status tool to connect your account.`,
296-
},
297-
],
298-
isError: true,
286+
success: false,
287+
content: {
288+
content: [
289+
{
290+
type: 'text',
291+
text: `X authorization required. Please use the x_auth_status tool to connect your account.`,
292+
},
293+
],
294+
isError: true,
295+
},
299296
};
300297
}
301298

@@ -307,24 +304,75 @@ export async function xConversations(): Promise<unknown> {
307304
errorMessage.toLowerCase().includes('unauthorized')
308305
) {
309306
return {
307+
success: false,
308+
content: {
309+
content: [
310+
{
311+
type: 'text',
312+
text: `X authorization required. Please use the x_auth_status tool to connect your account.`,
313+
},
314+
],
315+
isError: true,
316+
},
317+
};
318+
}
319+
320+
return {
321+
success: false,
322+
content: {
310323
content: [
311324
{
312325
type: 'text',
313-
text: `X authorization required. Please use the x_auth_status tool to connect your account.`,
326+
text: `Error fetching conversations: ${errorMessage}`,
314327
},
315328
],
316329
isError: true,
317-
};
318-
}
319-
320-
return {
321-
content: [
322-
{
323-
type: 'text',
324-
text: `Error fetching conversations: ${errorMessage}`,
325-
},
326-
],
327-
isError: true,
330+
},
328331
};
329332
}
330333
}
334+
335+
/**
336+
* Tool: x_conversations (for agent)
337+
* Returns a brief message - UI fetches full data via x_get_conversations
338+
*/
339+
export async function xConversations(): Promise<unknown> {
340+
const result = await fetchConversations();
341+
342+
if (!result.success) {
343+
return result.content;
344+
}
345+
346+
const count = result.data.conversations.length;
347+
return {
348+
content: [
349+
{
350+
type: 'text',
351+
text: count === 0
352+
? 'No conversations awaiting your reply.'
353+
: `${count} conversation${count === 1 ? '' : 's'} awaiting your reply.`,
354+
},
355+
],
356+
};
357+
}
358+
359+
/**
360+
* Tool: x_get_conversations (for UI only, hidden from model)
361+
* Returns full conversation data as JSON
362+
*/
363+
export async function xGetConversations(): Promise<unknown> {
364+
const result = await fetchConversations();
365+
366+
if (!result.success) {
367+
return result.content;
368+
}
369+
370+
return {
371+
content: [
372+
{
373+
type: 'text',
374+
text: JSON.stringify(result.data),
375+
},
376+
],
377+
};
378+
}

ui-apps/conversation-list.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
padding: 0;
1212
}
1313

14+
html, body {
15+
height: fit-content;
16+
min-height: 0;
17+
}
18+
1419
body {
1520
font-family:
1621
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
@@ -20,6 +25,7 @@
2025

2126
.container {
2227
max-width: 600px;
28+
height: fit-content;
2329
}
2430

2531

0 commit comments

Comments
 (0)