Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/agents/codemie-code/tools/assistant-invocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { CodemieAssistant } from '@/env/types.js';
interface HistoryMessage {
role: 'User' | 'Assistant';
message?: string;
message_raw?: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import type { SessionProcessor, ProcessingContext, ProcessingResult } from '../../../../core/session/BaseProcessor.js';
import type { ParsedSession } from '../../../../core/session/BaseSessionAdapter.js';
import { CONVERSATION_SYNC_STATUS } from '../../../../../providers/plugins/sso/session/processors/conversations/types.js';
import { logger } from '../../../../../utils/logger.js';
import { getSessionConversationPath } from '../../../../core/session/session-config.js';

Expand Down Expand Up @@ -124,7 +125,7 @@ export class ConversationsProcessor implements SessionProcessor {
conversationId: context.agentSessionId,
history: result.history
},
status: 'pending' as const
status: CONVERSATION_SYNC_STATUS.PENDING
};

await appendFile(conversationsPath, JSON.stringify(payloadRecord) + '\n');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import type { SessionProcessor, ProcessingContext, ProcessingResult } from '../../../../core/session/BaseProcessor.js';
import type { ParsedSession } from '../../../../core/session/BaseSessionAdapter.js';
import type { ConversationPayloadRecord } from '../../../../../providers/plugins/sso/session/processors/conversations/conversation-types.js';
import type { ConversationPayloadRecord } from '../../../../../providers/plugins/sso/session/processors/conversations/types.js';
import { CONVERSATION_SYNC_STATUS } from '../../../../../providers/plugins/sso/session/processors/conversations/types.js';
import { logger } from '../../../../../utils/logger.js';
import { getSessionConversationPath } from '../../../../core/session/session-config.js';
import { SessionStore } from '../../../../core/session/SessionStore.js';
Expand Down Expand Up @@ -175,7 +176,7 @@ export class GeminiConversationsProcessor implements SessionProcessor {
conversationId: context.agentSessionId!, // From processing context
history: [userRecord, assistantRecord]
},
status: 'pending'
status: CONVERSATION_SYNC_STATUS.PENDING
};

payloads.push(payload);
Expand Down
6 changes: 3 additions & 3 deletions src/cli/commands/assistants/__tests__/chat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import { describe, it, expect, beforeEach } from 'vitest';
import { createAssistantsChatCommand } from '../chat.js';
import { createAssistantsChatCommand } from '../chat/index.js';
import { MESSAGES } from '../constants.js';

describe('Assistants Chat Command', () => {
Expand Down Expand Up @@ -74,9 +74,9 @@ describe('Assistants Chat Command', () => {
});

describe('Command Options', () => {
it('should have verbose and conversation-id options', () => {
it('should have verbose, conversation-id, and load-history options', () => {
const command = createAssistantsChatCommand();
expect(command.options).toHaveLength(2);
expect(command.options).toHaveLength(3);
});

it('should accept --verbose flag', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/assistants/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Assistants Command (Parent)', () => {
});

it('should have correct description', () => {
expect(command.description()).toBe('Manage CodeMie assistants');
expect(command.description()).toBe('Chat with CodeMie assistant');
});

it('should be configured as a Commander command', () => {
Expand Down Expand Up @@ -89,7 +89,7 @@ describe('Assistants Command (Parent)', () => {
// Assistants command is now focused on chat operations
// Setup has been moved to `codemie setup assistants`
expect(command.name()).toBe('assistants');
expect(command.description()).toContain('assistants');
expect(command.description()).toContain('assistant');
});
});

Expand Down
97 changes: 97 additions & 0 deletions src/cli/commands/assistants/chat/historyLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/**
* Conversation History Loader
*
* Loads conversation history from session files stored in ~/.codemie/sessions
*/

import { existsSync } from 'fs';

/**
* Maximum number of history messages to load from previous sessions
* This prevents sending excessively large context to the API
*/
const MAX_HISTORY_MESSAGES = 20;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to move it to config

import { logger } from '@/utils/logger.js';
import { getSessionConversationPath } from '@/agents/core/session/session-config.js';
import { readJSONL } from '@/providers/plugins/sso/session/utils/jsonl-reader.js';
import {
type ConversationPayloadRecord,
CONVERSATION_SYNC_STATUS
} from '@/providers/plugins/sso/session/processors/conversations/types.js';
import type { HistoryMessage } from '../constants.js';

/**
* Load conversation history from session files
*
* @param conversationId - Optional conversation ID to load history for
* @returns Array of history messages, or empty array if none found or on error
*
* @example
* ```ts
* const history = await loadConversationHistory('abc-123');
* console.log(`Loaded ${history.length} messages`);
* ```
*/
export async function loadConversationHistory(
conversationId: string | undefined
): Promise<HistoryMessage[]> {
if (!conversationId) return [];

try {
const filePath = getSessionConversationPath(conversationId);

// File doesn't exist yet - normal for first-time conversations
if (!existsSync(filePath)) {
logger.debug('Conversation history file not found (first-time conversation)', {
conversationId,
filePath
});
return [];
}

const records = await readJSONL<ConversationPayloadRecord>(filePath);
const validRecords = records.filter(
record => record.status === CONVERSATION_SYNC_STATUS.SUCCESS ||
record.status === CONVERSATION_SYNC_STATUS.PENDING
);

if (validRecords.length === 0) {
logger.debug('No valid conversation records found', {
conversationId,
totalRecords: records.length
});
return [];
}

const allMessages = validRecords
.flatMap(record => record.payload?.history ?? [])
.reduce((map, msg) => {
const key = `${msg.role}:${msg.message}:${msg.history_index ?? 0}`;
if (!map.has(key)) {
map.set(key, {
role: msg.role,
message: msg.message,
message_raw: msg.message
});
}
return map;
}, new Map<string, HistoryMessage>());

if (allMessages.size === 0) {
logger.debug('No history messages found in conversation records', {
conversationId
});
return [];
}

const allHistory: HistoryMessage[] = Array.from(allMessages.values());

return allHistory.slice(-MAX_HISTORY_MESSAGES);
} catch (error) {
logger.error('Failed to load conversation history', {
conversationId,
error: error instanceof Error ? error.message : String(error)
});
return [];
}
}
Loading