feat: agent-to-agent communication, dynamic agent creation, self-modification tools

Agent-to-agent: host routes messages with channel_type='agent' to target
agent's inbound.db, enriches with sender info, wakes target container.
Bidirectional routing works via inherited routing context.

Dynamic agents: create_agent MCP tool + system action handler creates
agent groups, folders, and optional CLAUDE.md on the fly.

Self-modification: install_packages (apt/npm, requires admin approval),
add_mcp_server (no approval), request_rebuild (builds per-agent-group
Docker image with approved packages). Approval flow reuses interactive
card infrastructure with pending_approvals table.

Also includes fixes from prior session: attachment download, reply context
extraction, message editing (platform message ID tracking), delivery retry
limits, and card update on button click.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-10 01:10:34 +03:00
parent 9af9bc947a
commit d8fbd3b239
24 changed files with 1025 additions and 78 deletions

View File

@@ -76,20 +76,36 @@ async function main(): Promise<void> {
CLAUDE_CODE_AUTO_COMPACT_WINDOW: '165000',
};
// Build MCP servers config: nanoclaw built-in + any additional from host
const mcpServers: Record<string, { command: string; args: string[]; env: Record<string, string> }> = {
nanoclaw: {
command: 'node',
args: [mcpServerPath],
env: {
SESSION_INBOUND_DB_PATH: process.env.SESSION_INBOUND_DB_PATH || '/workspace/inbound.db',
SESSION_OUTBOUND_DB_PATH: process.env.SESSION_OUTBOUND_DB_PATH || '/workspace/outbound.db',
SESSION_HEARTBEAT_PATH: process.env.SESSION_HEARTBEAT_PATH || '/workspace/.heartbeat',
},
},
};
// Merge additional MCP servers from host configuration
if (process.env.NANOCLAW_MCP_SERVERS) {
try {
const additional = JSON.parse(process.env.NANOCLAW_MCP_SERVERS) as Record<string, { command: string; args: string[]; env: Record<string, string> }>;
for (const [name, config] of Object.entries(additional)) {
mcpServers[name] = config;
log(`Additional MCP server: ${name} (${config.command})`);
}
} catch (e) {
log(`Failed to parse NANOCLAW_MCP_SERVERS: ${e}`);
}
}
await runPollLoop({
provider,
cwd: CWD,
mcpServers: {
nanoclaw: {
command: 'node',
args: [mcpServerPath],
env: {
SESSION_INBOUND_DB_PATH: process.env.SESSION_INBOUND_DB_PATH || '/workspace/inbound.db',
SESSION_OUTBOUND_DB_PATH: process.env.SESSION_OUTBOUND_DB_PATH || '/workspace/outbound.db',
SESSION_HEARTBEAT_PATH: process.env.SESSION_HEARTBEAT_PATH || '/workspace/.heartbeat',
},
},
},
mcpServers,
systemPrompt,
env,
additionalDirectories: additionalDirectories.length > 0 ? additionalDirectories : undefined,