Files
lettabot/src/core/interfaces.ts
Ani Tunturi 18010eb14f feat: Matrix adapter with E2EE, TTS/STT, reactions, and heartbeat routing
Full Matrix channel integration for LettaBot:

- E2EE via rust crypto (ephemeral mode, cross-signing bootstrap)
- Proactive SAS verification with Element clients
- TTS (VibeVoice) and STT (Faster-Whisper) voice pipeline
- Streaming message edits with 800ms throttle
- Collapsible reasoning blocks via <details> htmlPrefix
- Per-tool emoji reactions (brain, eyes, tool-specific, max 6)
- Heartbeat room conversation routing (heartbeatTargetChatId)
- Custom heartbeat prompt with first-person voice
- Per-room conversation isolation (per-chat mode)
- !pause, !resume, !status, !new, !timeout, !turns commands
- Audio/image/file upload handlers with E2EE media
- SDK 0.1.11 (approval recovery), CLI 0.18.2

Tested against Synapse homeserver with E2EE enabled for 2+ weeks,
handles key backup/restore and device verification.
2026-03-14 21:27:32 -04:00

93 lines
3.3 KiB
TypeScript

/**
* AgentSession interface - the contract for agent communication.
*
* Consumers (cron, heartbeat, polling, API server) depend on this interface,
* not the concrete LettaBot class. This enables multi-agent orchestration
* via LettaGateway without changing consumer code.
*/
import type { ChannelAdapter } from '../channels/types.js';
import type { InboundMessage, TriggerContext } from './types.js';
import type { GroupBatcher } from './group-batcher.js';
import type { StreamMsg } from './types.js';
export interface AgentSession {
/** Register a channel adapter */
registerChannel(adapter: ChannelAdapter): void;
/** Configure group message batching */
setGroupBatcher(batcher: GroupBatcher, intervals: Map<string, number>, instantGroupIds?: Set<string>, listeningGroupIds?: Set<string>): void;
/** Process a batched group message */
processGroupBatch(msg: InboundMessage, adapter: ChannelAdapter): void;
/** Start all registered channels */
start(): Promise<void>;
/** Stop all channels */
stop(): Promise<void>;
/** Send a message to the agent (used by cron, heartbeat, polling) */
sendToAgent(text: string, context?: TriggerContext): Promise<string>;
/** Stream a message to the agent, yielding chunks as they arrive */
streamToAgent(text: string, context?: TriggerContext): AsyncGenerator<StreamMsg>;
/** Deliver a message/file to a specific channel */
deliverToChannel(channelId: string, chatId: string, options: {
text?: string;
filePath?: string;
kind?: 'image' | 'file' | 'audio';
}): Promise<string | undefined>;
/** Get agent status */
getStatus(): { agentId: string | null; conversationId: string | null; channels: string[] };
/** Set agent ID (for container deploys) */
setAgentId(agentId: string): void;
/** Reset agent state */
reset(): void;
/** Get the last message target (for heartbeat delivery) */
getLastMessageTarget(): { channel: string; chatId: string } | null;
/** Get the time of the last user message (for heartbeat skip logic) */
getLastUserMessageTime(): Date | null;
/** Callback to trigger heartbeat */
onTriggerHeartbeat?: () => Promise<void>;
/** Invalidate a cached session, forcing fresh session on next message */
invalidateSession(key?: string): void;
/** Callback for session invalidation requests (e.g., from !new command) */
onInvalidateSession?: (key?: string) => void;
}
/**
* Minimal interface for message delivery.
* Satisfied by both AgentSession and LettaGateway.
*/
export interface MessageDeliverer {
deliverToChannel(channelId: string, chatId: string, options: {
text?: string;
filePath?: string;
kind?: 'image' | 'file' | 'audio';
}): Promise<string | undefined>;
}
/**
* Extended interface for the API server.
* Supports both outbound delivery (to channels) and inbound chat (to agents).
* Satisfied by LettaGateway.
*/
export interface AgentRouter extends MessageDeliverer {
/** Send a message to a named agent and return the response text */
sendToAgent(agentName: string | undefined, text: string, context?: TriggerContext): Promise<string>;
/** Stream a message to a named agent, yielding chunks as they arrive */
streamToAgent(agentName: string | undefined, text: string, context?: TriggerContext): AsyncGenerator<StreamMsg>;
/** Get all registered agent names */
getAgentNames(): string[];
}