From 4711ec435a6fa8ed29eb1b692b38d062f989daec Mon Sep 17 00:00:00 2001 From: Gavriel Date: Mon, 2 Feb 2026 00:08:40 +0200 Subject: [PATCH] Add register_group IPC command for dynamic group registration Main agent can now register new groups via MCP tool without restart. Host updates both in-memory state and JSON file, creates group folders. Authorization enforced at both agent and host level. Co-Authored-By: Claude Opus 4.5 --- container/agent-runner/src/ipc-mcp.ts | 39 +++++++++++++++++++++++++++ src/index.ts | 36 +++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/container/agent-runner/src/ipc-mcp.ts b/container/agent-runner/src/ipc-mcp.ts index d7a6a29..d6c1fc8 100644 --- a/container/agent-runner/src/ipc-mcp.ts +++ b/container/agent-runner/src/ipc-mcp.ts @@ -276,6 +276,45 @@ SCHEDULE VALUE FORMAT (all times are LOCAL timezone): }] }; } + ), + + tool( + 'register_group', + `Register a new WhatsApp group so the agent can respond to messages there. Main group only. + +Use available_groups.json to find the JID for a group. The folder name should be lowercase with hyphens (e.g., "family-chat").`, + { + jid: z.string().describe('The WhatsApp JID (e.g., "120363336345536173@g.us")'), + name: z.string().describe('Display name for the group'), + folder: z.string().describe('Folder name for group files (lowercase, hyphens, e.g., "family-chat")'), + trigger: z.string().describe('Trigger word (e.g., "@Andy")') + }, + async (args) => { + if (!isMain) { + return { + content: [{ type: 'text', text: 'Only the main group can register new groups.' }], + isError: true + }; + } + + const data = { + type: 'register_group', + jid: args.jid, + name: args.name, + folder: args.folder, + trigger: args.trigger, + timestamp: new Date().toISOString() + }; + + writeIpcFile(TASKS_DIR, data); + + return { + content: [{ + type: 'text', + text: `Group "${args.name}" registered. It will start receiving messages immediately.` + }] + }; + } ) ] }); diff --git a/src/index.ts b/src/index.ts index ffb01cb..cf58c08 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,17 @@ function saveState(): void { saveJson(path.join(DATA_DIR, 'sessions.json'), sessions); } +function registerGroup(jid: string, group: RegisteredGroup): void { + registeredGroups[jid] = group; + saveJson(path.join(DATA_DIR, 'registered_groups.json'), registeredGroups); + + // Create group folder + const groupDir = path.join(DATA_DIR, '..', 'groups', group.folder); + fs.mkdirSync(path.join(groupDir, 'logs'), { recursive: true }); + + logger.info({ jid, name: group.name, folder: group.folder }, 'Group registered'); +} + /** * Sync group metadata from WhatsApp. * Fetches all participating groups and stores their names in the database. @@ -306,6 +317,12 @@ async function processTaskIpc( context_mode?: string; groupFolder?: string; chatJid?: string; + // For register_group + jid?: string; + name?: string; + folder?: string; + trigger?: string; + containerConfig?: RegisteredGroup['containerConfig']; }, sourceGroup: string, // Verified identity from IPC directory isMain: boolean // Verified from directory path @@ -431,6 +448,25 @@ async function processTaskIpc( } break; + case 'register_group': + // Only main group can register new groups + if (!isMain) { + logger.warn({ sourceGroup }, 'Unauthorized register_group attempt blocked'); + break; + } + if (data.jid && data.name && data.folder && data.trigger) { + registerGroup(data.jid, { + name: data.name, + folder: data.folder, + trigger: data.trigger, + added_at: new Date().toISOString(), + containerConfig: data.containerConfig + }); + } else { + logger.warn({ data }, 'Invalid register_group request - missing required fields'); + } + break; + default: logger.warn({ type: data.type }, 'Unknown IPC task type'); }