v2 phase 4: channel adapter interface, registry, and host wiring

ChannelAdapter interface with setup/deliver/teardown/setTyping lifecycle.
Self-registration pattern via channel-registry. Host wiring in index-v2
bridges inbound messages to routeInbound and outbound delivery to adapters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-09 00:10:46 +03:00
parent d35386a46e
commit 7201fe5032
6 changed files with 483 additions and 9 deletions

79
src/channels/adapter.ts Normal file
View File

@@ -0,0 +1,79 @@
/**
* v2 Channel Adapter interface.
*
* Channel adapters bridge NanoClaw with messaging platforms (Discord, Slack, etc.).
* Two patterns: native adapters (implement directly) or Chat SDK bridge (wrap a Chat SDK adapter).
*/
/** Configuration for a registered conversation (messaging group + agent wiring). */
export interface ConversationConfig {
platformId: string;
agentGroupId: string;
triggerPattern?: string; // regex string (for native channels)
requiresTrigger: boolean;
sessionMode: 'shared' | 'per-thread';
}
/** Passed to the adapter at setup time. */
export interface ChannelSetup {
/** Known conversations from central DB. */
conversations: ConversationConfig[];
/** Called when an inbound message arrives from the platform. */
onInbound(platformId: string, threadId: string | null, message: InboundMessage): void;
/** Called when the adapter discovers metadata about a conversation. */
onMetadata(platformId: string, name?: string, isGroup?: boolean): void;
}
/** Inbound message from adapter to host. */
export interface InboundMessage {
id: string;
kind: 'chat' | 'chat-sdk';
content: unknown; // JS object — host will JSON.stringify before writing to session DB
timestamp: string;
}
/** Outbound message from host to adapter. */
export interface OutboundMessage {
kind: string;
content: unknown; // parsed JSON from messages_out
}
/** Discovered conversation info (from syncConversations). */
export interface ConversationInfo {
platformId: string;
name: string;
isGroup: boolean;
}
/** The v2 channel adapter contract. */
export interface ChannelAdapter {
name: string;
channelType: string;
// Lifecycle
setup(config: ChannelSetup): Promise<void>;
teardown(): Promise<void>;
isConnected(): boolean;
// Outbound delivery
deliver(platformId: string, threadId: string | null, message: OutboundMessage): Promise<void>;
// Optional
setTyping?(platformId: string, threadId: string | null): Promise<void>;
syncConversations?(): Promise<ConversationInfo[]>;
updateConversations?(conversations: ConversationConfig[]): void;
}
/** Factory function that creates a channel adapter (returns null if credentials missing). */
export type ChannelAdapterFactory = () => ChannelAdapter | null;
/** Registration entry for a channel adapter. */
export interface ChannelRegistration {
factory: ChannelAdapterFactory;
containerConfig?: {
mounts?: Array<{ hostPath: string; containerPath: string; readonly: boolean }>;
env?: Record<string, string>;
};
}