Files
nanoclaw/src/types.ts
exe.dev user ee599b9f0c feat: add reply/quoted message context support
Add generic reply context fields to NewMessage (reply_to_message_id,
reply_to_message_content, reply_to_sender_name) so any channel can
pass quoted message context to the agent.

- Add thread_id and reply_to_* fields to NewMessage interface
- Add DB migration for reply context columns on messages table
- Update storeMessage/getMessagesSince/getNewMessages to persist and
  retrieve reply fields
- Render reply context as <quoted_message> XML in formatMessages
- Add DB and formatting tests

Co-Authored-By: Alfred-the-buttler <leon.alfred.bot@gmail.com>
Co-Authored-By: moktamd <moktamd@users.noreply.github.com>
Co-Authored-By: gurixs-carson <gurixs-carson@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:05:24 +00:00

113 lines
3.3 KiB
TypeScript

export interface AdditionalMount {
hostPath: string; // Absolute path on host (supports ~ for home)
containerPath?: string; // Optional — defaults to basename of hostPath. Mounted at /workspace/extra/{value}
readonly?: boolean; // Default: true for safety
}
/**
* Mount Allowlist - Security configuration for additional mounts
* This file should be stored at ~/.config/nanoclaw/mount-allowlist.json
* and is NOT mounted into any container, making it tamper-proof from agents.
*/
export interface MountAllowlist {
// Directories that can be mounted into containers
allowedRoots: AllowedRoot[];
// Glob patterns for paths that should never be mounted (e.g., ".ssh", ".gnupg")
blockedPatterns: string[];
// If true, non-main groups can only mount read-only regardless of config
nonMainReadOnly: boolean;
}
export interface AllowedRoot {
// Absolute path or ~ for home (e.g., "~/projects", "/var/repos")
path: string;
// Whether read-write mounts are allowed under this root
allowReadWrite: boolean;
// Optional description for documentation
description?: string;
}
export interface ContainerConfig {
additionalMounts?: AdditionalMount[];
timeout?: number; // Default: 300000 (5 minutes)
}
export interface RegisteredGroup {
name: string;
folder: string;
trigger: string;
added_at: string;
containerConfig?: ContainerConfig;
requiresTrigger?: boolean; // Default: true for groups, false for solo chats
isMain?: boolean; // True for the main control group (no trigger, elevated privileges)
}
export interface NewMessage {
id: string;
chat_jid: string;
sender: string;
sender_name: string;
content: string;
timestamp: string;
is_from_me?: boolean;
is_bot_message?: boolean;
thread_id?: string;
reply_to_message_id?: string;
reply_to_message_content?: string;
reply_to_sender_name?: string;
}
export interface ScheduledTask {
id: string;
group_folder: string;
chat_jid: string;
prompt: string;
script?: string | null;
schedule_type: 'cron' | 'interval' | 'once';
schedule_value: string;
context_mode: 'group' | 'isolated';
next_run: string | null;
last_run: string | null;
last_result: string | null;
status: 'active' | 'paused' | 'completed';
created_at: string;
}
export interface TaskRunLog {
task_id: string;
run_at: string;
duration_ms: number;
status: 'success' | 'error';
result: string | null;
error: string | null;
}
// --- Channel abstraction ---
export interface Channel {
name: string;
connect(): Promise<void>;
sendMessage(jid: string, text: string): Promise<void>;
isConnected(): boolean;
ownsJid(jid: string): boolean;
disconnect(): Promise<void>;
// Optional: typing indicator. Channels that support it implement it.
setTyping?(jid: string, isTyping: boolean): Promise<void>;
// Optional: sync group/chat names from the platform.
syncGroups?(force: boolean): Promise<void>;
}
// Callback type that channels use to deliver inbound messages
export type OnInboundMessage = (chatJid: string, message: NewMessage) => void;
// Callback for chat metadata discovery.
// name is optional — channels that deliver names inline (Telegram) pass it here;
// channels that sync names separately (via syncGroups) omit it.
export type OnChatMetadata = (
chatJid: string,
timestamp: string,
name?: string,
channel?: string,
isGroup?: boolean,
) => void;