Files
ai/gateway/src/harness/memory/session-context.ts

227 lines
5.3 KiB
TypeScript

import type { License, ChannelType } from '../../types/user.js';
import type { BaseMessage } from '@langchain/core/messages';
/**
* Channel capabilities (what the channel supports)
*/
export interface ChannelCapabilities {
supportsMarkdown: boolean;
supportsImages: boolean;
supportsButtons: boolean;
supportsVoice: boolean;
supportsFiles: boolean;
maxMessageLength: number;
}
/**
* Active channel information for multi-channel routing
*/
export interface ActiveChannel {
type: ChannelType;
channelUserId: string; // Platform-specific ID (telegram_id, discord_id, etc)
capabilities: ChannelCapabilities;
metadata?: Record<string, unknown>;
}
/**
* Workspace state (current user context)
*/
export interface WorkspaceContext {
activeIndicators: string[];
activeStrategies: string[];
watchlist: string[];
recentQueries: string[];
preferences: Record<string, unknown>;
}
/**
* Memory chunk from RAG retrieval
*/
export interface MemoryChunk {
id: string;
content: string;
role: 'user' | 'assistant' | 'system';
timestamp: number;
relevanceScore: number;
metadata?: Record<string, unknown>;
}
/**
* Enhanced user context for agent harness
*
* Contains all necessary context for an agent session:
* - User identity and license
* - Active channel info (for multi-channel support)
* - Conversation state and history
* - RAG-retrieved relevant memories
* - Workspace state
*
* This object is passed to all agent nodes and tools.
*/
export interface UserContext {
// Identity
userId: string;
sessionId: string;
license: License;
// Channel context (for multi-channel routing)
activeChannel: ActiveChannel;
// Conversation state
conversationHistory: BaseMessage[];
currentMessage?: string;
// RAG context
relevantMemories: MemoryChunk[];
// Workspace state
workspaceState: WorkspaceContext;
// Metadata
createdAt: Date;
lastActivity: Date;
}
/**
* Get default channel capabilities based on type
*/
export function getDefaultCapabilities(channelType: ChannelType): ChannelCapabilities {
switch (channelType) {
case 'websocket':
return {
supportsMarkdown: true,
supportsImages: true,
supportsButtons: true,
supportsVoice: false,
supportsFiles: true,
maxMessageLength: 100000,
};
case 'telegram':
return {
supportsMarkdown: true,
supportsImages: true,
supportsButtons: true,
supportsVoice: true,
supportsFiles: true,
maxMessageLength: 4096,
};
case 'slack':
return {
supportsMarkdown: true,
supportsImages: true,
supportsButtons: true,
supportsVoice: false,
supportsFiles: true,
maxMessageLength: 40000,
};
case 'discord':
return {
supportsMarkdown: true,
supportsImages: true,
supportsButtons: true,
supportsVoice: true,
supportsFiles: true,
maxMessageLength: 2000,
};
default:
// Default fallback
return {
supportsMarkdown: false,
supportsImages: false,
supportsButtons: false,
supportsVoice: false,
supportsFiles: false,
maxMessageLength: 1000,
};
}
}
/**
* Create a new user context
*/
export function createUserContext(params: {
userId: string;
sessionId: string;
license: License;
channelType: ChannelType;
channelUserId: string;
channelCapabilities?: Partial<ChannelCapabilities>;
}): UserContext {
const defaultCapabilities = getDefaultCapabilities(params.channelType);
const capabilities: ChannelCapabilities = {
...defaultCapabilities,
...params.channelCapabilities,
};
return {
userId: params.userId,
sessionId: params.sessionId,
license: params.license,
activeChannel: {
type: params.channelType,
channelUserId: params.channelUserId,
capabilities,
},
conversationHistory: [],
relevantMemories: [],
workspaceState: {
activeIndicators: [],
activeStrategies: [],
watchlist: [],
recentQueries: [],
preferences: {},
},
createdAt: new Date(),
lastActivity: new Date(),
};
}
/**
* Update last activity timestamp
*/
export function touchContext(context: UserContext): UserContext {
return {
...context,
lastActivity: new Date(),
};
}
/**
* Check if context has expired (for TTL management)
*/
export function isContextExpired(context: UserContext, ttlSeconds: number): boolean {
const now = Date.now();
const lastActivity = context.lastActivity.getTime();
return (now - lastActivity) / 1000 > ttlSeconds;
}
/**
* Serialize context for Redis storage
*/
export function serializeContext(context: UserContext): string {
return JSON.stringify({
...context,
createdAt: context.createdAt.toISOString(),
lastActivity: context.lastActivity.toISOString(),
// Don't serialize conversation history (too large, use checkpoint instead)
conversationHistory: undefined,
});
}
/**
* Deserialize context from Redis storage
*/
export function deserializeContext(data: string): Partial<UserContext> {
const parsed = JSON.parse(data);
return {
...parsed,
createdAt: new Date(parsed.createdAt),
lastActivity: new Date(parsed.lastActivity),
conversationHistory: [], // Will be loaded from checkpoint
};
}