agentix-cli 0.5.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/chunk-NGKOM4ZZ.js +137 -0
- package/dist/chunk-NGKOM4ZZ.js.map +1 -0
- package/dist/cli.js +43 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +73 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-WT2UJMBC.js +0 -68
- package/dist/chunk-WT2UJMBC.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/a2a/client.ts","../src/a2a/mesh.ts","../src/daemon/config.ts","../src/agents/runtime.ts","../src/wiki/store.ts","../src/agents/sessions.ts","../src/daemon/rate-limit.ts","../src/daemon/token-tracker.ts","../src/agents/context.ts","../src/agents/registry.ts","../src/channels/group-log.ts","../src/channels/router.ts","../src/channels/telegram-format.ts","../src/channels/telegram.ts","../src/channels/whatsapp.ts","../src/crons/scheduler.ts","../src/daemon/index.ts","../src/channels/discord.ts","../src/channels/gitlab.ts","../src/daemon/logger.ts","../src/daemon/webhooks.ts","../src/utils/get-package-info.ts"],"sourcesContent":["import type { AgentCard, Task, MessagePart, JsonRpcResponse } from \"./types\"\n\n// --- A2A Client: call remote agents via A2A protocol ---\n\nexport class A2AClient {\n private baseUrl: string\n private token?: string\n\n constructor(baseUrl: string, token?: string) {\n this.baseUrl = baseUrl.replace(/\\/$/, \"\")\n this.token = token\n }\n\n /**\n * Fetch the remote agent's card.\n */\n async getAgentCard(): Promise<AgentCard> {\n const res = await fetch(`${this.baseUrl}/.well-known/agent-card.json`, {\n headers: this.headers(),\n })\n if (!res.ok) throw new Error(`Failed to fetch agent card: ${res.status}`)\n return res.json() as Promise<AgentCard>\n }\n\n /**\n * Send a task synchronously and wait for completion.\n */\n async sendTask(\n text: string,\n metadata?: Record<string, unknown>,\n ): Promise<Task> {\n const response = await this.rpc(\"tasks/send\", {\n id: `task-${Date.now().toString(36)}`,\n message: {\n role: \"user\",\n parts: [{ type: \"text\", text }] as MessagePart[],\n },\n metadata,\n })\n\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n\n return response.result as Task\n }\n\n /**\n * Send a task with SSE streaming.\n */\n async *sendTaskStream(\n text: string,\n metadata?: Record<string, unknown>,\n ): AsyncIterable<{ state: string; message?: string; final: boolean }> {\n const body = JSON.stringify({\n jsonrpc: \"2.0\",\n id: 1,\n method: \"tasks/sendSubscribe\",\n params: {\n id: `task-${Date.now().toString(36)}`,\n message: {\n role: \"user\",\n parts: [{ type: \"text\", text }],\n },\n metadata,\n },\n })\n\n const res = await fetch(this.baseUrl, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n \"Accept\": \"text/event-stream\",\n },\n body,\n })\n\n if (!res.ok || !res.body) {\n throw new Error(`A2A stream error: ${res.status}`)\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split(\"\\n\")\n buffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (line.startsWith(\"data: \")) {\n try {\n const data = JSON.parse(line.slice(6))\n yield {\n state: data.state,\n message: data.message?.parts?.[0]?.text,\n final: data.final,\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n /**\n * Get task status by ID.\n */\n async getTask(taskId: string): Promise<Task> {\n const response = await this.rpc(\"tasks/get\", { id: taskId })\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n return response.result as Task\n }\n\n /**\n * Cancel a running task.\n */\n async cancelTask(taskId: string): Promise<Task> {\n const response = await this.rpc(\"tasks/cancel\", { id: taskId })\n if (response.error) {\n throw new Error(`A2A error: ${response.error.message}`)\n }\n return response.result as Task\n }\n\n private async rpc(method: string, params: Record<string, unknown>): Promise<JsonRpcResponse> {\n const res = await fetch(this.baseUrl, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n id: Date.now(),\n method,\n params,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`A2A HTTP error: ${res.status}`)\n }\n\n return res.json() as Promise<JsonRpcResponse>\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = {}\n if (this.token) {\n h[\"Authorization\"] = `Bearer ${this.token}`\n }\n return h\n }\n}\n","import type { MeshPeer, DaemonConfig } from \"@/daemon/config\"\nimport type { AgentCard, AgentSkill } from \"./types\"\nimport { A2AClient } from \"./client\"\n\n// --- A2A Mesh: peer discovery, health checks, agent directory ---\n\nexport interface PeerState {\n peer: MeshPeer\n client: A2AClient\n healthy: boolean\n lastCheck?: Date\n agentCard?: AgentCard\n agents: AgentSkill[]\n}\n\nexport class A2AMesh {\n private peers: Map<string, PeerState> = new Map()\n private healthTimer?: ReturnType<typeof setInterval>\n private config: DaemonConfig\n private log: (...args: unknown[]) => void\n\n constructor(\n config: DaemonConfig,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[mesh]\"),\n ) {\n this.config = config\n this.log = log\n\n for (const peer of config.mesh.peers) {\n this.peers.set(peer.name, {\n peer,\n client: new A2AClient(peer.url, peer.token),\n healthy: false,\n agents: [],\n })\n }\n }\n\n /**\n * Start the mesh: discover peers and begin health checks.\n */\n async start(): Promise<void> {\n this.log(`Mesh starting with ${this.peers.size} peer(s)`)\n\n // Initial discovery\n await this.discoverAll()\n\n // Periodic health checks\n const interval = this.config.mesh.healthCheck.interval * 1000\n this.healthTimer = setInterval(() => this.discoverAll(), interval)\n }\n\n async stop(): Promise<void> {\n if (this.healthTimer) {\n clearInterval(this.healthTimer)\n }\n }\n\n /**\n * Discover agent cards from all peers.\n */\n async discoverAll(): Promise<void> {\n const results = await Promise.allSettled(\n Array.from(this.peers.entries()).map(([name, state]) =>\n this.discoverPeer(name, state),\n ),\n )\n\n const healthy = Array.from(this.peers.values()).filter((p) => p.healthy).length\n this.log(`Discovery complete: ${healthy}/${this.peers.size} peers healthy`)\n }\n\n private async discoverPeer(name: string, state: PeerState): Promise<void> {\n const timeout = this.config.mesh.healthCheck.timeout * 1000\n\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeout)\n\n const card = await state.client.getAgentCard()\n clearTimeout(timer)\n\n state.healthy = true\n state.lastCheck = new Date()\n state.agentCard = card\n state.agents = card.skills || []\n\n this.log(`Peer \"${name}\" healthy: ${card.name} (${state.agents.length} skills)`)\n } catch (e: any) {\n state.healthy = false\n state.lastCheck = new Date()\n this.log(`Peer \"${name}\" unreachable: ${e.message}`)\n }\n }\n\n /**\n * Send a task to a remote peer by name.\n * Uses the peer's /task HTTP endpoint (agentx daemon API).\n * If no agent specified, uses the first available agent on the peer.\n */\n async sendTask(peerName: string, text: string, agentId?: string): Promise<string> {\n const state = this.peers.get(peerName)\n if (!state) throw new Error(`Unknown peer: ${peerName}`)\n if (!state.healthy) throw new Error(`Peer \"${peerName}\" is not healthy`)\n\n // Default to first agent on the peer\n const agent = agentId || state.agents[0]?.id\n if (!agent) throw new Error(`Peer \"${peerName}\" has no agents`)\n\n const url = `${state.peer.url}/task`\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" }\n if (state.peer.token) {\n headers[\"Authorization\"] = `Bearer ${state.peer.token}`\n }\n\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({ agent, message: text }),\n })\n\n if (!res.ok) {\n throw new Error(`Peer \"${peerName}\" /task error: ${res.status}`)\n }\n\n const data = await res.json() as { content?: string; error?: string }\n if (data.error) throw new Error(`Peer \"${peerName}\" agent error: ${data.error}`)\n return data.content || \"No response\"\n }\n\n /**\n * Find a peer that has a specific skill.\n */\n findPeerWithSkill(skillId: string): PeerState | undefined {\n for (const state of this.peers.values()) {\n if (state.healthy && state.agents.some((a) => a.id === skillId)) {\n return state\n }\n }\n return undefined\n }\n\n /**\n * Get the combined agent directory across all healthy peers.\n */\n directory(): Array<{\n peer: string\n peerUrl: string\n healthy: boolean\n skills: AgentSkill[]\n lastCheck?: Date\n }> {\n return Array.from(this.peers.entries()).map(([name, state]) => ({\n peer: name,\n peerUrl: state.peer.url,\n healthy: state.healthy,\n skills: state.agents,\n lastCheck: state.lastCheck,\n }))\n }\n}\n","import { z } from \"zod\"\nimport { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n/**\n * Load .env file into process.env (simple, no dependency).\n */\nfunction loadDotEnv(dir: string): void {\n const envPath = resolve(dir, \".env\")\n if (!existsSync(envPath)) return\n\n const content = readFileSync(envPath, \"utf-8\")\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const eqIdx = trimmed.indexOf(\"=\")\n if (eqIdx === -1) continue\n const key = trimmed.slice(0, eqIdx).trim()\n const value = trimmed.slice(eqIdx + 1).trim()\n if (!process.env[key]) {\n process.env[key] = value\n }\n }\n}\n\n// --- Daemon configuration schema & loader ---\n\nconst providerConfigSchema = z.object({\n apiKey: z.string().optional(),\n defaultModel: z.string().optional(),\n baseUrl: z.string().optional(),\n})\n\nconst agentConfigSchema = z.object({\n name: z.string(),\n workspace: z.string(),\n tier: z.enum([\"claude-code\", \"sdk\", \"orchestrator\"]).default(\"claude-code\"),\n provider: z.string().optional(),\n model: z.string().optional(),\n systemPrompt: z.string().optional(),\n mentions: z.array(z.string()).default([]),\n maxConcurrent: z.number().default(1),\n permissionMode: z.string().default(\"default\"),\n})\n\nconst telegramAccountSchema = z.object({\n token: z.string(),\n agentBinding: z.string(),\n})\n\nconst channelsConfigSchema = z.object({\n telegram: z.object({\n enabled: z.boolean().default(false),\n accounts: z.record(z.string(), telegramAccountSchema).default({}),\n policy: z.object({\n dm: z.enum([\"pair\", \"block\"]).default(\"pair\"),\n group: z.enum([\"mention-required\", \"all\"]).default(\"mention-required\"),\n }).default({}),\n }).default({}),\n whatsapp: z.object({\n enabled: z.boolean().default(false),\n sessionDir: z.string().default(\".agentx/whatsapp-sessions\"),\n defaultAgent: z.string().optional(),\n allowFrom: z.array(z.string()).optional(),\n routes: z.array(z.object({\n contact: z.string().optional(),\n group: z.string().optional(),\n agent: z.string(),\n })).default([]),\n }).default({}),\n discord: z.object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n agentBinding: z.string().optional(),\n }).default({}),\n gitlab: z.object({\n enabled: z.boolean().default(false),\n webhookPort: z.number().default(18810),\n webhookSecret: z.string().optional(),\n host: z.string().default(\"https://gitlab.com\"),\n token: z.string().optional(),\n routes: z.array(z.object({\n project: z.string(),\n agent: z.string(),\n })).default([]),\n agentMappings: z.array(z.object({\n agentId: z.string(),\n gitlabUsernames: z.array(z.string()).default([]),\n keywords: z.array(z.string()).default([]),\n })).default([]),\n }).default({}),\n})\n\nconst cronJobSchema = z.object({\n enabled: z.boolean().default(true),\n schedule: z.string(),\n timezone: z.string().default(\"UTC\"),\n agent: z.string(),\n prompt: z.string(),\n timeout: z.number().default(600),\n model: z.string().optional(),\n onError: z.enum([\"log\", \"notify\", \"disable\"]).default(\"log\"),\n})\n\nconst meshPeerSchema = z.object({\n url: z.string(),\n name: z.string(),\n token: z.string().optional(),\n})\n\nconst meshConfigSchema = z.object({\n enabled: z.boolean().default(false),\n peers: z.array(meshPeerSchema).default([]),\n discovery: z.enum([\"static\", \"mdns\"]).default(\"static\"),\n healthCheck: z.object({\n interval: z.number().default(60),\n timeout: z.number().default(10),\n }).default({}),\n})\n\nexport const daemonConfigSchema = z.object({\n node: z.object({\n id: z.string(),\n name: z.string(),\n bind: z.string().default(\"127.0.0.1:18800\"),\n }),\n providers: z.record(z.string(), providerConfigSchema).default({}),\n agents: z.record(z.string(), agentConfigSchema).default({}),\n channels: channelsConfigSchema.default({}),\n crons: z.record(z.string(), cronJobSchema).default({}),\n mesh: meshConfigSchema.default({}),\n})\n\nexport type DaemonConfig = z.infer<typeof daemonConfigSchema>\nexport type AgentDef = z.infer<typeof agentConfigSchema>\nexport type CronJobDef = z.infer<typeof cronJobSchema>\nexport type MeshPeer = z.infer<typeof meshPeerSchema>\n\n/**\n * Expand environment variables in strings: ${VAR_NAME} -> process.env.VAR_NAME\n */\nfunction expandEnvVars(obj: unknown): unknown {\n if (typeof obj === \"string\") {\n return obj.replace(/\\$\\{(\\w+)\\}/g, (_match, name) => process.env[name] || \"\")\n }\n if (Array.isArray(obj)) {\n return obj.map(expandEnvVars)\n }\n if (obj !== null && typeof obj === \"object\") {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n result[key] = expandEnvVars(value)\n }\n return result\n }\n return obj\n}\n\n/**\n * Load daemon config from agentx.json, with env var expansion and validation.\n */\nexport function loadDaemonConfig(configPath?: string): DaemonConfig {\n const paths = configPath\n ? [configPath]\n : [\n resolve(process.cwd(), \"agentx.json\"),\n resolve(process.cwd(), \".agentx/config.json\"),\n ]\n\n // Load .env from same directory as config search\n loadDotEnv(process.cwd())\n\n let raw: string | undefined\n let foundPath: string | undefined\n\n for (const p of paths) {\n if (existsSync(p)) {\n raw = readFileSync(p, \"utf-8\")\n foundPath = p\n break\n }\n }\n\n if (!raw || !foundPath) {\n throw new Error(\n `No config found. Create agentx.json or .agentx/config.json\\n` +\n `Searched: ${paths.join(\", \")}`\n )\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch (e: any) {\n throw new Error(`Invalid JSON in ${foundPath}: ${e.message}`)\n }\n\n // Expand environment variables\n const expanded = expandEnvVars(parsed)\n\n // Validate\n const result = daemonConfigSchema.safeParse(expanded)\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .join(\"\\n\")\n throw new Error(`Config validation failed (${foundPath}):\\n${issues}`)\n }\n\n return result.data\n}\n\n/**\n * Validate agent workspace directories exist and have .claude/ setup.\n */\nexport function validateWorkspaces(config: DaemonConfig): string[] {\n const warnings: string[] = []\n\n for (const [id, agent] of Object.entries(config.agents)) {\n if (!existsSync(agent.workspace)) {\n warnings.push(`Agent \"${id}\": workspace not found at ${agent.workspace}`)\n continue\n }\n\n if (agent.tier === \"claude-code\") {\n const claudeDir = resolve(agent.workspace, \".claude\")\n if (!existsSync(claudeDir)) {\n warnings.push(\n `Agent \"${id}\": no .claude/ directory in workspace ${agent.workspace}. ` +\n `Claude Code native features (hooks, MCP, skills) won't be available.`\n )\n }\n }\n\n // Check provider availability\n const providerName = agent.provider || \"claude\"\n const providerConfig = config.providers[providerName]\n if (agent.tier !== \"claude-code\" && (!providerConfig || !providerConfig.apiKey)) {\n warnings.push(\n `Agent \"${id}\": provider \"${providerName}\" has no API key configured. ` +\n `Set providers.${providerName}.apiKey in config or use tier \"claude-code\" for subscription.`\n )\n }\n }\n\n // Validate cron agent bindings\n for (const [id, cron] of Object.entries(config.crons)) {\n if (!config.agents[cron.agent]) {\n warnings.push(`Cron \"${id}\": references unknown agent \"${cron.agent}\"`)\n }\n }\n\n // Validate channel agent bindings\n if (config.channels.telegram.enabled) {\n for (const [name, account] of Object.entries(config.channels.telegram.accounts)) {\n if (!config.agents[account.agentBinding]) {\n warnings.push(\n `Telegram account \"${name}\": references unknown agent \"${account.agentBinding}\"`\n )\n }\n }\n }\n\n return warnings\n}\n","import { execa } from \"execa\"\nimport { execFile, spawn } from \"child_process\"\nimport type { AgentDef } from \"@/daemon/config\"\n\n// --- Agent execution runtime ---\n// Routes agent tasks to the correct execution tier:\n// - claude-code: spawns claude CLI (subscription, full features)\n// - sdk: uses Claude Agent SDK (API key, programmatic)\n// - orchestrator: uses agentx's own agentic loop (any provider)\n\nexport interface AgentPeer {\n id: string\n name: string\n handle?: string // e.g. \"@noqta_devops_bot\"\n role?: string // from systemPrompt, first line\n}\n\nexport interface AgentTask {\n message: string\n agentId: string\n context?: {\n channel?: string\n sender?: string\n group?: string\n /** Attached media file (image, audio, video, document) */\n mediaPath?: string\n mediaType?: string\n /** Text of the message being replied to */\n replyToText?: string\n conversationHistory?: Array<{ role: string; content: string }>\n /** This agent's own handle on the current channel */\n myHandle?: string\n /** Other available agents and their handles */\n peers?: AgentPeer[]\n }\n}\n\nexport interface AgentResponse {\n content: string\n error?: string\n tokensUsed?: number\n duration?: number\n claudeSessionId?: string // Captured from Claude Code for session resumption\n}\n\n/** Callback for streaming text deltas */\nexport type StreamCallback = (delta: string, fullText: string) => void\n\n/**\n * Build the prompt from agent config + task context + conversation history.\n */\nfunction buildPrompt(agent: AgentDef, task: AgentTask, historyContext?: string): string {\n const parts: string[] = []\n\n if (agent.systemPrompt) {\n parts.push(agent.systemPrompt)\n }\n\n // Inject environment context so the agent knows where it is and who else is available\n if (task.context) {\n const ctx = task.context\n const envLines: string[] = [\"\", \"[Environment]\"]\n const isGitLab = ctx.channel === \"gitlab\" || ctx.channel?.startsWith(\"webhook:\")\n const isTelegram = ctx.channel === \"telegram\"\n\n if (ctx.channel) envLines.push(`Channel: ${ctx.channel}`)\n if (ctx.group) envLines.push(`Group: ${ctx.group}`)\n if (ctx.sender) envLines.push(`Message from: ${ctx.sender}`)\n if (ctx.myHandle) envLines.push(`Your handle on this channel: ${ctx.myHandle}`)\n\n // Channel-specific instructions\n if (isGitLab) {\n envLines.push(\"\")\n envLines.push(\"[IMPORTANT: You are responding to a GitLab comment/event]\")\n envLines.push(\"- Reply with a focused, actionable GitLab comment\")\n envLines.push(\"- Use markdown (GitLab flavored) for formatting\")\n envLines.push(\"- Do NOT mention Telegram handles (@noqta_*) — they don't work on GitLab\")\n envLines.push(\"- Do NOT try to delegate to other agents — reply directly\")\n envLines.push(\"- Reference issues with #IID and MRs with !IID\")\n }\n\n // Only show team roster on Telegram (where bot-to-bot works)\n if (isTelegram && ctx.peers?.length) {\n envLines.push(\"\")\n envLines.push(\"[Team — other agents you can mention to delegate or collaborate]\")\n for (const peer of ctx.peers) {\n const handle = peer.handle ? ` (mention: ${peer.handle})` : \"\"\n const role = peer.role ? ` — ${peer.role}` : \"\"\n envLines.push(`• ${peer.name}${handle}${role}`)\n }\n envLines.push(\"\")\n envLines.push(\"To involve another agent, mention their handle in your response and they will automatically see it and reply.\")\n }\n\n parts.push(envLines.join(\"\\n\"))\n }\n\n // Reply-to context (when user replies to a specific message)\n if (task.context?.replyToText) {\n parts.push(\"\")\n parts.push(`[Replying to]: ${task.context.replyToText}`)\n }\n\n // Attached media\n if (task.context?.mediaPath) {\n parts.push(\"\")\n parts.push(`[Attached file: ${task.context.mediaPath}]`)\n parts.push(`[File type: ${task.context.mediaType || \"unknown\"}]`)\n if (task.context.mediaType?.startsWith(\"image/\")) {\n parts.push(\"Please read/view this image file and describe or respond to it.\")\n } else if (task.context.mediaType?.startsWith(\"audio/\")) {\n parts.push(\"Please transcribe this audio file and respond to its content.\")\n } else if (task.context.mediaType?.startsWith(\"video/\")) {\n parts.push(\"A video file is attached. Describe what you can determine about it.\")\n } else {\n parts.push(\"Please read this file and respond based on its content.\")\n }\n }\n\n // Inject conversation history for session continuity\n if (historyContext) {\n parts.push(\"\")\n parts.push(historyContext)\n }\n\n parts.push(\"\")\n parts.push(task.message)\n\n return parts.join(\"\\n\")\n}\n\n/**\n * Build CLI args for claude command.\n */\nfunction buildClaudeArgs(\n agent: AgentDef,\n prompt: string,\n streaming: boolean,\n resumeSessionId?: string,\n): string[] {\n const args: string[] = [\n \"-p\", prompt,\n \"--output-format\", streaming ? \"stream-json\" : \"json\",\n ]\n\n // stream-json requires --verbose in recent Claude Code versions\n if (streaming) {\n args.push(\"--verbose\")\n }\n\n // Resume existing Claude session for conversation continuity\n if (resumeSessionId) {\n args.push(\"--resume\", resumeSessionId)\n }\n\n if (agent.model) {\n args.push(\"--model\", agent.model)\n }\n\n if (agent.permissionMode === \"bypassPermissions\") {\n args.push(\"--dangerously-skip-permissions\")\n }\n\n return args\n}\n\n/**\n * Parse JSON output from `claude -p --output-format json`.\n * Returns the text result and session_id.\n */\nfunction parseClaudeJsonOutput(stdout: string): { text: string; sessionId?: string } {\n try {\n const data = JSON.parse(stdout)\n return {\n text: data.result || data.content || \"\",\n sessionId: data.session_id,\n }\n } catch {\n // Not JSON — return raw text (fallback for non-json output)\n return { text: stdout }\n }\n}\n\n/**\n * Execute a task using the Claude Code CLI (tier: \"claude-code\").\n * Non-streaming — waits for full response.\n */\nexport async function executeClaudeCode(\n agent: AgentDef,\n task: AgentTask,\n historyContext?: string,\n resumeSessionId?: string,\n): Promise<AgentResponse> {\n const start = Date.now()\n const prompt = buildPrompt(agent, task, resumeSessionId ? undefined : historyContext)\n const args = buildClaudeArgs(agent, prompt, false, resumeSessionId)\n\n try {\n const { stdout, stderr, exitCode } = await new Promise<{ stdout: string; stderr: string; exitCode: number }>((resolve, reject) => {\n const proc = execFile(\"claude\", args, {\n cwd: agent.workspace,\n timeout: 600_000,\n maxBuffer: 10 * 1024 * 1024,\n env: { ...process.env, HOME: process.env.HOME || \"/home/\" + (process.env.USER || \"clawd\") },\n }, (error, stdout, stderr) => {\n // Always resolve — we handle errors ourselves based on stdout/stderr\n resolve({\n stdout: stdout || \"\",\n stderr: stderr || \"\",\n exitCode: error ? (error as any).code ?? 1 : 0,\n })\n })\n })\n\n if (!stdout && exitCode !== 0) {\n const errMsg = stderr?.trim() || `Claude Code exited with code ${exitCode}`\n return {\n content: \"\",\n error: errMsg.slice(0, 300),\n duration: Date.now() - start,\n }\n }\n\n // Parse JSON output — gives us both text and session_id cleanly\n const parsed = parseClaudeJsonOutput(stdout)\n\n return {\n content: parsed.text,\n duration: Date.now() - start,\n claudeSessionId: parsed.sessionId,\n }\n } catch (error: any) {\n console.error(`[runtime] execFile threw: ${error.message}`)\n return {\n content: \"\",\n error: error.message || \"Claude Code failed\",\n duration: Date.now() - start,\n }\n }\n}\n\n/**\n * Execute with streaming — calls onDelta with text chunks as they arrive.\n * Uses claude --output-format stream-json to get real-time output.\n */\nexport async function executeClaudeCodeStreaming(\n agent: AgentDef,\n task: AgentTask,\n onDelta: StreamCallback,\n historyContext?: string,\n resumeSessionId?: string,\n): Promise<AgentResponse> {\n const start = Date.now()\n const prompt = buildPrompt(agent, task, resumeSessionId ? undefined : historyContext)\n const args = buildClaudeArgs(agent, prompt, true, resumeSessionId)\n\n let fullText = \"\"\n\n try {\n const proc = execa(\"claude\", args, {\n cwd: agent.workspace,\n timeout: 600_000, // 10 minutes\n reject: false,\n env: process.env,\n // Don't buffer — we'll read stdout line by line\n buffer: false,\n })\n\n // Parse stream-json output line by line\n if (proc.stdout) {\n let lineBuffer = \"\"\n\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n lineBuffer += chunk.toString()\n const lines = lineBuffer.split(\"\\n\")\n lineBuffer = lines.pop() || \"\"\n\n for (const line of lines) {\n if (!line.trim()) continue\n try {\n const event = JSON.parse(line)\n\n // Claude stream-json emits different event types\n // \"assistant\" messages with content contain the text\n if (event.type === \"assistant\" && event.message?.content) {\n for (const block of event.message.content) {\n if (block.type === \"text\" && block.text) {\n const delta = block.text.slice(fullText.length)\n if (delta) {\n fullText = block.text\n onDelta(delta, fullText)\n }\n }\n }\n }\n\n // \"content_block_delta\" for streaming text\n if (event.type === \"content_block_delta\" && event.delta?.text) {\n fullText += event.delta.text\n onDelta(event.delta.text, fullText)\n }\n\n // \"result\" event contains final text\n if (event.type === \"result\" && event.result) {\n const resultText = typeof event.result === \"string\"\n ? event.result\n : event.result\n if (typeof resultText === \"string\" && resultText.length > fullText.length) {\n const delta = resultText.slice(fullText.length)\n fullText = resultText\n if (delta) onDelta(delta, fullText)\n }\n }\n } catch {\n // Not JSON — could be raw text output, append it\n if (line.trim() && !line.startsWith(\"{\")) {\n fullText += line + \"\\n\"\n onDelta(line + \"\\n\", fullText)\n }\n }\n }\n })\n }\n\n const result = await proc\n\n // If we got no streaming content, fall back to stdout\n if (!fullText && result.stdout) {\n fullText = typeof result.stdout === \"string\" ? result.stdout : \"\"\n }\n\n if (result.exitCode !== 0 && !fullText) {\n const stderr = typeof result.stderr === \"string\" ? result.stderr : \"\"\n return {\n content: \"\",\n error: stderr || `Claude Code exited with code ${result.exitCode}`,\n duration: Date.now() - start,\n }\n }\n\n return {\n content: fullText,\n duration: Date.now() - start,\n }\n } catch (error: any) {\n return {\n content: fullText || \"\",\n error: error.message,\n duration: Date.now() - start,\n }\n }\n}\n\n/**\n * Execute a task using the Claude Agent SDK (tier: \"sdk\").\n */\nexport async function executeSdk(\n agent: AgentDef,\n task: AgentTask,\n apiKey: string,\n): Promise<AgentResponse> {\n const start = Date.now()\n\n try {\n // @ts-ignore — SDK may not be installed\n const sdk = await import(\"@anthropic-ai/claude-agent-sdk\")\n const { query } = sdk\n\n const prompt = agent.systemPrompt\n ? `${agent.systemPrompt}\\n\\n${task.message}`\n : task.message\n\n let content = \"\"\n\n const q = query({\n prompt,\n options: {\n model: agent.model,\n cwd: agent.workspace,\n permissionMode: \"bypassPermissions\" as any,\n },\n })\n\n for await (const message of q) {\n if (message.type === \"result\" && message.subtype === \"success\") {\n content = message.result || \"\"\n }\n }\n\n return {\n content,\n duration: Date.now() - start,\n }\n } catch (error: any) {\n return {\n content: \"\",\n error: `SDK error: ${error.message}`,\n duration: Date.now() - start,\n }\n }\n}\n\n/**\n * Execute a task using agentx's own orchestrator (tier: \"orchestrator\").\n */\nexport async function executeOrchestrator(\n agent: AgentDef,\n task: AgentTask,\n apiKey?: string,\n): Promise<AgentResponse> {\n const start = Date.now()\n\n try {\n const { generate } = await import(\"@/agent\")\n const providerName = agent.provider || \"claude-code\"\n\n const result = await generate({\n task: task.message,\n cwd: agent.workspace,\n provider: providerName as any,\n model: agent.model,\n apiKey,\n overwrite: true,\n interactive: false,\n context7: false,\n })\n\n return {\n content: result.content || \"Done.\",\n tokensUsed: result.tokensUsed,\n duration: Date.now() - start,\n }\n } catch (error: any) {\n return {\n content: \"\",\n error: `Orchestrator error: ${error.message}`,\n duration: Date.now() - start,\n }\n }\n}\n\n/**\n * Route a task to the correct execution tier.\n */\nexport async function executeTask(\n agent: AgentDef,\n task: AgentTask,\n providers: Record<string, { apiKey?: string }>,\n onDelta?: StreamCallback,\n historyContext?: string,\n resumeSessionId?: string,\n): Promise<AgentResponse> {\n switch (agent.tier) {\n case \"claude-code\":\n if (onDelta) {\n return executeClaudeCodeStreaming(agent, task, onDelta, historyContext, resumeSessionId)\n }\n return executeClaudeCode(agent, task, historyContext, resumeSessionId)\n\n case \"sdk\": {\n const providerName = agent.provider || \"claude\"\n const apiKey = providers[providerName]?.apiKey\n if (!apiKey) {\n return {\n content: \"\",\n error: `No API key for provider \"${providerName}\". Configure providers.${providerName}.apiKey`,\n }\n }\n return executeSdk(agent, task, apiKey)\n }\n\n case \"orchestrator\": {\n const providerName = agent.provider || \"claude-code\"\n const apiKey = providers[providerName]?.apiKey\n return executeOrchestrator(agent, task, apiKey)\n }\n\n default:\n return {\n content: \"\",\n error: `Unknown tier: ${agent.tier}`,\n }\n }\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join, relative, dirname } from \"path\"\nimport type { WikiArticle, WikiArticleMeta, WikiEntry, WikiIndex, WikiAccess } from \"./types\"\n\n// --- Wiki Store: filesystem-based knowledge base with permissions ---\n//\n// Structure:\n// .agentx/wiki/\n// _index.md # Master index with aliases\n// _backlinks.json # Reverse link index\n// raw/entries/ # One .md per ingested entry\n// {directories}/ # Articles organized by type\n//\n// Permission model:\n// - private: only owner agent reads/writes\n// - shared: owner writes, listed agents read\n// - public: owner writes, all agents read\n\nexport class WikiStore {\n private baseDir: string\n private rawDir: string\n private log: (...args: unknown[]) => void\n\n constructor(\n baseDir: string = resolve(process.cwd(), \".agentx/wiki\"),\n log: (...args: unknown[]) => void = console.error.bind(console, \"[wiki]\"),\n ) {\n this.baseDir = baseDir\n this.rawDir = resolve(baseDir, \"raw/entries\")\n this.log = log\n\n mkdirSync(this.rawDir, { recursive: true })\n mkdirSync(resolve(baseDir, \"raw\"), { recursive: true })\n }\n\n // --- Permission checks ---\n\n /**\n * Check if an agent can read an article.\n */\n canRead(article: WikiArticleMeta, agentId: string): boolean {\n if (article.access === \"public\") return true\n if (article.owner === agentId) return true\n if (article.access === \"shared\" && article.sharedWith?.includes(agentId)) return true\n return false\n }\n\n /**\n * Check if an agent can write an article.\n */\n canWrite(article: WikiArticleMeta, agentId: string): boolean {\n return article.owner === agentId\n }\n\n // --- Raw entries (ingestion) ---\n\n /**\n * Add a raw entry (from a conversation, cron result, etc.)\n */\n addEntry(entry: WikiEntry): string {\n const filename = `${entry.date}_${entry.id}.md`\n const filepath = resolve(this.rawDir, filename)\n\n const frontmatter = [\n \"---\",\n `id: ${entry.id}`,\n `date: ${entry.date}`,\n `agent: ${entry.agentId}`,\n `source: ${entry.source}`,\n ]\n if (entry.sourceContext) frontmatter.push(`context: ${entry.sourceContext}`)\n if (entry.meta) {\n for (const [k, v] of Object.entries(entry.meta)) {\n frontmatter.push(`${k}: ${JSON.stringify(v)}`)\n }\n }\n frontmatter.push(\"---\", \"\", entry.content)\n\n writeFileSync(filepath, frontmatter.join(\"\\n\"))\n return filename\n }\n\n /**\n * List raw entries, optionally filtered by agent or date range.\n */\n listEntries(filter?: { agentId?: string; after?: string; before?: string }): WikiEntry[] {\n if (!existsSync(this.rawDir)) return []\n\n const files = readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).sort()\n const entries: WikiEntry[] = []\n\n for (const file of files) {\n const content = readFileSync(resolve(this.rawDir, file), \"utf-8\")\n const entry = this.parseEntry(content, file)\n if (!entry) continue\n\n if (filter?.agentId && entry.agentId !== filter.agentId) continue\n if (filter?.after && entry.date < filter.after) continue\n if (filter?.before && entry.date > filter.before) continue\n\n entries.push(entry)\n }\n\n return entries\n }\n\n private parseEntry(content: string, filename: string): WikiEntry | null {\n const fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const body = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim() || \"\"\n }\n\n return {\n id: get(\"id\") || filename.replace(\".md\", \"\"),\n date: get(\"date\"),\n agentId: get(\"agent\"),\n source: get(\"source\"),\n sourceContext: get(\"context\") || undefined,\n content: body,\n }\n }\n\n // --- Articles (compiled knowledge) ---\n\n /**\n * Write or update a wiki article.\n */\n writeArticle(\n path: string,\n meta: WikiArticleMeta,\n content: string,\n agentId: string,\n ): boolean {\n // Check write permission if article exists\n const existing = this.readArticle(path)\n if (existing && !this.canWrite(existing.meta, agentId)) {\n this.log(`Permission denied: \"${agentId}\" cannot write \"${path}\" (owner: ${existing.meta.owner})`)\n return false\n }\n\n const fullPath = resolve(this.baseDir, path)\n mkdirSync(dirname(fullPath), { recursive: true })\n\n const frontmatter = [\n \"---\",\n `title: \"${meta.title}\"`,\n `type: ${meta.type}`,\n `owner: ${meta.owner}`,\n `access: ${meta.access}`,\n ]\n if (meta.sharedWith?.length) {\n frontmatter.push(`shared_with: [${meta.sharedWith.map(s => `\"${s}\"`).join(\", \")}]`)\n }\n frontmatter.push(\n `created: ${meta.created}`,\n `last_updated: ${meta.lastUpdated}`,\n `related: [${meta.related.map(r => `\"${r}\"`).join(\", \")}]`,\n `sources: [${meta.sources.map(s => `\"${s}\"`).join(\", \")}]`,\n )\n if (meta.tags?.length) {\n frontmatter.push(`tags: [${meta.tags.map(t => `\"${t}\"`).join(\", \")}]`)\n }\n frontmatter.push(\"---\", \"\", content)\n\n writeFileSync(fullPath, frontmatter.join(\"\\n\"))\n return true\n }\n\n /**\n * Read an article. Returns null if not found.\n */\n readArticle(path: string): WikiArticle | null {\n const fullPath = resolve(this.baseDir, path)\n if (!existsSync(fullPath)) return null\n\n const raw = readFileSync(fullPath, \"utf-8\")\n return this.parseArticle(raw, path)\n }\n\n /**\n * Read an article with permission check.\n */\n readArticleAs(path: string, agentId: string): WikiArticle | null {\n const article = this.readArticle(path)\n if (!article) return null\n if (!this.canRead(article.meta, agentId)) return null\n return article\n }\n\n private parseArticle(raw: string, path: string): WikiArticle | null {\n const fmMatch = raw.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const content = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim().replace(/^\"(.*)\"$/, \"$1\") || \"\"\n }\n\n const getArray = (key: string): string[] => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*\\\\[(.*)\\\\]$`, \"m\"))\n if (!m) return []\n return m[1].split(\",\").map(s => s.trim().replace(/^\"(.*)\"$/, \"$1\")).filter(Boolean)\n }\n\n return {\n meta: {\n title: get(\"title\"),\n type: get(\"type\"),\n owner: get(\"owner\"),\n access: (get(\"access\") as WikiAccess) || \"public\",\n sharedWith: getArray(\"shared_with\"),\n created: get(\"created\"),\n lastUpdated: get(\"last_updated\"),\n related: getArray(\"related\"),\n sources: getArray(\"sources\"),\n tags: getArray(\"tags\"),\n },\n content,\n path,\n }\n }\n\n // --- Index and discovery ---\n\n /**\n * List all articles accessible to an agent.\n */\n listArticles(agentId: string): WikiArticle[] {\n const articles: WikiArticle[] = []\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (article && this.canRead(article.meta, agentId)) {\n articles.push(article)\n }\n })\n return articles\n }\n\n /**\n * Search articles by keyword. Returns articles the agent can read.\n */\n search(query: string, agentId: string, maxResults: number = 10): WikiArticle[] {\n const queryLower = query.toLowerCase()\n const results: Array<{ article: WikiArticle; score: number }> = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article || !this.canRead(article.meta, agentId)) return\n\n let score = 0\n const titleLower = article.meta.title.toLowerCase()\n const contentLower = article.content.toLowerCase()\n\n // Title match scores highest\n if (titleLower.includes(queryLower)) score += 10\n // Tag match\n if (article.meta.tags?.some(t => t.toLowerCase().includes(queryLower))) score += 5\n // Content match (count occurrences, cap at 5)\n const occurrences = (contentLower.match(new RegExp(queryLower.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"), \"g\")) || []).length\n score += Math.min(occurrences, 5)\n\n if (score > 0) {\n results.push({ article, score })\n }\n })\n\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults)\n .map(r => r.article)\n }\n\n /**\n * Find articles relevant to a message (for context injection).\n * Extracts keywords from the message and searches the wiki.\n */\n findRelevant(message: string, agentId: string, maxArticles: number = 3): WikiArticle[] {\n // Extract meaningful keywords (skip common words)\n const stopWords = new Set([\n \"the\", \"a\", \"an\", \"is\", \"are\", \"was\", \"were\", \"be\", \"been\", \"being\",\n \"have\", \"has\", \"had\", \"do\", \"does\", \"did\", \"will\", \"would\", \"could\",\n \"should\", \"may\", \"might\", \"can\", \"shall\", \"to\", \"of\", \"in\", \"for\",\n \"on\", \"with\", \"at\", \"by\", \"from\", \"as\", \"into\", \"about\", \"through\",\n \"and\", \"but\", \"or\", \"not\", \"no\", \"if\", \"then\", \"so\", \"what\", \"how\",\n \"when\", \"where\", \"who\", \"which\", \"that\", \"this\", \"it\", \"i\", \"you\",\n \"we\", \"they\", \"he\", \"she\", \"me\", \"my\", \"your\", \"our\", \"their\",\n \"please\", \"just\", \"also\", \"very\", \"much\", \"some\", \"any\", \"all\",\n ])\n\n const words = message.toLowerCase()\n .replace(/[^a-z0-9\\s@_-]/g, \" \")\n .split(/\\s+/)\n .filter(w => w.length > 2 && !stopWords.has(w))\n\n if (words.length === 0) return []\n\n // Score articles by keyword matches\n const articleScores = new Map<string, { article: WikiArticle; score: number }>()\n\n for (const word of words) {\n const matches = this.search(word, agentId, 5)\n for (const article of matches) {\n const existing = articleScores.get(article.path)\n if (existing) {\n existing.score += 1\n } else {\n articleScores.set(article.path, { article, score: 1 })\n }\n }\n }\n\n return Array.from(articleScores.values())\n .sort((a, b) => b.score - a.score)\n .slice(0, maxArticles)\n .map(r => r.article)\n }\n\n /**\n * Build context string from relevant wiki articles for prompt injection.\n * Token-efficient: includes title + first ~500 chars of each article.\n */\n buildContext(articles: WikiArticle[], maxChars: number = 4000): string {\n if (articles.length === 0) return \"\"\n\n const lines: string[] = [\"[Wiki Knowledge]\"]\n\n let totalChars = 0\n for (const article of articles) {\n const header = `\\n## ${article.meta.title} (${article.meta.type})`\n const body = article.content.length > 600\n ? article.content.slice(0, 600) + \"...\"\n : article.content\n\n const chunk = header + \"\\n\" + body\n if (totalChars + chunk.length > maxChars) break\n\n lines.push(chunk)\n totalChars += chunk.length\n }\n\n lines.push(\"\\n[End Wiki Knowledge]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Rebuild the master index.\n */\n rebuildIndex(): WikiIndex {\n const articles: WikiIndex[\"articles\"] = []\n const backlinks = new Map<string, number>()\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n // Count wikilinks for backlinks\n const links = article.content.match(/\\[\\[([^\\]]+)\\]\\]/g) || []\n for (const link of links) {\n const target = link.replace(/\\[\\[|\\]\\]/g, \"\")\n backlinks.set(target, (backlinks.get(target) || 0) + 1)\n }\n\n // Extract aliases from title and tags\n const aliases = [article.meta.title.toLowerCase()]\n if (article.meta.tags) {\n aliases.push(...article.meta.tags.map(t => t.toLowerCase()))\n }\n\n articles.push({\n path: relPath,\n title: article.meta.title,\n type: article.meta.type,\n owner: article.meta.owner,\n access: article.meta.access,\n sharedWith: article.meta.sharedWith,\n aliases,\n backlinks: backlinks.get(article.meta.title) || 0,\n })\n })\n\n const index: WikiIndex = {\n articles,\n lastRebuilt: new Date().toISOString(),\n }\n\n // Write index files\n writeFileSync(\n resolve(this.baseDir, \"_index.json\"),\n JSON.stringify(index, null, 2),\n )\n\n // Write human-readable WIKI.md\n const md = [\"# Wiki Index\", \"\", `Last rebuilt: ${index.lastRebuilt}`, \"\"]\n const byType = new Map<string, typeof articles>()\n for (const a of articles) {\n const list = byType.get(a.type) || []\n list.push(a)\n byType.set(a.type, list)\n }\n\n for (const [type, list] of Array.from(byType.entries()).sort()) {\n md.push(`## ${type}`, \"\")\n for (const a of list.sort((x, y) => x.title.localeCompare(y.title))) {\n const access = a.access === \"private\" ? \" (private)\" : a.access === \"shared\" ? \" (shared)\" : \"\"\n md.push(`- [${a.title}](${a.path})${access} — owner: ${a.owner}`)\n }\n md.push(\"\")\n }\n\n writeFileSync(resolve(this.baseDir, \"WIKI.md\"), md.join(\"\\n\"))\n\n this.log(`Index rebuilt: ${articles.length} articles`)\n return index\n }\n\n /**\n * Get wiki stats.\n */\n stats(): {\n totalArticles: number\n totalEntries: number\n articlesByType: Record<string, number>\n articlesByAccess: Record<string, number>\n articlesByOwner: Record<string, number>\n } {\n let totalArticles = 0\n const byType: Record<string, number> = {}\n const byAccess: Record<string, number> = {}\n const byOwner: Record<string, number> = {}\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\") return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n totalArticles++\n byType[article.meta.type] = (byType[article.meta.type] || 0) + 1\n byAccess[article.meta.access] = (byAccess[article.meta.access] || 0) + 1\n byOwner[article.meta.owner] = (byOwner[article.meta.owner] || 0) + 1\n })\n\n const entryFiles = existsSync(this.rawDir)\n ? readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).length\n : 0\n\n return {\n totalArticles,\n totalEntries: entryFiles,\n articlesByType: byType,\n articlesByAccess: byAccess,\n articlesByOwner: byOwner,\n }\n }\n\n // --- Helpers ---\n\n private walkDir(dir: string, callback: (filePath: string) => void): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const stat = statSync(full)\n if (stat.isDirectory()) {\n this.walkDir(full, callback)\n } else {\n callback(full)\n }\n }\n }\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Conversation session store ---\n// One session per agent + channel + chatId + day.\n// For claude-code tier: stores the Claude session ID so we can --resume it.\n// For other tiers: stores message history as text, prepended to each prompt.\n\nexport interface SessionMessage {\n role: \"user\" | \"agent\"\n name?: string // sender name or agent name\n content: string\n timestamp: string\n}\n\nexport interface Session {\n id: string\n agentId: string\n channel: string\n chatId: string\n day: string // YYYY-MM-DD\n claudeSessionId?: string // Claude Code native session ID (for --resume)\n messages: SessionMessage[]\n createdAt: string\n updatedAt: string\n}\n\nconst MAX_HISTORY_CHARS = 12000 // Keep last ~12k chars of history to fit in context\nconst MAX_MESSAGES = 30 // Keep last 30 messages max\n\nexport class SessionStore {\n private sessionsDir: string\n private cache: Map<string, Session> = new Map()\n\n constructor(baseDir: string = process.cwd()) {\n this.sessionsDir = resolve(baseDir, \".agentx/sessions\")\n if (!existsSync(this.sessionsDir)) {\n mkdirSync(this.sessionsDir, { recursive: true })\n }\n }\n\n /**\n * Build a deterministic session key.\n */\n private sessionKey(agentId: string, channel: string, chatId: string): string {\n const day = new Date().toISOString().slice(0, 10) // YYYY-MM-DD\n return `${agentId}:${channel}:${chatId}:${day}`\n }\n\n private sessionFile(key: string): string {\n // Sanitize key for filesystem\n const safe = key.replace(/[^a-zA-Z0-9_:-]/g, \"_\")\n return resolve(this.sessionsDir, `${safe}.json`)\n }\n\n /**\n * Get or create a session for this agent+channel+chat+day.\n */\n getSession(agentId: string, channel: string, chatId: string): Session {\n const key = this.sessionKey(agentId, channel, chatId)\n\n // Check cache\n if (this.cache.has(key)) {\n return this.cache.get(key)!\n }\n\n // Try loading from disk\n const file = this.sessionFile(key)\n if (existsSync(file)) {\n try {\n const data = JSON.parse(readFileSync(file, \"utf-8\")) as Session\n this.cache.set(key, data)\n return data\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Create new session\n const day = new Date().toISOString().slice(0, 10)\n const session: Session = {\n id: key,\n agentId,\n channel,\n chatId,\n day,\n messages: [],\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n }\n\n this.cache.set(key, session)\n this.save(session)\n return session\n }\n\n /**\n * Add a user message to the session.\n */\n addUserMessage(agentId: string, channel: string, chatId: string, senderName: string, content: string): void {\n const session = this.getSession(agentId, channel, chatId)\n session.messages.push({\n role: \"user\",\n name: senderName,\n content,\n timestamp: new Date().toISOString(),\n })\n this.trim(session)\n session.updatedAt = new Date().toISOString()\n this.save(session)\n }\n\n /**\n * Add an agent response to the session.\n */\n addAgentMessage(agentId: string, channel: string, chatId: string, content: string): void {\n const session = this.getSession(agentId, channel, chatId)\n session.messages.push({\n role: \"agent\",\n name: agentId,\n content,\n timestamp: new Date().toISOString(),\n })\n this.trim(session)\n session.updatedAt = new Date().toISOString()\n this.save(session)\n }\n\n /**\n * Get the Claude Code session ID for this conversation (for --resume).\n * Returns undefined if no session yet (first message of the day).\n */\n getClaudeSessionId(agentId: string, channel: string, chatId: string): string | undefined {\n const session = this.getSession(agentId, channel, chatId)\n return session.claudeSessionId\n }\n\n /**\n * Store the Claude Code session ID after first invocation.\n */\n setClaudeSessionId(agentId: string, channel: string, chatId: string, claudeSessionId: string): void {\n const session = this.getSession(agentId, channel, chatId)\n session.claudeSessionId = claudeSessionId\n session.updatedAt = new Date().toISOString()\n this.save(session)\n }\n\n /**\n * Build conversation history string to prepend to the prompt.\n * Returns empty string if no history.\n * Used for non-claude-code tiers that don't have native sessions.\n */\n buildHistoryContext(agentId: string, channel: string, chatId: string): string {\n const session = this.getSession(agentId, channel, chatId)\n if (session.messages.length === 0) return \"\"\n\n const lines: string[] = [\n `[Conversation history for today (${session.day})]`,\n ]\n\n for (const msg of session.messages) {\n const time = msg.timestamp.slice(11, 16) // HH:MM\n if (msg.role === \"user\") {\n lines.push(`[${time}] ${msg.name || \"User\"}: ${msg.content}`)\n } else {\n lines.push(`[${time}] ${msg.name || \"Agent\"}: ${msg.content}`)\n }\n }\n\n lines.push(\"[End of history — respond to the latest message above]\")\n lines.push(\"\")\n\n return lines.join(\"\\n\")\n }\n\n /**\n * Trim session to stay within limits.\n */\n private trim(session: Session): void {\n // Trim by message count\n if (session.messages.length > MAX_MESSAGES) {\n session.messages = session.messages.slice(-MAX_MESSAGES)\n }\n\n // Trim by total character count\n let totalChars = session.messages.reduce((sum, m) => sum + m.content.length, 0)\n while (totalChars > MAX_HISTORY_CHARS && session.messages.length > 2) {\n const removed = session.messages.shift()!\n totalChars -= removed.content.length\n }\n }\n\n private save(session: Session): void {\n try {\n const file = this.sessionFile(session.id)\n writeFileSync(file, JSON.stringify(session, null, 2))\n } catch {\n // Best-effort persistence\n }\n }\n}\n","// --- Rate limiter for agent tasks ---\n// Prevents message floods from exhausting Claude API/subscription.\n// Per-agent, sliding window.\n\nexport class RateLimiter {\n private windows: Map<string, number[]> = new Map()\n private maxPerMinute: number\n private maxPerHour: number\n\n constructor(maxPerMinute: number = 10, maxPerHour: number = 100) {\n this.maxPerMinute = maxPerMinute\n this.maxPerHour = maxPerHour\n }\n\n /**\n * Check if an agent can process a task. Returns true if allowed.\n */\n check(agentId: string): { allowed: boolean; reason?: string } {\n const now = Date.now()\n const timestamps = this.windows.get(agentId) || []\n\n // Clean old entries (older than 1 hour)\n const hourAgo = now - 3600_000\n const recent = timestamps.filter(t => t > hourAgo)\n\n // Check per-minute\n const minuteAgo = now - 60_000\n const lastMinute = recent.filter(t => t > minuteAgo).length\n if (lastMinute >= this.maxPerMinute) {\n return { allowed: false, reason: `Rate limit: ${lastMinute}/${this.maxPerMinute} per minute` }\n }\n\n // Check per-hour\n if (recent.length >= this.maxPerHour) {\n return { allowed: false, reason: `Rate limit: ${recent.length}/${this.maxPerHour} per hour` }\n }\n\n // Record this request\n recent.push(now)\n this.windows.set(agentId, recent)\n\n return { allowed: true }\n }\n\n /**\n * Get current usage for an agent.\n */\n usage(agentId: string): { lastMinute: number; lastHour: number } {\n const now = Date.now()\n const timestamps = this.windows.get(agentId) || []\n return {\n lastMinute: timestamps.filter(t => t > now - 60_000).length,\n lastHour: timestamps.filter(t => t > now - 3600_000).length,\n }\n }\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks estimated token usage per agent per day.\n// Persisted to .agentx/usage/{YYYY-MM-DD}.json\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\ninterface AgentUsage {\n tasks: number\n estimatedTokens: number\n totalDuration: number\n errors: number\n}\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n }\n\n /**\n * Record a task execution.\n */\n record(agentId: string, messageLength: number, responseLength: number, duration: number, error?: boolean): void {\n const usage = this.today()\n const agent = usage.agents[agentId] || { tasks: 0, estimatedTokens: 0, totalDuration: 0, errors: 0 }\n\n agent.tasks++\n agent.totalDuration += duration\n // Rough estimate: 1 token ≈ 4 chars for English text\n agent.estimatedTokens += Math.ceil((messageLength + responseLength) / 4)\n if (error) agent.errors++\n\n usage.agents[agentId] = agent\n this.save(usage)\n }\n\n /**\n * Get today's usage summary.\n */\n today(): DailyUsage {\n const date = new Date().toISOString().slice(0, 10)\n\n if (this.cache?.date === date) return this.cache\n\n const path = this.filePath(date)\n if (existsSync(path)) {\n try {\n this.cache = JSON.parse(readFileSync(path, \"utf-8\"))\n return this.cache!\n } catch {}\n }\n\n this.cache = { date, agents: {} }\n return this.cache\n }\n\n /**\n * Get usage for a specific date.\n */\n getDate(date: string): DailyUsage | null {\n const path = this.filePath(date)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"))\n } catch {\n return null\n }\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalErrors: number\n byAgent: Record<string, { tasks: number; tokens: number; avgDuration: number }>\n } {\n let totalTasks = 0\n let totalTokens = 0\n let totalErrors = 0\n const byAgent: Record<string, { tasks: number; tokens: number; avgDuration: number; totalDuration: number }> = {}\n\n for (let i = 0; i < days; i++) {\n const date = new Date(Date.now() - i * 86400_000).toISOString().slice(0, 10)\n const usage = this.getDate(date)\n if (!usage) continue\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n totalTasks += agent.tasks\n totalTokens += agent.estimatedTokens\n totalErrors += agent.errors\n\n const existing = byAgent[id] || { tasks: 0, tokens: 0, avgDuration: 0, totalDuration: 0 }\n existing.tasks += agent.tasks\n existing.tokens += agent.estimatedTokens\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n byAgent[id] = existing\n }\n }\n\n return { totalTasks, totalTokens, totalErrors, byAgent }\n }\n\n private filePath(date: string): string {\n return resolve(this.dir, `${date}.json`)\n }\n\n private save(usage: DailyUsage): void {\n try {\n writeFileSync(this.filePath(usage.date), JSON.stringify(usage, null, 2))\n } catch {}\n }\n}\n","// --- Agent Context Engine ---\n//\n// Structured, layered context with token budget management.\n// Each context layer has a priority, max tokens, and rules.\n//\n// Layers (highest priority first):\n// 1. Channel — where the message came from + channel-specific rules\n// 2. Scope — group/personal/project + scope-specific constraints\n// 3. Identity — who the agent is, what it can do\n// 4. Peers — other agents available (only on channels that support delegation)\n// 5. Intent — what the user is asking about (extracted from message)\n// 6. Artifacts — project, issue, MR, file references\n// 7. History — group conversation log or session history\n// 8. Wiki — relevant knowledge articles\n//\n// Token budget: each layer gets a max allocation. Total capped at configurable limit.\n// Layers are rendered top-down; if budget exhausted, lower layers are truncated or skipped.\n\nexport interface ContextLayer {\n name: string\n priority: number // lower = higher priority (1 = most important)\n maxTokens: number // max tokens for this layer\n content: string // rendered text\n tags: string[] // metadata tags for filtering/matching\n rules?: string[] // constraints/instructions specific to this layer\n}\n\nexport interface ContextConfig {\n /** Total token budget for all context combined (default: 4000) */\n totalBudget: number\n /** Per-layer budgets override (layer name -> max tokens) */\n layerBudgets?: Record<string, number>\n}\n\nconst DEFAULT_CONFIG: ContextConfig = {\n totalBudget: 4000,\n layerBudgets: {\n channel: 200,\n scope: 200,\n identity: 300,\n peers: 400,\n intent: 200,\n artifacts: 500,\n history: 1200,\n wiki: 1000,\n },\n}\n\n// Rough estimate: 1 token ≈ 4 chars for English\nconst CHARS_PER_TOKEN = 4\n\nexport interface ContextInput {\n // Channel layer\n channel: string // \"telegram\", \"whatsapp\", \"gitlab\", \"discord\"\n channelScope?: \"group\" | \"personal\" | \"project\"\n\n // Scope layer\n groupName?: string\n projectPath?: string // \"mtgl/mtgl-system-v2\"\n issueMR?: { type: string; iid: string; title: string }\n\n // Identity layer\n agentId: string\n agentName: string\n agentHandle?: string // \"@noqta_pm_mtgl_bot\"\n systemPrompt?: string\n\n // Participants\n sender: string\n senderRole?: string // \"user\", \"agent:atlas\"\n\n // Peers (other agents)\n peers?: Array<{ name: string; handle?: string; role?: string }>\n\n // Artifacts\n mediaPath?: string\n mediaType?: string\n replyToText?: string\n\n // History\n groupHistory?: string // from GroupLog.buildContext()\n sessionHistory?: string // from SessionStore.buildHistoryContext()\n\n // Wiki\n wikiContext?: string // from WikiStore.buildContext()\n\n // Message\n message: string\n}\n\n/**\n * Build optimized context string from structured input.\n * Respects token budgets per layer and total cap.\n */\nexport function buildAgentContext(input: ContextInput, config: ContextConfig = DEFAULT_CONFIG): string {\n const layers = buildLayers(input, config)\n\n // Sort by priority (lower = first)\n layers.sort((a, b) => a.priority - b.priority)\n\n // Render within budget\n const parts: string[] = []\n let totalChars = 0\n const maxChars = config.totalBudget * CHARS_PER_TOKEN\n\n for (const layer of layers) {\n if (!layer.content) continue\n\n const layerMaxChars = layer.maxTokens * CHARS_PER_TOKEN\n const trimmed = layer.content.length > layerMaxChars\n ? layer.content.slice(0, layerMaxChars) + \"...\"\n : layer.content\n\n if (totalChars + trimmed.length > maxChars) {\n // Budget exhausted — add what fits or skip\n const remaining = maxChars - totalChars\n if (remaining > 100) {\n parts.push(trimmed.slice(0, remaining) + \"...\")\n }\n break\n }\n\n parts.push(trimmed)\n totalChars += trimmed.length\n }\n\n return parts.join(\"\\n\\n\")\n}\n\n/**\n * Build individual context layers from input.\n */\nfunction buildLayers(input: ContextInput, config: ContextConfig): ContextLayer[] {\n const budget = (name: string, fallback: number) =>\n config.layerBudgets?.[name] ?? fallback\n\n const layers: ContextLayer[] = []\n\n // 1. Channel layer\n layers.push(buildChannelLayer(input, budget(\"channel\", 200)))\n\n // 2. Scope layer\n layers.push(buildScopeLayer(input, budget(\"scope\", 200)))\n\n // 3. Identity (only first line of systemPrompt — agent already has CLAUDE.md)\n if (input.systemPrompt) {\n layers.push({\n name: \"identity\",\n priority: 3,\n maxTokens: budget(\"identity\", 300),\n content: input.systemPrompt.split(\"\\n\")[0],\n tags: [\"identity\", input.agentId],\n })\n }\n\n // 4. Peers (only on channels that support delegation)\n if (input.peers?.length && supportsDelgation(input.channel)) {\n layers.push(buildPeersLayer(input, budget(\"peers\", 400)))\n }\n\n // 5. Intent (extracted keywords from message)\n // Lightweight — just tag detection, not LLM-based\n const intentTags = extractIntentTags(input.message)\n if (intentTags.length) {\n layers.push({\n name: \"intent\",\n priority: 5,\n maxTokens: budget(\"intent\", 200),\n content: `[Intent: ${intentTags.join(\", \")}]`,\n tags: intentTags,\n })\n }\n\n // 6. Artifacts (media, reply-to, issue/MR context)\n const artifactLines: string[] = []\n if (input.replyToText) {\n artifactLines.push(`[Replying to]: ${input.replyToText.slice(0, 300)}`)\n }\n if (input.mediaPath) {\n artifactLines.push(`[Attached file: ${input.mediaPath}]`)\n artifactLines.push(`[File type: ${input.mediaType || \"unknown\"}]`)\n if (input.mediaType?.startsWith(\"image/\")) artifactLines.push(\"Please view this image and respond to it.\")\n else if (input.mediaType?.startsWith(\"audio/\")) artifactLines.push(\"Please transcribe this audio and respond.\")\n }\n if (input.issueMR) {\n artifactLines.push(`[${input.issueMR.type} #${input.issueMR.iid}: ${input.issueMR.title}]`)\n }\n if (artifactLines.length) {\n layers.push({\n name: \"artifacts\",\n priority: 6,\n maxTokens: budget(\"artifacts\", 500),\n content: artifactLines.join(\"\\n\"),\n tags: [\"artifacts\", ...(input.mediaType ? [\"media\"] : [])],\n })\n }\n\n // 7. History (group > session — prefer group if available)\n const history = input.groupHistory || input.sessionHistory\n if (history) {\n layers.push({\n name: \"history\",\n priority: 7,\n maxTokens: budget(\"history\", 1200),\n content: history,\n tags: [\"history\", \"conversation\"],\n })\n }\n\n // 8. Wiki knowledge\n if (input.wikiContext) {\n layers.push({\n name: \"wiki\",\n priority: 8,\n maxTokens: budget(\"wiki\", 1000),\n content: input.wikiContext,\n tags: [\"wiki\", \"knowledge\"],\n })\n }\n\n return layers\n}\n\n/**\n * Build channel-specific context with rules.\n */\nfunction buildChannelLayer(input: ContextInput, maxTokens: number): ContextLayer {\n const lines: string[] = [`Channel: ${input.channel}`]\n const rules: string[] = []\n const tags = [input.channel]\n\n switch (input.channel) {\n case \"telegram\":\n if (input.agentHandle) lines.push(`Your handle: ${input.agentHandle}`)\n lines.push(`From: ${input.sender}`)\n rules.push(\"Format responses using Telegram-compatible markdown\")\n rules.push(\"Keep responses concise for mobile reading\")\n break\n\n case \"whatsapp\":\n lines.push(`From: ${input.sender}`)\n rules.push(\"Keep responses concise — WhatsApp is mobile-first\")\n rules.push(\"No rich formatting — plain text only\")\n break\n\n case \"gitlab\":\n lines.push(`From: ${input.sender}`)\n rules.push(\"Reply as a GitLab comment with GitLab-flavored markdown\")\n rules.push(\"Do NOT mention Telegram handles (@noqta_*)\")\n rules.push(\"Do NOT delegate to other agents\")\n rules.push(\"Reference issues with #IID and merge requests with !IID\")\n rules.push(\"Be specific and actionable — this is a code review context\")\n tags.push(\"code-review\")\n break\n\n case \"discord\":\n lines.push(`From: ${input.sender}`)\n rules.push(\"Use Discord markdown for formatting\")\n break\n\n default:\n if (input.channel.startsWith(\"webhook:\")) {\n rules.push(\"This is an automated event — respond with actionable steps\")\n tags.push(\"webhook\", \"automated\")\n }\n }\n\n if (rules.length) {\n lines.push(\"\")\n lines.push(\"[Rules]\")\n lines.push(...rules.map(r => `- ${r}`))\n }\n\n return { name: \"channel\", priority: 1, maxTokens, content: lines.join(\"\\n\"), tags, rules }\n}\n\n/**\n * Build scope context (group/personal/project).\n */\nfunction buildScopeLayer(input: ContextInput, maxTokens: number): ContextLayer {\n const lines: string[] = []\n const tags: string[] = []\n\n if (input.channelScope === \"group\" && input.groupName) {\n lines.push(`Group: ${input.groupName}`)\n tags.push(\"group\", input.groupName)\n } else if (input.channelScope === \"project\" && input.projectPath) {\n lines.push(`Project: ${input.projectPath}`)\n tags.push(\"project\", input.projectPath)\n } else if (input.channelScope === \"personal\") {\n lines.push(\"Direct message\")\n tags.push(\"dm\")\n }\n\n return { name: \"scope\", priority: 2, maxTokens, content: lines.join(\"\\n\"), tags }\n}\n\n/**\n * Build peers layer (only for channels that support bot-to-bot).\n */\nfunction buildPeersLayer(input: ContextInput, maxTokens: number): ContextLayer {\n const lines = [\"[Team — mention to delegate]\"]\n for (const peer of input.peers || []) {\n const handle = peer.handle ? ` (${peer.handle})` : \"\"\n const role = peer.role ? ` — ${peer.role}` : \"\"\n lines.push(`• ${peer.name}${handle}${role}`)\n }\n lines.push(\"Mention their handle to involve them.\")\n return { name: \"peers\", priority: 4, maxTokens, content: lines.join(\"\\n\"), tags: [\"peers\", \"team\"] }\n}\n\n/**\n * Extract intent tags from message (lightweight, no LLM).\n */\nfunction extractIntentTags(message: string): string[] {\n const tags: string[] = []\n const lower = message.toLowerCase()\n\n // Action intents\n if (/deploy|push|release|ship/.test(lower)) tags.push(\"deployment\")\n if (/review|check|look at|approve/.test(lower)) tags.push(\"review\")\n if (/fix|bug|broken|error|issue/.test(lower)) tags.push(\"bugfix\")\n if (/create|add|build|implement/.test(lower)) tags.push(\"feature\")\n if (/test|spec|coverage/.test(lower)) tags.push(\"testing\")\n if (/refactor|clean|improve/.test(lower)) tags.push(\"refactor\")\n if (/docs|document|readme/.test(lower)) tags.push(\"docs\")\n if (/security|vuln|auth|token/.test(lower)) tags.push(\"security\")\n if (/perf|slow|optim|fast/.test(lower)) tags.push(\"performance\")\n if (/status|update|progress|standup/.test(lower)) tags.push(\"status\")\n if (/help|how|what|explain/.test(lower)) tags.push(\"question\")\n\n // Domain intents\n if (/gitlab|merge|mr|issue|pipeline/.test(lower)) tags.push(\"gitlab\")\n if (/seo|analytics|content|marketing/.test(lower)) tags.push(\"marketing\")\n if (/infra|server|docker|k8s|devops/.test(lower)) tags.push(\"devops\")\n\n return tags\n}\n\n/**\n * Channels that support agent delegation (bot-to-bot mentions).\n */\nfunction supportsDelgation(channel: string): boolean {\n return channel === \"telegram\"\n}\n\n/**\n * Estimate token count from text.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN)\n}\n","import type { DaemonConfig, AgentDef } from \"@/daemon/config\"\nimport { executeTask, type AgentTask, type AgentResponse, type StreamCallback, type AgentPeer } from \"./runtime\"\nimport { SessionStore } from \"./sessions\"\nimport { WikiStore } from \"@/wiki\"\nimport { RateLimiter } from \"@/daemon/rate-limit\"\nimport { TokenTracker } from \"@/daemon/token-tracker\"\nimport { buildAgentContext, type ContextInput } from \"./context\"\n\n// --- Agent Registry: lifecycle management + concurrency control ---\n\ninterface AgentState {\n id: string\n def: AgentDef\n activeTasks: number\n totalTasks: number\n lastActive?: Date\n errors: number\n}\n\nexport class AgentRegistry {\n private agents: Map<string, AgentState> = new Map()\n private config: DaemonConfig\n private providers: Record<string, { apiKey?: string }> = {}\n private sessions: SessionStore\n private wiki: WikiStore\n private rateLimiter: RateLimiter\n private tokenTracker: TokenTracker\n private log: (...args: unknown[]) => void\n\n constructor(\n config: DaemonConfig,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[agents]\"),\n ) {\n this.log = log\n this.config = config\n this.providers = config.providers\n this.sessions = new SessionStore()\n this.wiki = new WikiStore()\n this.rateLimiter = new RateLimiter()\n this.tokenTracker = new TokenTracker()\n\n for (const [id, def] of Object.entries(config.agents)) {\n this.agents.set(id, {\n id,\n def,\n activeTasks: 0,\n totalTasks: 0,\n errors: 0,\n })\n }\n }\n\n /**\n * Get agent definition by ID.\n */\n getAgent(id: string): AgentDef | undefined {\n return this.agents.get(id)?.def\n }\n\n /**\n * Find agent by mention pattern (e.g., \"@nadia\" -> \"marketing-agent\").\n * Returns the agent with the longest (most specific) mention match.\n */\n findByMention(text: string): string | undefined {\n const lower = text.toLowerCase()\n let bestId: string | undefined\n let bestLen = 0\n\n for (const [id, state] of this.agents) {\n for (const mention of state.def.mentions) {\n const mentionLower = mention.toLowerCase()\n if (lower.includes(mentionLower) && mentionLower.length > bestLen) {\n bestId = id\n bestLen = mentionLower.length\n }\n }\n }\n\n return bestId\n }\n\n /**\n * Find ALL agents mentioned in text (for bot-to-bot detection).\n */\n findAllMentioned(text: string): string[] {\n const lower = text.toLowerCase()\n const found: string[] = []\n\n for (const [id, state] of this.agents) {\n for (const mention of state.def.mentions) {\n if (lower.includes(mention.toLowerCase())) {\n found.push(id)\n break\n }\n }\n }\n\n return found\n }\n\n /**\n * Build peer list for context engine.\n */\n private buildPeerList(agentId: string, channel?: string): Array<{ name: string; handle?: string; role?: string }> {\n const peers: Array<{ name: string; handle?: string; role?: string }> = []\n for (const [id, state] of this.agents) {\n if (id === agentId) continue\n peers.push({\n name: state.def.name,\n handle: this.getChannelHandle(id, channel),\n role: state.def.systemPrompt?.split(\"\\n\")[0]?.slice(0, 80),\n })\n }\n return peers\n }\n\n /**\n * Get the primary channel handle for an agent (e.g. \"@noqta_devops_bot\" on telegram).\n */\n private getChannelHandle(agentId: string, channel?: string): string | undefined {\n const agent = this.agents.get(agentId)?.def\n if (!agent) return undefined\n\n // For telegram, find the mention that starts with @ (bot username)\n if (channel === \"telegram\") {\n return agent.mentions.find((m) => m.startsWith(\"@\"))\n }\n\n // Fallback: first mention\n return agent.mentions[0]\n }\n\n /**\n * Execute a task on an agent. Respects maxConcurrent limit.\n */\n async execute(task: AgentTask, onDelta?: StreamCallback): Promise<AgentResponse> {\n const state = this.agents.get(task.agentId)\n if (!state) {\n return { content: \"\", error: `Unknown agent: ${task.agentId}` }\n }\n\n if (state.activeTasks >= state.def.maxConcurrent) {\n return {\n content: \"\",\n error: `Agent \"${task.agentId}\" is busy (${state.activeTasks}/${state.def.maxConcurrent} tasks)`,\n }\n }\n\n // Rate limit check\n const rateCheck = this.rateLimiter.check(task.agentId)\n if (!rateCheck.allowed) {\n this.log(`[${task.agentId}] ${rateCheck.reason}`)\n return { content: \"\", error: rateCheck.reason }\n }\n\n state.activeTasks++\n state.totalTasks++\n state.lastActive = new Date()\n\n this.log(`[${task.agentId}] executing task (${state.activeTasks}/${state.def.maxConcurrent})`)\n\n // Build conversation history for session continuity\n const channel = task.context?.channel || \"api\"\n const chatId = task.context?.group || task.context?.sender || \"default\"\n const senderName = task.context?.sender || \"User\"\n\n // Record user message in session\n this.sessions.addUserMessage(task.agentId, channel, chatId, senderName, task.message)\n\n // Build structured context using the context engine\n const wikiArticles = this.wiki.findRelevant(task.message, task.agentId, 3)\n const wikiContext = this.wiki.buildContext(wikiArticles)\n\n const resumeSessionId = state.def.tier === \"claude-code\"\n ? this.sessions.getClaudeSessionId(task.agentId, channel, chatId)\n : undefined\n\n const sessionHistory = !resumeSessionId\n ? this.sessions.buildHistoryContext(task.agentId, channel, chatId)\n : undefined\n\n // Resolve peers for the context engine\n const peers = this.buildPeerList(task.agentId, channel)\n\n const contextInput: ContextInput = {\n channel,\n channelScope: task.context?.group ? \"group\" : (channel === \"gitlab\" ? \"project\" : \"personal\"),\n groupName: task.context?.group,\n agentId: task.agentId,\n agentName: state.def.name,\n agentHandle: this.getChannelHandle(task.agentId, channel),\n systemPrompt: state.def.systemPrompt,\n sender: senderName,\n peers,\n mediaPath: task.context?.mediaPath,\n mediaType: task.context?.mediaType,\n replyToText: task.context?.replyToText,\n groupHistory: task.context?.group ? undefined : undefined, // group log is injected by router\n sessionHistory,\n wikiContext,\n message: task.message,\n }\n\n const historyContext = buildAgentContext(contextInput)\n\n try {\n const response = await executeTask(state.def, task, this.providers, onDelta, historyContext, resumeSessionId)\n\n if (response.error) {\n state.errors++\n this.log(`[${task.agentId}] error: ${response.error}`)\n } else {\n // Record agent response in session\n this.sessions.addAgentMessage(task.agentId, channel, chatId, response.content)\n\n // Store Claude session ID for future --resume\n if (response.claudeSessionId) {\n this.sessions.setClaudeSessionId(task.agentId, channel, chatId, response.claudeSessionId)\n }\n\n // Wiki: export conversation as raw entry for later absorption\n if (response.content.length > 50) {\n try {\n const entryId = `${task.agentId}-${Date.now().toString(36)}`\n this.wiki.addEntry({\n id: entryId,\n date: new Date().toISOString().slice(0, 10),\n agentId: task.agentId,\n source: channel,\n sourceContext: task.context?.group || task.context?.sender,\n content: `User: ${task.message}\\n\\nAgent: ${response.content}`,\n })\n } catch {\n // Wiki export is best-effort\n }\n }\n\n // Track token usage\n this.tokenTracker.record(\n task.agentId,\n task.message.length,\n response.content.length,\n response.duration || 0,\n )\n\n this.log(\n `[${task.agentId}] completed in ${response.duration}ms` +\n (response.tokensUsed ? ` (${response.tokensUsed} tokens)` : \"\"),\n )\n }\n\n return response\n } catch (error: any) {\n state.errors++\n this.log(`[${task.agentId}] unexpected error: ${error.message}`)\n return { content: \"\", error: error.message }\n } finally {\n state.activeTasks--\n }\n }\n\n /**\n * List all agents and their status.\n */\n list(): Array<{\n id: string\n name: string\n tier: string\n workspace: string\n active: number\n total: number\n errors: number\n lastActive?: Date\n }> {\n return Array.from(this.agents.values()).map((s) => ({\n id: s.id,\n name: s.def.name,\n tier: s.def.tier,\n workspace: s.def.workspace,\n active: s.activeTasks,\n total: s.totalTasks,\n errors: s.errors,\n lastActive: s.lastActive,\n }))\n }\n\n /**\n * Get token usage summary.\n */\n getUsage(days: number = 7) {\n return this.tokenTracker.summary(days)\n }\n\n /**\n * Get today's usage.\n */\n getTodayUsage() {\n return this.tokenTracker.today()\n }\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Persistent group conversation log ---\n// Stores recent messages per group chat in .agentx/groups/{chatId}.jsonl\n// Survives daemon restarts. Trimmed to max entries on write.\n\nexport interface GroupLogEntry {\n sender: string\n text: string\n timestamp: number\n}\n\nconst MAX_ENTRIES = 30\nconst MAX_CONTEXT_CHARS = 6000\n\nexport class GroupLog {\n private dir: string\n private cache: Map<string, GroupLogEntry[]> = new Map()\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/groups\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n }\n\n /**\n * Log a message in a group conversation.\n */\n add(chatId: string, sender: string, text: string): void {\n const log = this.load(chatId)\n log.push({ sender, text: text.slice(0, 500), timestamp: Date.now() })\n\n // Trim\n while (log.length > MAX_ENTRIES) log.shift()\n\n this.cache.set(chatId, log)\n this.save(chatId, log)\n }\n\n /**\n * Build conversation context string for an agent.\n * Returns recent messages so the agent knows what was discussed.\n */\n buildContext(chatId: string): string {\n const log = this.load(chatId)\n if (log.length <= 1) return \"\"\n\n const lines: string[] = [\"[Recent group conversation]\"]\n let chars = 0\n\n // Walk backwards from most recent, skip the very last (it's the current message)\n for (let i = log.length - 2; i >= 0; i--) {\n const entry = log[i]\n const time = new Date(entry.timestamp).toLocaleTimeString(\"en-US\", { hour: \"2-digit\", minute: \"2-digit\", hour12: false })\n const line = `[${time}] ${entry.sender}: ${entry.text}`\n if (chars + line.length > MAX_CONTEXT_CHARS) break\n lines.splice(1, 0, line)\n chars += line.length\n }\n\n if (lines.length <= 1) return \"\"\n lines.push(\"[End of conversation — respond to the latest message]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Get raw log entries for a group.\n */\n getEntries(chatId: string): GroupLogEntry[] {\n return [...this.load(chatId)]\n }\n\n // --- Persistence ---\n\n private filePath(chatId: string): string {\n const safe = chatId.replace(/[^a-zA-Z0-9_-]/g, \"_\")\n return resolve(this.dir, `${safe}.json`)\n }\n\n private load(chatId: string): GroupLogEntry[] {\n // Check cache first\n if (this.cache.has(chatId)) return this.cache.get(chatId)!\n\n // Load from disk\n const path = this.filePath(chatId)\n if (!existsSync(path)) {\n this.cache.set(chatId, [])\n return []\n }\n\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\"))\n const entries = Array.isArray(data) ? data : []\n this.cache.set(chatId, entries)\n return entries\n } catch {\n this.cache.set(chatId, [])\n return []\n }\n }\n\n private save(chatId: string, entries: GroupLogEntry[]): void {\n try {\n writeFileSync(this.filePath(chatId), JSON.stringify(entries))\n } catch {\n // Best-effort persistence\n }\n }\n}\n","import type { DaemonConfig } from \"@/daemon/config\"\nimport type { AgentRegistry } from \"@/agents/registry\"\nimport type { A2AMesh } from \"@/a2a/mesh\"\nimport type { ChannelAdapter, IncomingMessage } from \"./types\"\nimport type { TelegramAdapter } from \"./telegram\"\nimport type { HookRegistry } from \"@/hooks\"\nimport { GroupLog } from \"./group-log\"\n\n// --- Message Router ---\n// Routes channel messages to agents. Supports:\n// - Typing indicator while processing\n// - Streaming response edits\n// - Seen reaction (👀) on mention\n// - Bot-to-bot: if response mentions another agent, route it\n// - Correct bot account sends the reply (not always the first one)\n\nconst STREAM_EDIT_INTERVAL_MS = 1500\nconst TYPING_INTERVAL_MS = 4000\n\nexport class MessageRouter {\n private registry: AgentRegistry\n private config: DaemonConfig\n private channels: Map<string, ChannelAdapter> = new Map()\n private hooks?: HookRegistry\n private mesh?: A2AMesh\n private groupLog: GroupLog\n private log: (...args: unknown[]) => void\n\n constructor(\n registry: AgentRegistry,\n config: DaemonConfig,\n hooks?: HookRegistry,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[router]\"),\n ) {\n this.registry = registry\n this.config = config\n this.hooks = hooks\n this.log = log\n this.groupLog = new GroupLog()\n }\n\n setMesh(mesh: A2AMesh): void {\n this.mesh = mesh\n }\n\n addChannel(adapter: ChannelAdapter): void {\n this.channels.set(adapter.name, adapter)\n adapter.onMessage((msg) => this.handleMessage(adapter, msg))\n }\n\n async startAll(): Promise<void> {\n for (const [name, adapter] of this.channels) {\n this.log(`Starting channel: ${name}`)\n await adapter.start()\n }\n }\n\n async stopAll(): Promise<void> {\n for (const [name, adapter] of this.channels) {\n this.log(`Stopping channel: ${name}`)\n await adapter.stop()\n }\n }\n\n private async handleMessage(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n ): Promise<void> {\n // Pre-hook\n if (this.hooks?.has(\"pre:channel-message\" as any)) {\n const hookResult = await this.hooks.execute(\"pre:channel-message\" as any, {\n event: \"pre:channel-message\" as any,\n channel: msg.channel,\n sender: msg.sender.name,\n text: msg.text,\n group: msg.group?.name,\n })\n\n if (hookResult.blocked) {\n this.log(`Message blocked by hook: ${hookResult.message}`)\n return\n }\n\n if (hookResult.modified?.text) {\n msg = { ...msg, text: hookResult.modified.text as string }\n }\n }\n\n // Log ALL group messages for conversation context (before agent resolution)\n if (msg.group) {\n const chatId = msg.group.id\n this.groupLog.add(chatId, msg.sender.name, msg.text)\n }\n\n // Resolve agent — check local first, then mesh peers\n const agentId = this.resolveAgent(msg)\n\n if (!agentId) {\n return\n }\n\n // Dedup: in groups, multiple bot accounts receive the same message.\n // Only the account BOUND to this agent should handle it.\n if (msg.group && msg.channel === \"telegram\") {\n const boundAccount = this.getAccountForAgent(agentId)\n if (boundAccount && boundAccount !== msg.accountId) {\n return\n }\n }\n\n const chatId = msg.group?.id || msg.sender.id\n const agentDef = this.registry.getAgent(agentId)\n const agentName = agentDef?.name || agentId\n\n // Determine which bot account should send the response\n const replyAccountId = this.getAccountForAgent(agentId) || msg.accountId\n\n this.log(\n `Routing [${msg.channel}/${msg.sender.name}] -> \"${agentName}\": ${msg.text.slice(0, 80)}`,\n )\n\n // React with 👀 to acknowledge\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n\n // Start typing indicator loop (from the correct bot)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n // Streaming setup\n const canStream = typeof adapter.editMessage === \"function\"\n let sentMessageId: string | undefined\n let lastEditTime = 0\n\n const onDelta = canStream\n ? async (_delta: string, fullText: string) => {\n const now = Date.now()\n if (now - lastEditTime < STREAM_EDIT_INTERVAL_MS) return\n\n if (!sentMessageId) {\n const preview = fullText.length > 20\n ? fullText\n : `_${agentName} is writing..._\\n\\n${fullText}`\n try {\n sentMessageId = await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: preview,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n lastEditTime = now\n } catch { /* retry next delta */ }\n } else {\n try {\n await this.adapterEdit(adapter, chatId, sentMessageId, fullText, undefined, replyAccountId)\n lastEditTime = now\n } catch { /* retry next delta */ }\n }\n }\n : undefined\n\n // Build group conversation context (recent messages from the group)\n const groupContext = msg.group ? this.groupLog.buildContext(chatId) : \"\"\n const messageWithContext = groupContext\n ? `${groupContext}\\n\\n${msg.sender.name}: ${msg.text}`\n : msg.text\n\n // Execute agent task\n const response = await this.registry.execute(\n {\n message: messageWithContext,\n agentId,\n context: {\n channel: msg.channel,\n sender: msg.sender.name,\n group: msg.group?.name,\n mediaPath: msg.media?.path,\n mediaType: msg.media?.type,\n replyToText: msg.replyToText,\n },\n },\n onDelta,\n )\n\n clearInterval(typingTimer)\n\n if (response.error) {\n this.log(`Agent error: ${response.error}`)\n const errorText = `Error: ${response.error}`\n if (sentMessageId) {\n await this.adapterEdit(adapter, chatId, sentMessageId, errorText, \"plain\", replyAccountId)\n } else {\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: errorText,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n }\n return\n }\n\n // Post-hook\n let responseText = response.content\n if (this.hooks?.has(\"post:channel-message\" as any)) {\n const hookResult = await this.hooks.execute(\"post:channel-message\" as any, {\n event: \"post:channel-message\" as any,\n channel: msg.channel,\n sender: msg.sender.name,\n response: responseText,\n agentId,\n })\n\n if (hookResult.blocked) {\n this.log(`Response blocked by hook: ${hookResult.message}`)\n return\n }\n\n if (hookResult.modified?.response) {\n responseText = hookResult.modified.response as string\n }\n }\n\n // Final message\n let sentResponseId: string | undefined\n if (responseText) {\n if (sentMessageId) {\n await this.adapterEdit(adapter, chatId, sentMessageId, responseText, undefined, replyAccountId)\n sentResponseId = sentMessageId\n } else {\n sentResponseId = await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: responseText,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n }\n }\n\n // Log bot response in group conversation\n if (msg.group && responseText) {\n this.groupLog.add(chatId, agentName, responseText)\n }\n\n // Bot-to-bot: only for Telegram (not GitLab/WhatsApp/Discord — would cause loops)\n if (responseText && sentResponseId && msg.channel === \"telegram\") {\n this.handleBotToBotChain(adapter, msg, agentId, responseText, sentResponseId, 0).catch((e) => {\n this.log(`Bot-to-bot error: ${e.message}`)\n })\n }\n }\n\n private static readonly MAX_BOT_CHAIN_DEPTH = 3\n\n /**\n * Bot-to-bot conversation chain.\n * Guards:\n * 1. Max depth (default 3)\n * 2. No agent called twice in the same chain (prevents A→B→A→B loops)\n * 3. Only on channels that support it (Telegram)\n */\n private async handleBotToBotChain(\n adapter: ChannelAdapter,\n originalMsg: IncomingMessage,\n sourceAgentId: string,\n responseText: string,\n responseMessageId: string,\n depth: number,\n visited: Set<string> = new Set(),\n ): Promise<void> {\n if (depth >= MessageRouter.MAX_BOT_CHAIN_DEPTH) {\n this.log(`Bot-to-bot: max depth (${depth}) reached, stopping`)\n return\n }\n\n // Track who's been in this chain\n visited.add(sourceAgentId)\n\n for (const [id, def] of Object.entries(this.config.agents)) {\n if (id === sourceAgentId) continue\n\n // Stop if this agent was already in the chain (prevents A→B→A loop)\n if (visited.has(id)) {\n this.log(`Bot-to-bot: \"${id}\" already participated, stopping chain`)\n continue\n }\n\n const mentioned = def.mentions.some((m: string) =>\n responseText.toLowerCase().includes(m.toLowerCase()),\n )\n if (!mentioned) continue\n\n this.log(`Bot-to-bot [${depth + 1}]: \"${sourceAgentId}\" -> \"${id}\"`)\n\n const chatId = originalMsg.group?.id || originalMsg.sender.id\n const targetAccountId = this.getAccountForAgent(id)\n const sourceAccountId = this.getAccountForAgent(sourceAgentId)\n\n try {\n // Target bot reacts 👀 to the source bot's message\n this.adapterReact(adapter, chatId, responseMessageId, \"👀\", targetAccountId)\n\n // Target bot shows typing\n const typingTimer = this.startTypingLoop(adapter, chatId, targetAccountId)\n\n // Include original user message as context so target bot knows the full picture\n const contextMessage = depth === 0\n ? `[Original from ${originalMsg.sender.name}]: ${originalMsg.text}\\n\\n[${sourceAgentId} said]: ${responseText}`\n : responseText\n\n const response = await this.registry.execute({\n message: contextMessage,\n agentId: id,\n context: {\n channel: originalMsg.channel,\n sender: `agent:${sourceAgentId}`,\n group: originalMsg.group?.name,\n },\n })\n\n clearInterval(typingTimer)\n\n if (response.content && !response.error) {\n const sentId = await this.adapterSend(adapter, {\n channel: originalMsg.channel,\n chatId,\n text: response.content,\n accountId: targetAccountId,\n })\n\n // Chain: check if this response also mentions another agent\n if (sentId && response.content) {\n await this.handleBotToBotChain(\n adapter, originalMsg, id, response.content, sentId as string, depth + 1, visited,\n )\n }\n } else if (response.error) {\n this.log(`Bot-to-bot \"${id}\" error: ${response.error}`)\n }\n } catch (e: any) {\n this.log(`Bot-to-bot \"${id}\" failed: ${e.message}`)\n }\n\n break // Route to first mentioned agent per level\n }\n }\n\n // --- Adapter helpers that pass accountId for Telegram ---\n\n private async adapterSend(\n adapter: ChannelAdapter,\n msg: { channel: string; chatId: string; text: string; replyTo?: string; parseMode?: string; accountId?: string },\n ): Promise<string> {\n // For Telegram, pass accountId so the correct bot sends the message\n if (adapter.name === \"telegram\" && msg.accountId) {\n return (adapter as TelegramAdapter).send({\n ...msg,\n parseMode: msg.parseMode as any,\n accountId: msg.accountId,\n }) as Promise<string>\n }\n return (adapter.send(msg as any) || \"\") as Promise<string>\n }\n\n private async adapterEdit(\n adapter: ChannelAdapter,\n chatId: string,\n messageId: string,\n text: string,\n parseMode?: string,\n accountId?: string,\n ): Promise<boolean> {\n if (adapter.name === \"telegram\" && accountId) {\n return (adapter as TelegramAdapter).editMessage(chatId, messageId, text, parseMode, accountId)\n }\n return adapter.editMessage?.(chatId, messageId, text, parseMode) ?? false\n }\n\n private adapterReact(\n adapter: ChannelAdapter,\n chatId: string,\n messageId: string,\n emoji: string,\n accountId?: string,\n ): void {\n if (adapter.name === \"telegram\" && accountId) {\n (adapter as TelegramAdapter).react(chatId, messageId, emoji, accountId)\n } else {\n adapter.react?.(chatId, messageId, emoji)\n }\n }\n\n private startTypingLoop(\n adapter: ChannelAdapter,\n chatId: string,\n accountId?: string,\n ): ReturnType<typeof setInterval> {\n const sendTyping = () => {\n if (adapter.name === \"telegram\" && accountId) {\n (adapter as TelegramAdapter).sendTyping(chatId, accountId)\n } else {\n adapter.sendTyping?.(chatId)\n }\n }\n\n sendTyping()\n return setInterval(sendTyping, TYPING_INTERVAL_MS)\n }\n\n // --- Agent resolution ---\n\n /**\n * Handle a message by routing to a mesh peer's agent.\n * Searches peer agent cards for mention matches.\n */\n private async handleViaMesh(\n adapter: ChannelAdapter,\n msg: IncomingMessage,\n ): Promise<boolean> {\n if (!this.mesh) return false\n\n const textLower = msg.text.toLowerCase()\n const directory = this.mesh.directory()\n\n for (const peer of directory) {\n if (!peer.healthy) continue\n\n for (const skill of peer.skills) {\n // Check if the message mentions this remote agent by name or ID\n if (\n textLower.includes(skill.id.toLowerCase()) ||\n textLower.includes(skill.name.toLowerCase())\n ) {\n this.log(`Mesh routing [${msg.channel}/${msg.sender.name}] -> peer \"${peer.peer}\" agent \"${skill.id}\"`)\n\n const chatId = msg.group?.id || msg.sender.id\n const replyAccountId = msg.accountId\n\n // React + typing\n this.adapterReact(adapter, chatId, msg.id, \"👀\", replyAccountId)\n const typingTimer = this.startTypingLoop(adapter, chatId, replyAccountId)\n\n try {\n const response = await this.mesh.sendTask(peer.peer, msg.text, skill.id)\n\n clearInterval(typingTimer)\n\n if (response) {\n // Prefix with remote agent name so user knows who's responding\n const header = `**${skill.name}** _(${peer.peer})_:\\n\\n`\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: header + response,\n replyTo: msg.id,\n accountId: replyAccountId,\n })\n }\n\n return true\n } catch (e: any) {\n clearInterval(typingTimer)\n this.log(`Mesh routing error: ${e.message}`)\n\n await this.adapterSend(adapter, {\n channel: msg.channel,\n chatId,\n text: `Error from ${peer.peer}/${skill.name}: ${e.message}`,\n replyTo: msg.id,\n parseMode: \"plain\",\n accountId: replyAccountId,\n })\n return true\n }\n }\n }\n }\n\n return false\n }\n\n private getAccountForAgent(agentId: string): string | undefined {\n for (const [accountId, account] of Object.entries(this.config.channels.telegram.accounts)) {\n if (account.agentBinding === agentId) {\n return accountId\n }\n }\n return undefined\n }\n\n private resolveAgent(msg: IncomingMessage): string | undefined {\n // Pre-resolved by channel adapter (WhatsApp route-based, etc.)\n if (msg.resolvedAgent) {\n return msg.resolvedAgent\n }\n\n // DM: route to the account's bound agent\n if (!msg.group) {\n if (msg.channel === \"telegram\") {\n const account = this.config.channels.telegram.accounts[msg.accountId]\n return account?.agentBinding\n }\n if (msg.channel === \"whatsapp\") {\n return this.config.channels.whatsapp.defaultAgent\n }\n return undefined\n }\n\n // Group: check policy\n if (msg.channel === \"telegram\") {\n const policy = this.config.channels.telegram.policy\n if (policy.group === \"mention-required\") {\n const agentId = this.registry.findByMention(msg.text)\n if (!agentId) return undefined\n return agentId\n }\n }\n\n // Default: mention matching, then account binding\n const mentionAgent = this.registry.findByMention(msg.text)\n if (mentionAgent) return mentionAgent\n\n if (msg.channel === \"telegram\") {\n const account = this.config.channels.telegram.accounts[msg.accountId]\n return account?.agentBinding\n }\n\n return this.config.channels.whatsapp.defaultAgent\n }\n}\n","// --- Convert standard Markdown to Telegram HTML ---\n//\n// Uses parse_mode: \"HTML\" (not MarkdownV2) — much simpler and more reliable.\n// Based on OpenClaw's approach: markdown → HTML tags.\n//\n// Supported Telegram HTML tags:\n// <b>bold</b>, <i>italic</i>, <s>strikethrough</s>,\n// <code>inline code</code>, <pre><code>code block</code></pre>,\n// <a href=\"url\">link</a>, <blockquote>quote</blockquote>,\n// <tg-spoiler>spoiler</tg-spoiler>\n//\n// Tables are converted to bullet-point format (Telegram has no table support).\n// File extensions that look like TLDs are wrapped in <code> to prevent\n// Telegram generating spurious link previews.\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n}\n\n/**\n * Convert standard Markdown (Claude output) to Telegram HTML.\n */\nexport function markdownToTelegramHtml(md: string): string {\n const lines = md.split(\"\\n\")\n const result: string[] = []\n let inCodeBlock = false\n let codeBlockLang = \"\"\n let codeBlockLines: string[] = []\n let inBlockquote = false\n let tableHeaders: string[] = []\n let inTable = false\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n\n // Code block toggle\n if (line.trimStart().startsWith(\"```\")) {\n if (!inCodeBlock) {\n inCodeBlock = true\n codeBlockLang = line.trimStart().slice(3).trim()\n codeBlockLines = []\n continue\n } else {\n inCodeBlock = false\n const code = escapeHtml(codeBlockLines.join(\"\\n\"))\n // Language hint as comment for context\n const langHint = codeBlockLang ? `// ${codeBlockLang}\\n` : \"\"\n result.push(`<pre><code>${langHint}${code}</code></pre>`)\n codeBlockLang = \"\"\n continue\n }\n }\n\n if (inCodeBlock) {\n codeBlockLines.push(line)\n continue\n }\n\n // Close blockquote if we left it\n if (inBlockquote && !line.trimStart().startsWith(\">\")) {\n result.push(\"</blockquote>\")\n inBlockquote = false\n }\n\n // Close table if we left it\n if (inTable && !line.trim().startsWith(\"|\")) {\n inTable = false\n tableHeaders = []\n }\n\n // Blockquote\n if (line.trimStart().startsWith(\"> \")) {\n const content = line.replace(/^>\\s*/, \"\")\n if (!inBlockquote) {\n result.push(\"<blockquote>\")\n inBlockquote = true\n }\n result.push(convertInline(content))\n continue\n }\n\n // Headers → bold with blank line before\n const headerMatch = line.match(/^(#{1,6})\\s+(.+)$/)\n if (headerMatch) {\n result.push(\"\")\n result.push(`<b>${convertInline(headerMatch[2])}</b>`)\n continue\n }\n\n // Horizontal rule\n if (/^[-*_]{3,}\\s*$/.test(line.trim())) {\n result.push(\"———\")\n continue\n }\n\n // Table handling — convert to readable bullet format\n if (line.trim().startsWith(\"|\") && line.trim().endsWith(\"|\")) {\n const cells = line.split(\"|\").slice(1, -1).map(c => c.trim())\n\n // Skip separator rows (|---|---|)\n if (/^\\|[\\s\\-:|]+\\|$/.test(line.trim())) {\n inTable = true\n continue\n }\n\n if (!inTable) {\n // This is the header row\n tableHeaders = cells\n inTable = true\n continue\n }\n\n // Data row — render as bullet with header labels\n if (tableHeaders.length > 0 && cells.length > 0) {\n if (cells.length >= 2) {\n // First cell as bold label, rest as key: value pairs\n const parts: string[] = []\n for (let c = 0; c < cells.length; c++) {\n if (c === 0) {\n parts.push(`<b>${convertInline(cells[c])}</b>`)\n } else {\n const header = tableHeaders[c] ? `${convertInline(tableHeaders[c])}: ` : \"\"\n parts.push(`${header}${convertInline(cells[c])}`)\n }\n }\n result.push(`• ${parts.join(\" — \")}`)\n } else {\n result.push(`• ${convertInline(cells[0])}`)\n }\n }\n continue\n }\n\n // Unordered list\n const ulMatch = line.match(/^(\\s*)[-*+]\\s+(.+)$/)\n if (ulMatch) {\n const indent = ulMatch[1].length > 0 ? \" \" : \"\"\n result.push(`${indent}• ${convertInline(ulMatch[2])}`)\n continue\n }\n\n // Ordered list\n const olMatch = line.match(/^(\\s*)\\d+[.)]\\s+(.+)$/)\n if (olMatch) {\n const indent = olMatch[1].length > 0 ? \" \" : \"\"\n const num = line.match(/^(\\s*)(\\d+)/)?.[2] || \"1\"\n result.push(`${indent}${num}. ${convertInline(olMatch[2])}`)\n continue\n }\n\n // Empty line\n if (!line.trim()) {\n result.push(\"\")\n continue\n }\n\n // Normal text\n result.push(convertInline(line))\n }\n\n // Close unclosed blocks\n if (inCodeBlock) {\n result.push(`<pre><code>${escapeHtml(codeBlockLines.join(\"\\n\"))}</code></pre>`)\n }\n if (inBlockquote) {\n result.push(\"</blockquote>\")\n }\n\n let html = result.join(\"\\n\").trim()\n\n // Wrap file extensions that look like TLDs to prevent Telegram link previews\n // e.g., \"config.ts\" would get wrapped as \"config<code>.ts</code>\"\n html = wrapFileRefsInCode(html)\n\n return html\n}\n\n/**\n * Convert inline markdown to HTML tags.\n */\nfunction convertInline(text: string): string {\n let result = escapeHtml(text)\n\n // Inline code (first, to protect content from further processing)\n result = result.replace(/`([^`]+)`/g, \"<code>$1</code>\")\n\n // Links: [text](url) — must come before bold/italic to preserve URLs\n result = result.replace(\n /\\[([^\\]]+)\\]\\(([^)]+)\\)/g,\n (_m, label, url) => `<a href=\"${escapeHtml(url)}\">${label}</a>`,\n )\n\n // Bold+italic: ***text***\n result = result.replace(/\\*\\*\\*(.+?)\\*\\*\\*/g, \"<b><i>$1</i></b>\")\n\n // Bold: **text**\n result = result.replace(/\\*\\*(.+?)\\*\\*/g, \"<b>$1</b>\")\n\n // Italic: *text* or _text_ (not inside other tags)\n result = result.replace(/(?<!\\*)\\*([^*]+?)\\*(?!\\*)/g, \"<i>$1</i>\")\n\n // Strikethrough: ~~text~~\n result = result.replace(/~~(.+?)~~/g, \"<s>$1</s>\")\n\n // Spoiler: ||text||\n result = result.replace(/\\|\\|(.+?)\\|\\|/g, \"<tg-spoiler>$1</tg-spoiler>\")\n\n return result\n}\n\n/**\n * Wrap file extensions that share TLDs in <code> to prevent Telegram\n * from generating spurious link previews (e.g., config.ts, main.py).\n * Skips content already inside <code>, <pre>, or <a> tags.\n */\nconst FILE_EXT_TLDS = /(?<=\\w)\\.(ts|js|py|rs|go|rb|cs|sh|md|yml|yaml|toml|json|env|css|html|xml|sql|tf|hcl)(?=[\\s,;:)\\]}<]|$)/gi\n\nfunction wrapFileRefsInCode(html: string): string {\n // Split on existing tags to avoid modifying content inside them\n const parts = html.split(/(<\\/?(?:code|pre|a)[^>]*>)/gi)\n let insideTag = false\n\n return parts.map((part) => {\n if (/<(?:code|pre|a)\\b/i.test(part)) {\n insideTag = true\n return part\n }\n if (/<\\/(?:code|pre|a)>/i.test(part)) {\n insideTag = false\n return part\n }\n if (insideTag) return part\n\n return part.replace(FILE_EXT_TLDS, \"<code>.$1</code>\")\n }).join(\"\")\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\nimport { markdownToTelegramHtml } from \"./telegram-format\"\n\n// --- Telegram Bot API adapter (long-polling, no dependencies) ---\n\ninterface TelegramUpdate {\n update_id: number\n message?: {\n message_id: number\n from: { id: number; first_name: string; last_name?: string; username?: string }\n chat: { id: number; type: string; title?: string }\n text?: string\n caption?: string\n date: number\n reply_to_message?: { message_id: number; text?: string; caption?: string; from?: { first_name: string } }\n photo?: Array<{ file_id: string; file_unique_id: string; width: number; height: number; file_size?: number }>\n voice?: { file_id: string; duration: number; mime_type?: string }\n audio?: { file_id: string; duration: number; mime_type?: string; title?: string }\n video?: { file_id: string; duration: number; mime_type?: string }\n document?: { file_id: string; file_name?: string; mime_type?: string }\n sticker?: { file_id: string; emoji?: string }\n }\n}\n\ninterface TelegramAccountConfig {\n token: string\n agentBinding: string\n}\n\nexport class TelegramAdapter implements ChannelAdapter {\n readonly name = \"telegram\"\n private accounts: Map<string, TelegramAccountConfig>\n private offsets: Map<string, number> = new Map()\n private handler?: (msg: IncomingMessage) => Promise<void>\n private polling = false\n private log: (...args: unknown[]) => void\n\n constructor(\n accounts: Record<string, TelegramAccountConfig>,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[telegram]\"),\n ) {\n this.accounts = new Map(Object.entries(accounts))\n this.log = log\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n this.polling = true\n\n const entries = Array.from(this.accounts.entries())\n this.log(`${entries.length} Telegram account(s) to start`)\n\n for (let i = 0; i < entries.length; i++) {\n const [accountId, config] = entries[i]\n this.log(`Starting polling for account \"${accountId}\" (${i + 1}/${entries.length})`)\n try {\n const me = await this.apiCall(config.token, \"getMe\")\n this.log(`Bot @${me.result?.username} ready (account: ${accountId})`)\n this.pollLoop(accountId, config)\n } catch (e: any) {\n this.log(`Failed to verify bot for account \"${accountId}\": ${e.message}`)\n }\n // Small delay between account starts to avoid Telegram rate limits\n if (i < entries.length - 1) {\n await new Promise((r) => setTimeout(r, 300))\n }\n }\n\n this.log(`All ${entries.length} Telegram account(s) started`)\n }\n\n async stop(): Promise<void> {\n this.polling = false\n }\n\n /**\n * Get token for a specific account ID. Used by router to send from correct bot.\n */\n getTokenForAccount(accountId: string): string | undefined {\n return this.accounts.get(accountId)?.token\n }\n\n /**\n * Get the default (first) account token as fallback.\n */\n private getDefaultToken(): string | undefined {\n const [, config] = Array.from(this.accounts.entries())[0]\n return config?.token\n }\n\n /**\n * Resolve which token to use: prefer accountId, fall back to chatAccountMap, then default.\n */\n private resolveToken(chatId: string, accountId?: string): string | undefined {\n if (accountId) {\n const token = this.getTokenForAccount(accountId)\n if (token) return token\n }\n return this.chatAccountMap.get(chatId)\n ? this.getTokenForAccount(this.chatAccountMap.get(chatId)!)\n : this.getDefaultToken()\n }\n\n /** Track which account a chat was last seen on (for DMs) */\n private chatAccountMap: Map<string, string> = new Map()\n\n /**\n * Send a message. Returns the sent message ID.\n * Pass accountId to send from a specific bot account.\n */\n async send(msg: OutgoingMessage & { accountId?: string }): Promise<string> {\n const token = this.resolveToken(msg.chatId, msg.accountId)\n if (!token) {\n this.log(\"No telegram token found for sending\")\n return \"\"\n }\n\n const maxLen = 4096\n const text = msg.text.length > maxLen ? msg.text.slice(0, maxLen - 3) + \"...\" : msg.text\n\n // Convert markdown to Telegram MarkdownV2\n const formatted = msg.parseMode === \"markdown\" || msg.parseMode === undefined\n ? markdownToTelegramHtml(text)\n : text\n\n const params: Record<string, unknown> = {\n chat_id: msg.chatId,\n text: formatted,\n parse_mode: \"HTML\",\n }\n\n if (msg.replyTo) {\n params.reply_to_message_id = parseInt(msg.replyTo, 10)\n }\n\n if (msg.parseMode === \"html\") {\n params.parse_mode = \"HTML\"\n params.text = text\n } else if (msg.parseMode === \"plain\") {\n delete params.parse_mode\n params.text = text\n }\n\n try {\n const result = await this.apiCall(token, \"sendMessage\", params)\n return String(result.result?.message_id || \"\")\n } catch (e: any) {\n // Retry without formatting if MarkdownV2 fails\n if (params.parse_mode) {\n delete params.parse_mode\n params.text = text\n const result = await this.apiCall(token, \"sendMessage\", params)\n return String(result.result?.message_id || \"\")\n }\n throw e\n }\n }\n\n /**\n * Edit an existing message (for streaming updates).\n */\n async editMessage(chatId: string, messageId: string, text: string, parseMode?: string, accountId?: string): Promise<boolean> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return false\n\n const maxLen = 4096\n const trimmed = text.length > maxLen ? text.slice(0, maxLen - 3) + \"...\" : text\n\n const formatted = parseMode !== \"html\" && parseMode !== \"plain\"\n ? markdownToTelegramHtml(trimmed)\n : trimmed\n\n const params: Record<string, unknown> = {\n chat_id: chatId,\n message_id: parseInt(messageId, 10),\n text: formatted,\n parse_mode: \"HTML\",\n }\n\n if (parseMode === \"html\") {\n params.parse_mode = \"HTML\"\n params.text = trimmed\n } else if (parseMode === \"plain\") {\n delete params.parse_mode\n params.text = trimmed\n }\n\n try {\n await this.apiCall(token, \"editMessageText\", params)\n return true\n } catch (e: any) {\n if (e.message?.includes(\"message is not modified\")) return true\n if (params.parse_mode) {\n delete params.parse_mode\n params.text = trimmed\n try {\n await this.apiCall(token, \"editMessageText\", params)\n return true\n } catch { return false }\n }\n return false\n }\n }\n\n /**\n * React to a message with an emoji.\n */\n async react(chatId: string, messageId: string, emoji: string = \"👀\", accountId?: string): Promise<void> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return\n\n try {\n await this.apiCall(token, \"setMessageReaction\", {\n chat_id: chatId,\n message_id: parseInt(messageId, 10),\n reaction: [{ type: \"emoji\", emoji }],\n })\n } catch {\n // Best-effort\n }\n }\n\n /**\n * Send typing indicator.\n */\n async sendTyping(chatId: string, accountId?: string): Promise<void> {\n const token = this.resolveToken(chatId, accountId)\n if (!token) return\n\n try {\n await this.apiCall(token, \"sendChatAction\", {\n chat_id: chatId,\n action: \"typing\",\n })\n } catch {\n // Best-effort\n }\n }\n\n // --- Internal ---\n\n private async pollLoop(accountId: string, config: TelegramAccountConfig): Promise<void> {\n let consecutiveErrors = 0\n\n while (this.polling) {\n try {\n const offset = this.offsets.get(accountId) || 0\n const data = await this.apiCall(config.token, \"getUpdates\", {\n offset: offset || undefined,\n timeout: 30,\n allowed_updates: [\"message\"],\n })\n\n const updates: TelegramUpdate[] = data.result || []\n\n for (const update of updates) {\n this.offsets.set(accountId, update.update_id + 1)\n\n if (update.message && this.handler) {\n const msg = update.message\n\n // Extract text from any message type\n let text = msg.text || msg.caption || \"\"\n let mediaInfo: IncomingMessage[\"media\"] | undefined\n\n // Handle media messages\n const hasPhoto = msg.photo && msg.photo.length > 0\n const hasVoice = !!msg.voice\n const hasAudio = !!msg.audio\n const hasVideo = !!msg.video\n const hasDocument = !!msg.document\n const hasMedia = hasPhoto || hasVoice || hasAudio || hasVideo || hasDocument\n\n if (hasMedia) {\n // Download media file\n let fileId: string | undefined\n let mime = \"application/octet-stream\"\n\n if (hasPhoto) {\n fileId = msg.photo![msg.photo!.length - 1].file_id // largest photo\n mime = \"image/jpeg\"\n if (!text) text = \"[Photo attached — please describe what you see]\"\n } else if (hasVoice) {\n fileId = msg.voice!.file_id\n mime = msg.voice!.mime_type || \"audio/ogg\"\n if (!text) text = \"[Voice message — please transcribe and respond]\"\n } else if (hasAudio) {\n fileId = msg.audio!.file_id\n mime = msg.audio!.mime_type || \"audio/mpeg\"\n if (!text) text = `[Audio: ${msg.audio!.title || \"audio file\"}]`\n } else if (hasVideo) {\n fileId = msg.video!.file_id\n mime = msg.video!.mime_type || \"video/mp4\"\n if (!text) text = \"[Video attached]\"\n } else if (hasDocument) {\n fileId = msg.document!.file_id\n mime = msg.document!.mime_type || \"application/octet-stream\"\n if (!text) text = `[Document: ${msg.document!.file_name || \"file\"}]`\n }\n\n if (fileId) {\n try {\n // Get file path from Telegram\n const fileInfo = await this.apiCall(config.token, \"getFile\", { file_id: fileId })\n const filePath = fileInfo.result?.file_path\n if (filePath) {\n // Download file\n const fileUrl = `https://api.telegram.org/file/bot${config.token}/${filePath}`\n const res = await fetch(fileUrl)\n if (res.ok) {\n const buffer = Buffer.from(await res.arrayBuffer())\n const ext = mime.split(\"/\")[1]?.split(\";\")[0] || \"bin\"\n const { mkdirSync, writeFileSync } = await import(\"fs\")\n const { randomUUID } = await import(\"crypto\")\n const { resolve, join } = await import(\"path\")\n const mediaDir = resolve(process.cwd(), \".agentx/media/telegram\")\n mkdirSync(mediaDir, { recursive: true })\n const fileName = msg.document?.file_name || `${randomUUID()}.${ext}`\n const localPath = join(mediaDir, fileName)\n writeFileSync(localPath, buffer)\n mediaInfo = { path: localPath, type: mime, fileName }\n }\n }\n } catch (e: any) {\n this.log(`Media download failed: ${e.message}`)\n }\n }\n }\n\n if (!text) continue\n\n const incoming: IncomingMessage = {\n id: String(msg.message_id),\n channel: \"telegram\",\n accountId,\n sender: {\n id: String(msg.from.id),\n name: [msg.from.first_name, msg.from.last_name].filter(Boolean).join(\" \"),\n username: msg.from.username,\n },\n group: msg.chat.type !== \"private\"\n ? { id: String(msg.chat.id), name: msg.chat.title || \"\" }\n : undefined,\n text,\n media: mediaInfo,\n replyTo: msg.reply_to_message\n ? String(msg.reply_to_message.message_id)\n : undefined,\n replyToText: msg.reply_to_message\n ? (msg.reply_to_message.text || msg.reply_to_message.caption || `[message from ${msg.reply_to_message.from?.first_name || \"unknown\"}]`)\n : undefined,\n timestamp: new Date(msg.date * 1000),\n raw: update,\n }\n\n // Track chat→account mapping for DM replies\n this.chatAccountMap.set(String(msg.chat.id), accountId)\n\n this.handler(incoming).catch((e) => {\n this.log(`Error handling message: ${e.message}`)\n })\n }\n }\n consecutiveErrors = 0\n } catch (e: any) {\n consecutiveErrors++\n const backoff = Math.min(5000 * Math.pow(2, consecutiveErrors - 1), 60000)\n this.log(`Poll error (${accountId}): ${e.message} [retry in ${backoff / 1000}s, errors: ${consecutiveErrors}]`)\n await new Promise((r) => setTimeout(r, backoff))\n }\n }\n }\n\n private async apiCall(\n token: string,\n method: string,\n params?: Record<string, unknown>,\n ): Promise<any> {\n const url = `https://api.telegram.org/bot${token}/${method}`\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: params ? JSON.stringify(params) : undefined,\n })\n\n if (!res.ok) {\n const text = await res.text()\n throw new Error(`Telegram API error: ${res.status} ${text}`)\n }\n\n return res.json()\n }\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\nimport { existsSync, mkdirSync, writeFileSync } from \"fs\"\nimport { resolve, join } from \"path\"\nimport { randomUUID } from \"crypto\"\n\n// --- WhatsApp adapter using Baileys (WhatsApp Web multi-device) ---\n// Baileys is an optional dependency. If not installed, adapter logs a warning.\n//\n// First run: displays QR code in terminal for pairing.\n// Subsequent runs: reconnects automatically using saved session.\n\nexport interface WhatsAppRoute {\n contact?: string // phone number or partial match\n group?: string // group name or JID partial match\n agent: string // agent ID to route to\n}\n\nexport class WhatsAppAdapter implements ChannelAdapter {\n readonly name = \"whatsapp\"\n private sessionDir: string\n private defaultAgent?: string\n private allowFrom?: string[]\n private routes: WhatsAppRoute[]\n private handler?: (msg: IncomingMessage) => Promise<void>\n private sock: any = null\n private sentMessageIds: Set<string> = new Set() // Track our own replies to prevent loops\n private log: (...args: unknown[]) => void\n\n constructor(\n config: {\n sessionDir: string\n defaultAgent?: string\n allowFrom?: string[]\n routes?: WhatsAppRoute[]\n },\n log: (...args: unknown[]) => void = console.error.bind(console, \"[whatsapp]\"),\n ) {\n this.sessionDir = resolve(config.sessionDir)\n this.defaultAgent = config.defaultAgent\n this.allowFrom = config.allowFrom\n this.routes = config.routes || []\n this.log = log\n }\n\n /**\n * Resolve which agent handles a message based on routes.\n */\n resolveAgent(senderPhone: string, groupName?: string, groupJid?: string): string | undefined {\n for (const route of this.routes) {\n // Match by contact phone number\n if (route.contact) {\n const normalized = route.contact.replace(/\\+/g, \"\")\n if (senderPhone.includes(normalized) || normalized.includes(senderPhone)) {\n return route.agent\n }\n }\n // Match by group name or JID\n if (route.group && (groupName || groupJid)) {\n const pattern = route.group.toLowerCase()\n if (\n groupName?.toLowerCase().includes(pattern) ||\n groupJid?.toLowerCase().includes(pattern)\n ) {\n return route.agent\n }\n }\n }\n return this.defaultAgent\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n // Dynamic import — Baileys is optional\n let makeWASocket: any\n let useMultiFileAuthState: any\n let DisconnectReason: any\n\n let baileys: any\n try {\n baileys = await import(\"@whiskeysockets/baileys\")\n makeWASocket = baileys.default || baileys.makeWASocket\n useMultiFileAuthState = baileys.useMultiFileAuthState\n DisconnectReason = baileys.DisconnectReason\n } catch {\n this.log(\"WhatsApp requires @whiskeysockets/baileys. Install with:\")\n this.log(\" npm install @whiskeysockets/baileys\")\n return\n }\n\n mkdirSync(this.sessionDir, { recursive: true })\n\n const { state, saveCreds } = await useMultiFileAuthState(this.sessionDir)\n\n // Fetch latest WhatsApp Web version (critical for avoiding 405 errors)\n let version: [number, number, number] | undefined\n try {\n const { version: v } = await baileys.fetchLatestBaileysVersion()\n version = v\n this.log(`WhatsApp Web version: ${v.join(\".\")}`)\n } catch {\n this.log(\"Could not fetch WA version, using default\")\n }\n\n // Silent logger with proper method stubs (Baileys v7 requires all methods)\n const silentLogger = {\n level: \"silent\",\n trace: () => {}, debug: () => {}, info: () => {},\n warn: () => {}, fatal: () => {},\n error: (...args: any[]) => this.log(\"WA error:\", ...args),\n child: () => silentLogger,\n } as any\n\n this.sock = makeWASocket({\n auth: {\n creds: state.creds,\n keys: baileys.makeCacheableSignalKeyStore\n ? baileys.makeCacheableSignalKeyStore(state.keys, silentLogger)\n : state.keys,\n },\n ...(version ? { version } : {}),\n logger: silentLogger,\n printQRInTerminal: false,\n browser: [\"agentx\", \"server\", \"1.0\"],\n syncFullHistory: false,\n markOnlineOnConnect: false,\n })\n\n // Save credentials on update\n this.sock.ev.on(\"creds.update\", saveCreds)\n\n // Debug: log all events to diagnose message reception\n this.sock.ev.on(\"messaging-history.set\", (data: any) => {\n this.log(`WA history sync: ${data.messages?.length || 0} messages, ${data.isLatest ? \"latest\" : \"partial\"}`)\n })\n\n // Handle connection updates\n this.sock.ev.on(\"connection.update\", async (update: any) => {\n const { connection, lastDisconnect, qr } = update\n\n if (qr) {\n this.log(\"Scan QR code with WhatsApp to connect:\")\n // Render QR as text in terminal\n try {\n // @ts-ignore - no type declarations for qrcode-terminal\n const { default: qrcode } = await import(\"qrcode-terminal\") as any\n qrcode.generate(qr, { small: true })\n } catch {\n // If qrcode-terminal not available, log raw QR string\n this.log(`QR: ${qr}`)\n this.log(\"Install qrcode-terminal for visual QR: npm install qrcode-terminal\")\n }\n }\n\n if (connection === \"close\") {\n const statusCode = lastDisconnect?.error?.output?.statusCode\n this.log(`WhatsApp connection closed (status: ${statusCode})`)\n\n if (statusCode === 515) {\n // Stream error — restart after delay\n this.log(\"Stream error, reconnecting in 5s...\")\n setTimeout(() => this.start(), 5000)\n } else if (statusCode === DisconnectReason?.loggedOut || statusCode === 401) {\n this.log(\"Logged out. Delete session dir and restart to re-scan QR.\")\n } else if (statusCode !== undefined) {\n this.log(\"Reconnecting in 3s...\")\n setTimeout(() => this.start(), 3000)\n }\n // If statusCode is undefined, don't reconnect (likely startup failure)\n }\n\n if (connection === \"open\") {\n this.log(\"WhatsApp connected\")\n }\n })\n\n // Handle incoming messages\n this.sock.ev.on(\"messages.upsert\", async (m: any) => {\n this.log(`WA messages.upsert: ${m.messages?.length || 0} messages, type: ${m.type}`)\n\n if (!this.handler) return\n\n for (const msg of m.messages || []) {\n const jidShort = (msg.key.remoteJid || \"\").replace(/@.*/, \"\").slice(-6)\n const hasText = !!(msg.message?.conversation || msg.message?.extendedTextMessage?.text)\n this.log(`WA msg: from=${jidShort} fromMe=${msg.key.fromMe} hasText=${hasText} type=${Object.keys(msg.message || {}).join(\",\")}`)\n\n // Skip status broadcasts\n if (msg.key.remoteJid === \"status@broadcast\") continue\n\n // Skip messages we sent as replies (prevents loops)\n if (msg.key.id && this.sentMessageIds.has(msg.key.id)) {\n this.sentMessageIds.delete(msg.key.id)\n continue\n }\n\n // For fromMe: only process self-chat (messaging yourself = talking to agent)\n // OpenClaw pattern: if your own number is in allowFrom, self-chat is enabled.\n // WhatsApp uses LID format for self-chat remoteJid.\n if (msg.key.fromMe) {\n const me = this.sock?.user\n const remoteJid = msg.key.remoteJid || \"\"\n // Self-chat: remoteJid matches our JID or LID\n const myJid = me?.id?.replace(/:.*/, \"\") || \"\" // 21624309128\n const myLid = me?.lid?.replace(/:.*/, \"\") || \"\" // 214997540012179\n const chatUser = remoteJid.replace(/:.*/, \"\").replace(/@.*/, \"\")\n const isSelfChat = (chatUser === myJid) || (chatUser === myLid)\n if (!isSelfChat) continue\n }\n\n // Extract text content + detect media\n let text = msg.message?.conversation\n || msg.message?.extendedTextMessage?.text\n || msg.message?.imageMessage?.caption\n || msg.message?.videoMessage?.caption\n || \"\"\n\n // Detect media type\n const msgContent = msg.message || {}\n const hasImage = !!msgContent.imageMessage\n const hasAudio = !!msgContent.audioMessage\n const hasVideo = !!msgContent.videoMessage\n const hasDocument = !!msgContent.documentMessage\n const hasSticker = !!msgContent.stickerMessage\n const hasMedia = hasImage || hasAudio || hasVideo || hasDocument || hasSticker\n\n // Add media placeholder to text if no caption\n if (hasMedia && !text) {\n if (hasImage) text = \"[Image attached — please describe what you see]\"\n else if (hasAudio) text = \"[Voice message attached — please transcribe and respond]\"\n else if (hasVideo) text = \"[Video attached]\"\n else if (hasDocument) text = `[Document: ${msgContent.documentMessage?.fileName || \"file\"}]`\n else if (hasSticker) text = \"[Sticker]\"\n }\n\n if (!text) continue\n\n const jid = msg.key.remoteJid || \"\"\n const isGroup = jid.endsWith(\"@g.us\")\n\n // For DMs: remoteJid is the conversation partner\n // For groups: participant is the sender\n const chatPhone = jid.replace(/@.*$/, \"\")\n const senderJid = isGroup ? (msg.key.participant || \"\") : jid\n const senderPhone = senderJid.replace(/@.*$/, \"\")\n\n // Allowlist check — match against chat (conversation partner) not just sender\n // This allows the owner (fromMe) to talk to their agent via any contact\n if (this.allowFrom?.length && !msg.key.fromMe) {\n const allowed = this.allowFrom.some(p => {\n const normalized = p.replace(/\\+/g, \"\")\n return senderPhone.includes(normalized) || chatPhone.includes(normalized)\n })\n if (!allowed) continue\n }\n\n const senderName = msg.key.fromMe ? \"me\" : (msg.pushName || senderPhone)\n\n // Get group name if available\n let groupName: string | undefined\n if (isGroup && this.sock) {\n try {\n const metadata = await this.sock.groupMetadata(jid)\n groupName = metadata.subject\n } catch { /* group metadata unavailable */ }\n }\n\n // Route to agent based on contact/group rules\n const resolvedAgent = this.resolveAgent(senderPhone, groupName, isGroup ? jid : undefined)\n if (!resolvedAgent) {\n this.log(`No route for ${isGroup ? `group ${groupName || jid}` : senderPhone}, skipping`)\n continue\n }\n\n // Download and save media if present\n let mediaInfo: IncomingMessage[\"media\"] | undefined\n if (hasMedia && this.sock) {\n try {\n const baileys = await import(\"@whiskeysockets/baileys\")\n const buffer = await baileys.downloadMediaMessage(msg, \"buffer\", {}, {\n reuploadRequest: this.sock.updateMediaMessage,\n logger: this.sock.logger,\n })\n if (buffer) {\n const mime = msgContent.imageMessage?.mimetype\n || msgContent.audioMessage?.mimetype || \"audio/ogg\"\n || msgContent.videoMessage?.mimetype || \"video/mp4\"\n || msgContent.documentMessage?.mimetype\n || msgContent.stickerMessage?.mimetype || \"image/webp\"\n || \"application/octet-stream\"\n const ext = mime.split(\"/\")[1]?.split(\";\")[0] || \"bin\"\n const mediaDir = resolve(this.sessionDir, \"../media/inbound\")\n mkdirSync(mediaDir, { recursive: true })\n const fileName = msgContent.documentMessage?.fileName || `${randomUUID()}.${ext}`\n const filePath = join(mediaDir, fileName)\n writeFileSync(filePath, buffer)\n mediaInfo = { path: filePath, type: mime, fileName }\n this.log(`WA media saved: ${mime} -> ${filePath}`)\n }\n } catch (e: any) {\n this.log(`WA media download failed: ${e.message}`)\n }\n }\n\n const incoming: IncomingMessage = {\n id: msg.key.id || String(Date.now()),\n channel: \"whatsapp\",\n accountId: \"default\",\n sender: {\n id: msg.key.fromMe\n ? (this.sock?.user?.id?.replace(/:.*/, \"\") || senderPhone) + \"@s.whatsapp.net\"\n : senderPhone,\n name: senderName,\n username: senderPhone,\n },\n group: isGroup ? {\n id: jid,\n name: groupName || jid,\n } : undefined,\n text,\n media: mediaInfo,\n replyTo: msg.message?.extendedTextMessage?.contextInfo?.stanzaId,\n timestamp: new Date((msg.messageTimestamp || 0) * 1000),\n raw: msg,\n resolvedAgent,\n }\n\n this.handler(incoming).catch((e: any) => {\n this.log(`Error handling message: ${e.message}`)\n })\n }\n })\n }\n\n async stop(): Promise<void> {\n if (this.sock) {\n this.sock.end()\n this.sock = null\n }\n }\n\n async send(msg: OutgoingMessage): Promise<string> {\n if (!this.sock) {\n this.log(\"WhatsApp not connected\")\n return \"\"\n }\n\n const jid = msg.chatId.includes(\"@\")\n ? msg.chatId\n : `${msg.chatId}@s.whatsapp.net`\n\n try {\n const sent = await this.sock.sendMessage(jid, { text: msg.text })\n const sentId = sent?.key?.id || \"\"\n // Track to prevent loop (our own reply echoed back as fromMe)\n if (sentId) this.sentMessageIds.add(sentId)\n return sentId\n } catch (e: any) {\n this.log(`Send error: ${e.message}`)\n return \"\"\n }\n }\n\n async editMessage(chatId: string, messageId: string, text: string): Promise<boolean> {\n if (!this.sock) return false\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendMessage(jid, {\n text,\n edit: { remoteJid: jid, id: messageId, fromMe: true },\n })\n return true\n } catch { return false }\n }\n\n async sendTyping(chatId: string): Promise<void> {\n if (!this.sock) return\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendPresenceUpdate(\"composing\", jid)\n } catch { /* best-effort */ }\n }\n\n async react(chatId: string, messageId: string, emoji: string = \"👀\"): Promise<void> {\n if (!this.sock) return\n const jid = chatId.includes(\"@\") ? chatId : `${chatId}@s.whatsapp.net`\n try {\n await this.sock.sendMessage(jid, {\n react: { text: emoji, key: { remoteJid: jid, id: messageId } },\n })\n } catch { /* best-effort */ }\n }\n}\n","import type { DaemonConfig, CronJobDef } from \"@/daemon/config\"\nimport type { AgentRegistry } from \"@/agents/registry\"\nimport type { HookRegistry } from \"@/hooks\"\nimport type { CronJobState, CronRunResult } from \"./types\"\nimport { writeFileSync, mkdirSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Cron Scheduler: lightweight cron engine with timezone support ---\n// No external dependencies — uses setTimeout-based scheduling.\n\n/**\n * Parse a cron expression into next-fire timestamp.\n * Supports standard 5-field format: minute hour day-of-month month day-of-week\n */\nfunction parseCronField(field: string, min: number, max: number): number[] {\n const values: number[] = []\n\n for (const part of field.split(\",\")) {\n if (part === \"*\") {\n for (let i = min; i <= max; i++) values.push(i)\n } else if (part.includes(\"/\")) {\n const [range, stepStr] = part.split(\"/\")\n const step = parseInt(stepStr, 10)\n const start = range === \"*\" ? min : parseInt(range, 10)\n for (let i = start; i <= max; i += step) values.push(i)\n } else if (part.includes(\"-\")) {\n const [a, b] = part.split(\"-\").map(Number)\n for (let i = a; i <= b; i++) values.push(i)\n } else {\n values.push(parseInt(part, 10))\n }\n }\n\n return [...new Set(values)].sort((a, b) => a - b)\n}\n\nfunction getNextCronDate(expression: string, after: Date, timezone: string): Date {\n const fields = expression.trim().split(/\\s+/)\n if (fields.length !== 5) throw new Error(`Invalid cron: ${expression}`)\n\n const minutes = parseCronField(fields[0], 0, 59)\n const hours = parseCronField(fields[1], 0, 23)\n const daysOfMonth = parseCronField(fields[2], 1, 31)\n const months = parseCronField(fields[3], 1, 12)\n const daysOfWeek = parseCronField(fields[4], 0, 6)\n\n // Convert to timezone-aware date\n const fmt = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n })\n\n // Start searching from next minute\n const candidate = new Date(after.getTime() + 60_000)\n candidate.setSeconds(0, 0)\n\n // Search up to 1 year ahead\n const limit = new Date(candidate.getTime() + 366 * 24 * 60 * 60 * 1000)\n\n while (candidate < limit) {\n const parts = fmt.formatToParts(candidate)\n const get = (type: string) => parseInt(parts.find((p) => p.type === type)?.value || \"0\", 10)\n\n const m = get(\"minute\")\n const h = get(\"hour\")\n const dom = get(\"day\")\n const mon = get(\"month\")\n const dow = candidate.getDay()\n\n if (\n minutes.includes(m) &&\n hours.includes(h) &&\n daysOfMonth.includes(dom) &&\n months.includes(mon) &&\n daysOfWeek.includes(dow)\n ) {\n return candidate\n }\n\n // Advance by 1 minute\n candidate.setTime(candidate.getTime() + 60_000)\n }\n\n throw new Error(`No next run found for cron \"${expression}\" within 1 year`)\n}\n\nexport class CronScheduler {\n private jobs: Map<string, CronJobState> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private registry: AgentRegistry\n private hooks?: HookRegistry\n private runsDir: string\n private running = false\n private log: (...args: unknown[]) => void\n\n constructor(\n config: DaemonConfig,\n registry: AgentRegistry,\n hooks?: HookRegistry,\n log: (...args: unknown[]) => void = console.error.bind(console, \"[cron]\"),\n ) {\n this.registry = registry\n this.hooks = hooks\n this.log = log\n this.runsDir = resolve(process.cwd(), \".agentx/cron/runs\")\n\n for (const [id, def] of Object.entries(config.crons)) {\n this.jobs.set(id, {\n id,\n enabled: def.enabled,\n schedule: def.schedule,\n timezone: def.timezone,\n agent: def.agent,\n prompt: def.prompt,\n timeout: def.timeout,\n model: def.model,\n onError: def.onError,\n consecutiveErrors: 0,\n totalRuns: 0,\n })\n }\n }\n\n async start(): Promise<void> {\n this.running = true\n\n if (!existsSync(this.runsDir)) {\n mkdirSync(this.runsDir, { recursive: true })\n }\n\n for (const [id, job] of this.jobs) {\n if (!job.enabled) {\n this.log(`Job \"${id}\" is disabled, skipping`)\n continue\n }\n this.scheduleNext(id)\n }\n\n this.log(`${this.jobs.size} cron job(s) loaded, ${Array.from(this.jobs.values()).filter((j) => j.enabled).length} enabled`)\n }\n\n async stop(): Promise<void> {\n this.running = false\n for (const timer of this.timers.values()) {\n clearTimeout(timer)\n }\n this.timers.clear()\n }\n\n private scheduleNext(jobId: string): void {\n const job = this.jobs.get(jobId)\n if (!job || !job.enabled || !this.running) return\n\n try {\n const nextRun = getNextCronDate(job.schedule, new Date(), job.timezone)\n job.nextRun = nextRun\n const delay = nextRun.getTime() - Date.now()\n\n this.log(`Job \"${jobId}\" next run: ${nextRun.toISOString()} (in ${Math.round(delay / 1000)}s)`)\n\n const timer = setTimeout(() => this.executeJob(jobId), delay)\n this.timers.set(jobId, timer)\n } catch (e: any) {\n this.log(`Failed to schedule \"${jobId}\": ${e.message}`)\n }\n }\n\n private async executeJob(jobId: string): Promise<void> {\n const job = this.jobs.get(jobId)\n if (!job || !this.running) return\n\n // Pre-hook\n if (this.hooks?.has(\"pre:cron-run\" as any)) {\n const hookResult = await this.hooks.execute(\"pre:cron-run\" as any, {\n event: \"pre:cron-run\" as any,\n jobId,\n agent: job.agent,\n prompt: job.prompt,\n })\n if (hookResult.blocked) {\n this.log(`Job \"${jobId}\" blocked by hook: ${hookResult.message}`)\n this.scheduleNext(jobId)\n return\n }\n }\n\n this.log(`Executing job \"${jobId}\" -> agent \"${job.agent}\"`)\n const startedAt = new Date()\n job.lastRun = startedAt\n job.totalRuns++\n\n try {\n const response = await this.registry.execute({\n message: job.prompt,\n agentId: job.agent,\n context: { channel: \"cron\" },\n })\n\n const result: CronRunResult = {\n jobId,\n startedAt,\n completedAt: new Date(),\n success: !response.error,\n response: response.content,\n error: response.error,\n duration: response.duration || Date.now() - startedAt.getTime(),\n }\n\n if (response.error) {\n job.consecutiveErrors++\n this.log(`Job \"${jobId}\" failed (${job.consecutiveErrors} consecutive): ${response.error}`)\n\n if (job.onError === \"disable\" && job.consecutiveErrors >= 3) {\n job.enabled = false\n this.log(`Job \"${jobId}\" disabled after ${job.consecutiveErrors} consecutive errors`)\n }\n } else {\n job.consecutiveErrors = 0\n this.log(`Job \"${jobId}\" completed in ${result.duration}ms`)\n }\n\n // Log run\n this.logRun(result)\n\n // Post-hook\n if (this.hooks?.has(\"post:cron-run\" as any)) {\n await this.hooks.execute(\"post:cron-run\" as any, {\n event: \"post:cron-run\" as any,\n jobId,\n success: result.success,\n duration: result.duration,\n error: result.error ? new Error(result.error) : undefined,\n })\n }\n } catch (e: any) {\n job.consecutiveErrors++\n this.log(`Job \"${jobId}\" threw: ${e.message}`)\n }\n\n // Schedule next run\n this.scheduleNext(jobId)\n }\n\n private logRun(result: CronRunResult): void {\n try {\n const runDir = resolve(this.runsDir, result.jobId)\n if (!existsSync(runDir)) {\n mkdirSync(runDir, { recursive: true })\n }\n\n const filename = `${result.startedAt.toISOString().replace(/[:.]/g, \"-\")}.json`\n writeFileSync(\n resolve(runDir, filename),\n JSON.stringify(result, null, 2),\n )\n } catch {\n // Don't fail on logging errors\n }\n }\n\n /**\n * List all jobs and their status.\n */\n list(): CronJobState[] {\n return Array.from(this.jobs.values())\n }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"http\"\nimport { writeFileSync, existsSync, unlinkSync, mkdirSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\nimport { loadDaemonConfig, validateWorkspaces, type DaemonConfig } from \"./config\"\nimport { AgentRegistry } from \"@/agents/registry\"\nimport { MessageRouter } from \"@/channels/router\"\nimport { TelegramAdapter } from \"@/channels/telegram\"\nimport { WhatsAppAdapter } from \"@/channels/whatsapp\"\nimport { DiscordAdapter } from \"@/channels/discord\"\nimport { GitLabAdapter } from \"@/channels/gitlab\"\nimport { CronScheduler } from \"@/crons/scheduler\"\nimport { Logger } from \"./logger\"\nimport { WebhookHandler } from \"./webhooks\"\nimport { A2AMesh } from \"@/a2a/mesh\"\nimport { HookRegistry, loadHooks } from \"@/hooks\"\n\n// --- AgentX Daemon: the thin orchestration layer ---\n//\n// This is NOT an AI runtime. It's a message router + scheduler + mesh\n// that triggers Claude Code sessions in workspace directories.\n//\n// Each agent = a workspace with .claude/ config\n// agentx just orchestrates WHEN and WHERE Claude Code runs.\n\nexport class AgentXDaemon {\n private config: DaemonConfig\n private registry: AgentRegistry\n private router: MessageRouter\n private cron: CronScheduler\n private mesh?: A2AMesh\n private hooks: HookRegistry\n private httpServer?: ReturnType<typeof createServer>\n private webhooks: WebhookHandler\n private log: (...args: unknown[]) => void\n\n constructor(configPath?: string) {\n const logger = new Logger(\"agentx\")\n this.log = logger.asConsoleLog()\n\n // Load config\n this.log(\"Loading configuration...\")\n this.config = loadDaemonConfig(configPath)\n\n // Validate\n const warnings = validateWorkspaces(this.config)\n for (const w of warnings) {\n this.log(` ⚠ ${w}`)\n }\n\n // Initialize hooks\n this.hooks = new HookRegistry()\n loadHooks(process.cwd(), this.hooks)\n\n // Initialize agent registry\n this.registry = new AgentRegistry(this.config, this.log)\n\n // Initialize message router\n this.router = new MessageRouter(this.registry, this.config, this.hooks, this.log)\n this.webhooks = new WebhookHandler(this.registry, {}, this.log)\n\n // Initialize cron scheduler\n this.cron = new CronScheduler(this.config, this.registry, this.hooks, this.log)\n\n // Initialize mesh (if enabled)\n if (this.config.mesh.enabled) {\n this.mesh = new A2AMesh(this.config, this.log)\n this.router.setMesh(this.mesh)\n }\n }\n\n async start(): Promise<void> {\n this.log(\"\")\n this.log(\" ┌─────────────────────────────────────┐\")\n this.log(\" │ agentx daemon │\")\n this.log(\" └─────────────────────────────────────┘\")\n this.log(\"\")\n this.log(` Node: ${this.config.node.name} (${this.config.node.id})`)\n this.log(` Bind: ${this.config.node.bind}`)\n this.log(\"\")\n\n // 1. Start channels\n await this.startChannels()\n\n // 2. Start cron scheduler\n await this.cron.start()\n\n // 3. Start mesh\n if (this.mesh) {\n await this.mesh.start()\n }\n\n // 4. Start HTTP API\n await this.startHttpApi()\n\n // Print agent summary\n this.log(\"\")\n this.log(\" Agents:\")\n for (const agent of this.registry.list()) {\n this.log(` ${agent.id} (${agent.tier}) → ${agent.workspace}`)\n }\n\n // Print cron summary\n const cronJobs = this.cron.list()\n if (cronJobs.length) {\n this.log(\"\")\n this.log(\" Cron Jobs:\")\n for (const job of cronJobs) {\n const status = job.enabled ? \"enabled\" : \"disabled\"\n this.log(` ${job.id} [${status}] → ${job.agent} (${job.schedule})`)\n }\n }\n\n // Print mesh summary\n if (this.mesh) {\n this.log(\"\")\n this.log(\" Mesh Peers:\")\n for (const peer of this.mesh.directory()) {\n const status = peer.healthy ? \"✓\" : \"✗\"\n this.log(` ${status} ${peer.peer} (${peer.peerUrl})`)\n }\n }\n\n this.log(\"\")\n this.log(\" Ready.\")\n this.log(\"\")\n\n // Write PID file\n const pidFile = resolve(process.cwd(), \".agentx/daemon.pid\")\n mkdirSync(dirname(pidFile), { recursive: true })\n writeFileSync(pidFile, String(process.pid))\n this.log(` PID: ${process.pid} (${pidFile})`)\n\n // Catch unhandled errors — log but don't crash\n process.on(\"uncaughtException\", (err) => {\n this.log(`UNCAUGHT EXCEPTION: ${err.message}`)\n this.log(err.stack || \"\")\n })\n process.on(\"unhandledRejection\", (reason) => {\n this.log(`UNHANDLED REJECTION: ${reason}`)\n })\n\n // Graceful shutdown\n let stopping = false\n const shutdown = async (signal: string) => {\n if (stopping) return\n stopping = true\n this.log(`\\n Received ${signal}, shutting down gracefully...`)\n await this.stop()\n }\n process.on(\"SIGINT\", () => shutdown(\"SIGINT\"))\n process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"))\n }\n\n async stop(): Promise<void> {\n const start = Date.now()\n\n try {\n this.log(\" Stopping channels...\")\n await Promise.race([this.router.stopAll(), new Promise(r => setTimeout(r, 5000))])\n } catch (e: any) {\n this.log(` Channel stop error: ${e.message}`)\n }\n\n try {\n this.log(\" Stopping crons...\")\n await this.cron.stop()\n } catch {}\n\n try {\n if (this.mesh) {\n this.log(\" Stopping mesh...\")\n await this.mesh.stop()\n }\n } catch {}\n\n if (this.httpServer) {\n this.httpServer.close()\n }\n\n // Clean PID file\n try {\n const pidFile = resolve(process.cwd(), \".agentx/daemon.pid\")\n if (existsSync(pidFile)) unlinkSync(pidFile)\n } catch {}\n\n this.log(` Shutdown complete (${Date.now() - start}ms)`)\n process.exit(0)\n }\n\n private async startChannels(): Promise<void> {\n // Telegram\n if (this.config.channels.telegram.enabled) {\n const accounts = this.config.channels.telegram.accounts\n if (Object.keys(accounts).length > 0) {\n const telegram = new TelegramAdapter(accounts, this.log)\n this.router.addChannel(telegram)\n this.log(\" Telegram: enabled\")\n }\n }\n\n // WhatsApp\n if (this.config.channels.whatsapp.enabled) {\n const whatsapp = new WhatsAppAdapter(\n {\n sessionDir: this.config.channels.whatsapp.sessionDir,\n defaultAgent: this.config.channels.whatsapp.defaultAgent,\n allowFrom: this.config.channels.whatsapp.allowFrom,\n routes: this.config.channels.whatsapp.routes,\n },\n this.log,\n )\n this.router.addChannel(whatsapp)\n this.log(` WhatsApp: enabled (${this.config.channels.whatsapp.routes.length} routes)`)\n }\n\n // Discord\n if (this.config.channels.discord?.enabled && this.config.channels.discord.token) {\n const discord = new DiscordAdapter(\n {\n token: this.config.channels.discord.token,\n agentBinding: this.config.channels.discord.agentBinding,\n },\n this.log,\n )\n this.router.addChannel(discord)\n this.log(\" Discord: enabled\")\n }\n\n // GitLab\n if (this.config.channels.gitlab?.enabled && this.config.channels.gitlab.token) {\n const gitlab = new GitLabAdapter(\n {\n webhookPort: this.config.channels.gitlab.webhookPort,\n webhookSecret: this.config.channels.gitlab.webhookSecret,\n host: this.config.channels.gitlab.host,\n token: this.config.channels.gitlab.token,\n routes: this.config.channels.gitlab.routes,\n agentMappings: this.config.channels.gitlab.agentMappings,\n },\n this.log,\n )\n this.router.addChannel(gitlab)\n this.log(` GitLab: enabled (${this.config.channels.gitlab.routes.length} project routes, webhook :${this.config.channels.gitlab.webhookPort})`)\n }\n\n await this.router.startAll()\n }\n\n private async startHttpApi(): Promise<void> {\n const [host, portStr] = this.config.node.bind.split(\":\")\n const port = parseInt(portStr || \"18800\", 10)\n\n this.httpServer = createServer(async (req, res) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\")\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\")\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204)\n res.end()\n return\n }\n\n await this.handleHttp(req, res)\n })\n\n this.httpServer.on(\"error\", (err: any) => {\n if (err.code === \"EADDRINUSE\") {\n this.log(` ERROR: Port ${port} is already in use. Retrying in 5s...`)\n setTimeout(() => {\n this.httpServer?.close()\n this.httpServer?.listen(port, host || \"0.0.0.0\")\n }, 5000)\n } else {\n this.log(` HTTP error: ${err.message}`)\n }\n })\n\n this.httpServer.listen(port, host || \"0.0.0.0\", () => {\n this.log(` HTTP API: http://${host || \"0.0.0.0\"}:${port}`)\n })\n }\n\n private async handleHttp(req: IncomingMessage, res: ServerResponse): Promise<void> {\n const url = new URL(req.url || \"/\", `http://${req.headers.host || \"localhost\"}`)\n const path = url.pathname\n\n try {\n // Dynamic routes (before static switch)\n if (req.method === \"POST\" && path.startsWith(\"/webhook/\")) {\n await this.webhooks.handle(req, res, path)\n return\n }\n\n // OpenAI-compatible endpoint for ElevenLabs, Cursor, etc.\n // POST /v1/chat/completions or /llm/:agentId/v1/chat/completions\n if (req.method === \"POST\" && (path === \"/v1/chat/completions\" || path.match(/^\\/llm\\/[^/]+\\/v1\\/chat\\/completions$/))) {\n await this.handleOpenAICompat(req, res, path)\n return\n }\n\n switch (`${req.method} ${path}`) {\n case \"GET /health\":\n this.json(res, 200, {\n status: \"ok\",\n node: this.config.node,\n uptime: process.uptime(),\n agents: this.registry.list(),\n crons: this.cron.list().map((j) => ({ id: j.id, enabled: j.enabled, nextRun: j.nextRun })),\n mesh: this.mesh?.directory() || [],\n usage: this.registry.getTodayUsage(),\n })\n break\n\n case \"GET /usage\":\n this.json(res, 200, this.registry.getUsage(7))\n break\n\n case \"GET /agents\":\n this.json(res, 200, this.registry.list())\n break\n\n case \"GET /crons\":\n this.json(res, 200, this.cron.list())\n break\n\n case \"GET /mesh\":\n this.json(res, 200, this.mesh?.directory() || [])\n break\n\n case \"POST /task\": {\n const body = await readBody(req)\n if (!body.agent || !body.message) {\n this.json(res, 400, { error: \"Missing: agent, message\" })\n return\n }\n const response = await this.registry.execute({\n agentId: body.agent as string,\n message: body.message as string,\n context: body.context as any,\n })\n this.json(res, response.error ? 500 : 200, response)\n break\n }\n\n case \"POST /mesh/task\": {\n const body = await readBody(req)\n if (!body.peer || !body.message) {\n this.json(res, 400, { error: \"Missing: peer, message\" })\n return\n }\n if (!this.mesh) {\n this.json(res, 400, { error: \"Mesh not enabled\" })\n return\n }\n const result = await this.mesh.sendTask(body.peer as string, body.message as string)\n this.json(res, 200, { response: result })\n break\n }\n\n // A2A agent card discovery\n case \"GET /.well-known/agent-card.json\":\n this.json(res, 200, {\n name: this.config.node.name,\n description: `AgentX daemon node \"${this.config.node.name}\"`,\n url: `http://${this.config.node.bind}`,\n version: \"1.0.0\",\n capabilities: {\n streaming: false,\n pushNotifications: false,\n stateTransitionHistory: false,\n },\n skills: this.registry.list().map((a) => ({\n id: a.id,\n name: a.name,\n description: `Agent \"${a.name}\" (${a.tier})`,\n tags: [a.tier],\n })),\n defaultInputModes: [\"text\"],\n defaultOutputModes: [\"text\"],\n })\n break\n\n default:\n this.json(res, 404, {\n error: \"Not found\",\n endpoints: [\n \"GET /health\",\n \"GET /agents\",\n \"GET /crons\",\n \"GET /mesh\",\n \"POST /task { agent, message, context? }\",\n \"POST /mesh/task { peer, message }\",\n \"POST /webhook/:agentId[/:source] — webhook callback\",\n \"GET /.well-known/agent-card.json\",\n ],\n })\n }\n } catch (e: any) {\n this.json(res, 500, { error: e.message })\n }\n }\n\n /**\n * OpenAI-compatible chat completions endpoint.\n * Allows ElevenLabs, Cursor, or any OpenAI-compatible client to use an AgentX agent as an LLM.\n *\n * Routes:\n * POST /v1/chat/completions — uses \"model\" field as agent ID\n * POST /llm/:agentId/v1/chat/completions — explicit agent ID in URL\n *\n * Request format (OpenAI): { model, messages: [{role, content}], stream?, temperature? }\n * Response format (OpenAI): { id, object, choices: [{message: {role, content}}], usage }\n */\n private async handleOpenAICompat(req: IncomingMessage, res: ServerResponse, path: string): Promise<void> {\n const body = await readBody(req)\n\n // Resolve agent ID: from URL path or \"model\" field\n const pathMatch = path.match(/^\\/llm\\/([^/]+)\\//)\n const agentId = pathMatch?.[1] || (body.model as string) || \"atlas\"\n\n // Extract messages — use the last user message as the task\n const messages = (body.messages as Array<{ role: string; content: string }>) || []\n const lastUserMsg = [...messages].reverse().find(m => m.role === \"user\")\n\n if (!lastUserMsg?.content) {\n this.json(res, 400, { error: { message: \"No user message found\", type: \"invalid_request_error\" } })\n return\n }\n\n // Build conversation context from message history\n const historyLines = messages\n .slice(0, -1) // exclude the last message (it's the prompt)\n .map(m => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content.slice(0, 200)}`)\n\n const contextPrefix = historyLines.length > 0\n ? `[Conversation]\\n${historyLines.slice(-10).join(\"\\n\")}\\n\\n`\n : \"\"\n\n const stream = body.stream === true\n\n if (stream) {\n // SSE streaming response\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n })\n\n const requestId = `chatcmpl-${Date.now().toString(36)}`\n\n const response = await this.registry.execute({\n agentId,\n message: contextPrefix + lastUserMsg.content,\n context: { channel: \"api\", sender: \"openai-compat\" },\n })\n\n const content = response.error || response.content || \"\"\n\n // Send as a single chunk (Claude Code doesn't stream to us via execFile)\n const chunk = {\n id: requestId,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: agentId,\n choices: [{\n index: 0,\n delta: { role: \"assistant\", content },\n finish_reason: \"stop\",\n }],\n }\n res.write(`data: ${JSON.stringify(chunk)}\\n\\n`)\n res.write(\"data: [DONE]\\n\\n\")\n res.end()\n } else {\n // Standard response\n const response = await this.registry.execute({\n agentId,\n message: contextPrefix + lastUserMsg.content,\n context: { channel: \"api\", sender: \"openai-compat\" },\n })\n\n const content = response.error || response.content || \"\"\n const tokens = Math.ceil(content.length / 4)\n\n this.json(res, 200, {\n id: `chatcmpl-${Date.now().toString(36)}`,\n object: \"chat.completion\",\n created: Math.floor(Date.now() / 1000),\n model: agentId,\n choices: [{\n index: 0,\n message: { role: \"assistant\", content },\n finish_reason: \"stop\",\n }],\n usage: {\n prompt_tokens: Math.ceil(lastUserMsg.content.length / 4),\n completion_tokens: tokens,\n total_tokens: Math.ceil(lastUserMsg.content.length / 4) + tokens,\n },\n })\n }\n }\n\n private json(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data, null, 2))\n }\n}\n\nasync function readBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve, reject) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try {\n resolve(body ? JSON.parse(body) : {})\n } catch {\n resolve({})\n }\n })\n req.on(\"error\", reject)\n })\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\n\n// --- Discord adapter using discord.js ---\n// discord.js is an optional dependency.\n// Bot needs MESSAGE_CONTENT intent enabled in Discord Developer Portal.\n\nexport class DiscordAdapter implements ChannelAdapter {\n readonly name = \"discord\"\n private token: string\n private agentBinding?: string\n private handler?: (msg: IncomingMessage) => Promise<void>\n private client: any = null\n private log: (...args: unknown[]) => void\n\n constructor(\n config: { token: string; agentBinding?: string },\n log: (...args: unknown[]) => void = console.error.bind(console, \"[discord]\"),\n ) {\n this.token = config.token\n this.agentBinding = config.agentBinding\n this.log = log\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n let Discord: any\n\n try {\n Discord = await import(\"discord.js\")\n } catch {\n this.log(\"Discord requires discord.js. Install with:\")\n this.log(\" npm install discord.js\")\n return\n }\n\n const { Client, GatewayIntentBits } = Discord\n\n this.client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n GatewayIntentBits.DirectMessages,\n ],\n })\n\n this.client.on(\"ready\", () => {\n this.log(`Discord connected as ${this.client.user?.tag}`)\n })\n\n this.client.on(\"messageCreate\", async (msg: any) => {\n if (!this.handler) return\n if (msg.author.bot) return\n\n // Check if bot is mentioned or it's a DM\n const isMentioned = msg.mentions.users.has(this.client.user?.id)\n const isDM = !msg.guild\n\n if (!isMentioned && !isDM) return\n\n // Strip bot mention from text\n let text = msg.content\n if (this.client.user) {\n text = text.replace(new RegExp(`<@!?${this.client.user.id}>`, \"g\"), \"\").trim()\n }\n\n if (!text) return\n\n const incoming: IncomingMessage = {\n id: msg.id,\n channel: \"discord\",\n accountId: \"default\",\n sender: {\n id: msg.author.id,\n name: msg.author.displayName || msg.author.username,\n username: msg.author.username,\n },\n group: msg.guild ? {\n id: msg.channelId,\n name: msg.channel?.name || msg.channelId,\n } : undefined,\n text,\n replyTo: msg.reference?.messageId,\n timestamp: msg.createdAt,\n raw: msg,\n }\n\n this.handler(incoming).catch((e: any) => {\n this.log(`Error handling message: ${e.message}`)\n })\n })\n\n try {\n await this.client.login(this.token)\n } catch (e: any) {\n this.log(`Discord login failed: ${e.message}`)\n }\n }\n\n async stop(): Promise<void> {\n if (this.client) {\n this.client.destroy()\n this.client = null\n }\n }\n\n async send(msg: OutgoingMessage): Promise<string> {\n if (!this.client) return \"\"\n try {\n const channel = await this.client.channels.fetch(msg.chatId)\n if (!channel?.isTextBased()) return \"\"\n\n const sent = await channel.send({\n content: msg.text,\n ...(msg.replyTo ? { reply: { messageReference: msg.replyTo } } : {}),\n })\n return sent.id\n } catch (e: any) {\n this.log(`Send error: ${e.message}`)\n return \"\"\n }\n }\n\n async editMessage(chatId: string, messageId: string, text: string): Promise<boolean> {\n if (!this.client) return false\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (!channel?.isTextBased()) return false\n const message = await channel.messages.fetch(messageId)\n await message.edit(text)\n return true\n } catch { return false }\n }\n\n async sendTyping(chatId: string): Promise<void> {\n if (!this.client) return\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (channel?.isTextBased()) await channel.sendTyping()\n } catch { /* best-effort */ }\n }\n\n async react(chatId: string, messageId: string, emoji: string = \"👀\"): Promise<void> {\n if (!this.client) return\n try {\n const channel = await this.client.channels.fetch(chatId)\n if (!channel?.isTextBased()) return\n const message = await channel.messages.fetch(messageId)\n await message.react(emoji)\n } catch { /* best-effort */ }\n }\n}\n","import type { ChannelAdapter, IncomingMessage, OutgoingMessage } from \"./types\"\nimport { createServer, type IncomingMessage as HttpRequest, type ServerResponse } from \"http\"\n\n// --- GitLab webhook channel adapter ---\n//\n// Receives GitLab webhook events (note/comment, issue, MR, push, pipeline)\n// and routes @mention comments to agents. Agents reply as GitLab API comments.\n//\n// Config:\n// channels.gitlab:\n// enabled: true\n// webhookPort: 18810 # separate port for GitLab webhooks\n// webhookSecret: \"secret\" # validates X-Gitlab-Token header\n// host: \"https://gitlab.noqta.tn\"\n// token: \"${GITLAB_TOKEN}\" # for posting comments back\n// routes:\n// - project: \"noqta/mtgl-v2\"\n// agent: \"pm-mtgl\"\n// - project: \"*\"\n// agent: \"atlas\" # default for unmatched projects\n\nexport interface GitLabRoute {\n project: string // \"group/project\" or \"*\" for default\n agent: string\n}\n\nexport interface GitLabAgentMapping {\n agentId: string\n gitlabUsernames: string[] // GitLab @usernames that map to this agent\n keywords: string[] // keywords in comments that trigger this agent (e.g. \"coder\", \"devops\")\n}\n\nexport interface GitLabChannelConfig {\n webhookPort: number\n webhookSecret?: string\n host: string\n token: string\n routes: GitLabRoute[]\n agentMappings?: GitLabAgentMapping[] // @mention -> agent mappings\n}\n\ninterface GitLabNoteEvent {\n object_kind: \"note\"\n event_type: string\n user: { name: string; username: string }\n project: { path_with_namespace: string; web_url: string }\n object_attributes: {\n id: number\n note: string\n noteable_type: string // \"Issue\", \"MergeRequest\", \"Commit\"\n url: string\n }\n issue?: { iid: number; title: string; state: string }\n merge_request?: { iid: number; title: string; state: string; source_branch: string; target_branch: string }\n}\n\ninterface GitLabIssueEvent {\n object_kind: \"issue\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n iid: number\n title: string\n description: string\n state: string\n action: string\n url: string\n assignee_ids?: number[]\n }\n}\n\ninterface GitLabMREvent {\n object_kind: \"merge_request\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n iid: number\n title: string\n description: string\n state: string\n action: string\n source_branch: string\n target_branch: string\n url: string\n }\n}\n\ninterface GitLabPipelineEvent {\n object_kind: \"pipeline\"\n user: { name: string; username: string }\n project: { path_with_namespace: string }\n object_attributes: {\n id: number\n ref: string\n status: string\n duration: number\n }\n}\n\ntype GitLabEvent = GitLabNoteEvent | GitLabIssueEvent | GitLabMREvent | GitLabPipelineEvent | Record<string, unknown>\n\nexport class GitLabAdapter implements ChannelAdapter {\n readonly name = \"gitlab\"\n private config: GitLabChannelConfig\n private handler?: (msg: IncomingMessage) => Promise<void>\n private server?: ReturnType<typeof createServer>\n private botUsername?: string // resolved on first API call\n private sentNoteIds: Set<string> = new Set() // track our own comments\n private log: (...args: unknown[]) => void\n\n constructor(config: GitLabChannelConfig, log: (...args: unknown[]) => void = console.error.bind(console, \"[gitlab]\")) {\n this.config = config\n this.log = log\n }\n\n onMessage(handler: (msg: IncomingMessage) => Promise<void>): void {\n this.handler = handler\n }\n\n async start(): Promise<void> {\n // Resolve bot username to skip own comments\n try {\n const res = await fetch(`${this.config.host}/api/v4/user`, {\n headers: { \"PRIVATE-TOKEN\": this.config.token },\n })\n const data = await res.json() as any\n this.botUsername = data.username\n this.log(`Bot user: ${this.botUsername}`)\n } catch (e: any) {\n this.log(`Could not resolve bot user: ${e.message}`)\n }\n\n this.server = createServer(async (req, res) => {\n if (req.method === \"POST\") {\n await this.handleWebhook(req, res)\n } else {\n res.writeHead(200, { \"Content-Type\": \"text/plain\" })\n res.end(\"GitLab webhook endpoint. POST events here.\")\n }\n })\n\n this.server.listen(this.config.webhookPort, () => {\n this.log(`GitLab webhook listening on :${this.config.webhookPort}`)\n })\n }\n\n async stop(): Promise<void> {\n if (this.server) this.server.close()\n }\n\n /**\n * Send a reply — posts a comment back to GitLab via API.\n * chatId format: \"project:noteable_type:iid\" (e.g. \"noqta/mtgl-v2:issue:123\")\n */\n async send(msg: OutgoingMessage): Promise<string> {\n // chatId format: \"group/project:type:iid\" — split from the end\n const parts = msg.chatId.split(\":\")\n if (parts.length < 3) {\n this.log(`Invalid chatId for GitLab reply: ${msg.chatId}`)\n return \"\"\n }\n const iid = parts.pop()!\n const noteableType = parts.pop()!\n const project = parts.join(\":\") // rejoin in case project path had colons\n\n const encodedProject = encodeURIComponent(project)\n let endpoint: string\n\n switch (noteableType) {\n case \"issue\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/issues/${iid}/notes`\n break\n case \"merge_request\":\n endpoint = `${this.config.host}/api/v4/projects/${encodedProject}/merge_requests/${iid}/notes`\n break\n default:\n this.log(`Unsupported noteable type: ${noteableType}`)\n return \"\"\n }\n\n try {\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"PRIVATE-TOKEN\": this.config.token,\n },\n body: JSON.stringify({ body: msg.text }),\n })\n\n if (!res.ok) {\n const text = await res.text()\n this.log(`GitLab API error: ${res.status} ${text}`)\n return \"\"\n }\n\n const data = await res.json() as any\n const noteId = String(data.id || \"\")\n if (noteId) this.sentNoteIds.add(noteId)\n return noteId\n } catch (e: any) {\n this.log(`GitLab send error: ${e.message}`)\n return \"\"\n }\n }\n\n // --- Webhook handling ---\n\n private async handleWebhook(req: HttpRequest, res: ServerResponse): Promise<void> {\n // Validate secret\n if (this.config.webhookSecret) {\n const token = req.headers[\"x-gitlab-token\"]\n if (token !== this.config.webhookSecret) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify({ error: \"Invalid token\" }))\n return\n }\n }\n\n const body = await this.readBody(req)\n const event = body as GitLabEvent\n const objectKind = (event.object_kind || event.event_type || \"unknown\") as string\n\n this.log(`Event: ${objectKind} from ${(event as any).project?.path_with_namespace || \"unknown\"}`)\n\n // Route based on event type\n switch (objectKind) {\n case \"note\":\n await this.handleNote(event as GitLabNoteEvent, res)\n break\n case \"issue\":\n await this.handleIssue(event as GitLabIssueEvent, res)\n break\n case \"merge_request\":\n await this.handleMR(event as GitLabMREvent, res)\n break\n case \"pipeline\":\n await this.handlePipeline(event as GitLabPipelineEvent, res)\n break\n default:\n this.log(`Unhandled event: ${objectKind}`)\n res.writeHead(200)\n res.end(\"ok\")\n }\n }\n\n /**\n * Handle note/comment events — the primary use case.\n * Routes @mention comments to the right agent.\n */\n private async handleNote(event: GitLabNoteEvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n const note = event.object_attributes.note\n const project = event.project.path_with_namespace\n const user = event.user\n const noteId = String(event.object_attributes.id)\n\n // Skip our own comments (prevents infinite loop)\n if (this.botUsername && user.username === this.botUsername) {\n this.log(`Skipping own comment from ${this.botUsername}`)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n // Skip comments we already sent\n if (this.sentNoteIds.has(noteId)) {\n this.sentNoteIds.delete(noteId)\n res.writeHead(200); res.end(\"ok\"); return\n }\n\n // Determine the noteable context\n let noteableType = \"\"\n let noteableIid = \"\"\n let noteableTitle = \"\"\n if (event.issue) {\n noteableType = \"issue\"\n noteableIid = String(event.issue.iid)\n noteableTitle = event.issue.title\n } else if (event.merge_request) {\n noteableType = \"merge_request\"\n noteableIid = String(event.merge_request.iid)\n noteableTitle = event.merge_request.title\n }\n\n // Resolve agent: check @mentions in comment first, fall back to project route\n const agentId = this.resolveAgentFromMention(note) || this.resolveAgent(project)\n\n const chatId = `${project}:${noteableType}:${noteableIid}`\n\n const incoming: IncomingMessage = {\n id: String(event.object_attributes.id),\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: chatId,\n name: user.name,\n username: user.username,\n },\n text: `[GitLab ${noteableType} #${noteableIid}: ${noteableTitle}]\\n${user.name} commented:\\n${note}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => {\n this.log(`Error handling note: ${e.message}`)\n })\n\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle issue events (opened, updated, closed).\n */\n private async handleIssue(event: GitLabIssueEvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const agentId = this.resolveAgent(project)\n\n const incoming: IncomingMessage = {\n id: `issue-${attrs.iid}-${attrs.action}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:issue:${attrs.iid}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab Issue #${attrs.iid} ${attrs.action}]: ${attrs.title}\\n${attrs.description?.slice(0, 500) || \"\"}\\nURL: ${attrs.url}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling issue: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle merge request events.\n */\n private async handleMR(event: GitLabMREvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const agentId = this.resolveAgent(project)\n\n const incoming: IncomingMessage = {\n id: `mr-${attrs.iid}-${attrs.action}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:merge_request:${attrs.iid}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab MR !${attrs.iid} ${attrs.action}]: ${attrs.title}\\nBranch: ${attrs.source_branch} -> ${attrs.target_branch}\\n${attrs.description?.slice(0, 500) || \"\"}\\nURL: ${attrs.url}`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling MR: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Handle pipeline events (success, failed).\n */\n private async handlePipeline(event: GitLabPipelineEvent, res: ServerResponse): Promise<void> {\n if (!this.handler) { res.writeHead(200); res.end(\"ok\"); return }\n\n // Only notify on failures\n if (event.object_attributes.status !== \"failed\") {\n res.writeHead(200)\n res.end(\"ok\")\n return\n }\n\n const attrs = event.object_attributes\n const project = event.project.path_with_namespace\n const agentId = this.resolveAgent(project)\n\n const incoming: IncomingMessage = {\n id: `pipeline-${attrs.id}`,\n channel: \"gitlab\",\n accountId: \"default\",\n sender: {\n id: `${project}:pipeline:${attrs.id}`,\n name: event.user.name,\n username: event.user.username,\n },\n // No group — sender.id has project:type:iid for reply routing\n text: `[GitLab Pipeline FAILED] Project: ${project}\\nRef: ${attrs.ref}\\nDuration: ${attrs.duration}s\\nPlease investigate the failure.`,\n timestamp: new Date(),\n raw: event,\n resolvedAgent: agentId,\n }\n\n this.handler(incoming).catch((e) => this.log(`Error handling pipeline: ${e.message}`))\n res.writeHead(200)\n res.end(\"ok\")\n }\n\n /**\n * Resolve agent from @mentions in a comment.\n * Checks agentMappings for GitLab usernames and keywords.\n */\n private resolveAgentFromMention(text: string): string | undefined {\n if (!this.config.agentMappings?.length) return undefined\n\n const lower = text.toLowerCase()\n // Extract @mentions from the comment\n const mentions = text.match(/@(\\w+)/g)?.map(m => m.slice(1).toLowerCase()) || []\n\n for (const mapping of this.config.agentMappings) {\n // Check if any GitLab @username matches\n for (const username of mapping.gitlabUsernames) {\n if (mentions.includes(username.toLowerCase())) {\n this.log(`Mention @${username} -> agent ${mapping.agentId}`)\n return mapping.agentId\n }\n }\n // Check keywords\n for (const keyword of mapping.keywords) {\n if (lower.includes(keyword.toLowerCase())) {\n this.log(`Keyword \"${keyword}\" -> agent ${mapping.agentId}`)\n return mapping.agentId\n }\n }\n }\n\n return undefined\n }\n\n /**\n * Resolve which agent handles a project based on routes.\n */\n private resolveAgent(project: string): string | undefined {\n for (const route of this.config.routes) {\n if (route.project === project || route.project === \"*\") {\n return route.agent\n }\n }\n return undefined\n }\n\n private async readBody(req: HttpRequest): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try { resolve(body ? JSON.parse(body) : {}) }\n catch { resolve({ raw: body }) }\n })\n req.on(\"error\", () => resolve({}))\n })\n }\n}\n","// --- Structured logger for the daemon ---\n// Outputs JSON lines to stderr for machine parsing, human-readable to stdout.\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\"\n\ninterface LogEntry {\n time: string\n level: LogLevel\n module: string\n msg: string\n [key: string]: unknown\n}\n\nexport class Logger {\n private module: string\n private minLevel: LogLevel\n private static levelOrder: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\n constructor(module: string, minLevel: LogLevel = \"info\") {\n this.module = module\n this.minLevel = minLevel\n }\n\n child(module: string): Logger {\n return new Logger(`${this.module}:${module}`, this.minLevel)\n }\n\n debug(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"debug\", msg, data)\n }\n\n info(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"info\", msg, data)\n }\n\n warn(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"warn\", msg, data)\n }\n\n error(msg: string, data?: Record<string, unknown>): void {\n this.emit(\"error\", msg, data)\n }\n\n /**\n * Create a console.error-compatible function for backward compat.\n */\n asConsoleLog(): (...args: unknown[]) => void {\n return (...args: unknown[]) => {\n const msg = args.map(a => typeof a === \"string\" ? a : JSON.stringify(a)).join(\" \")\n // Strip [agentx] prefix if present\n const clean = msg.replace(/^\\[agentx\\]\\s*/, \"\")\n if (clean) this.info(clean)\n }\n }\n\n private emit(level: LogLevel, msg: string, data?: Record<string, unknown>): void {\n if (Logger.levelOrder[level] < Logger.levelOrder[this.minLevel]) return\n\n const entry: LogEntry = {\n time: new Date().toISOString(),\n level,\n module: this.module,\n msg,\n ...data,\n }\n\n // Human-readable to stderr (what the user sees)\n const prefix = level === \"error\" ? \"ERROR\" : level === \"warn\" ? \"WARN\" : \"\"\n const tag = `[${this.module}]`\n const display = prefix ? `${tag} ${prefix}: ${msg}` : `${tag} ${msg}`\n console.error(display)\n }\n}\n","import type { IncomingMessage, ServerResponse } from \"http\"\nimport type { AgentRegistry } from \"@/agents/registry\"\n\n// --- Webhook handler ---\n// Routes incoming webhooks from GitLab, GitHub, Stripe, Sentry, etc.\n// to the appropriate agent with parsed payload context.\n//\n// Endpoints:\n// POST /webhook/:agentId — route to specific agent\n// POST /webhook/:agentId/:source — route with source hint (gitlab, github, etc.)\n//\n// The webhook payload is parsed and a human-readable summary is built\n// so the agent doesn't need to parse raw JSON.\n\ninterface WebhookConfig {\n /** Secret for validating webhook signatures (optional) */\n secrets?: Record<string, string> // source -> secret\n}\n\nexport class WebhookHandler {\n private registry: AgentRegistry\n private config: WebhookConfig\n private log: (...args: unknown[]) => void\n\n constructor(\n registry: AgentRegistry,\n config: WebhookConfig = {},\n log: (...args: unknown[]) => void = console.error.bind(console, \"[webhook]\"),\n ) {\n this.registry = registry\n this.config = config\n this.log = log\n }\n\n /**\n * Handle an incoming webhook request.\n * URL format: /webhook/:agentId or /webhook/:agentId/:source\n */\n async handle(req: IncomingMessage, res: ServerResponse, path: string): Promise<void> {\n // Parse path: /webhook/atlas or /webhook/atlas/gitlab\n const parts = path.replace(/^\\/webhook\\/?/, \"\").split(\"/\").filter(Boolean)\n const agentId = parts[0]\n const sourceHint = parts[1] // optional: \"gitlab\", \"github\", \"stripe\", \"sentry\"\n\n if (!agentId) {\n this.sendJson(res, 400, { error: \"Missing agent ID. Use /webhook/:agentId\" })\n return\n }\n\n // Read body\n const body = await this.readBody(req)\n\n // Detect source from headers if not in URL\n const source = sourceHint || this.detectSource(req.headers)\n\n // Parse the webhook payload into a human-readable message\n const summary = this.buildSummary(source, body, req.headers)\n\n this.log(`Webhook [${source}] -> ${agentId}: ${summary.slice(0, 100)}`)\n\n // Execute on the agent\n try {\n const response = await this.registry.execute({\n message: summary,\n agentId,\n context: {\n channel: `webhook:${source}`,\n sender: `webhook:${source}`,\n },\n })\n\n this.sendJson(res, response.error ? 500 : 200, {\n ok: !response.error,\n agent: agentId,\n source,\n response: response.content?.slice(0, 500),\n error: response.error,\n duration: response.duration,\n })\n } catch (e: any) {\n this.sendJson(res, 500, { error: e.message })\n }\n }\n\n /**\n * Detect webhook source from request headers.\n */\n private detectSource(headers: Record<string, string | string[] | undefined>): string {\n if (headers[\"x-gitlab-event\"] || headers[\"x-gitlab-token\"]) return \"gitlab\"\n if (headers[\"x-github-event\"]) return \"github\"\n if (headers[\"stripe-signature\"]) return \"stripe\"\n if (headers[\"sentry-hook-resource\"]) return \"sentry\"\n if (headers[\"x-vercel-signature\"]) return \"vercel\"\n if (headers[\"x-hub-signature-256\"]) return \"github\"\n return \"unknown\"\n }\n\n /**\n * Build a human-readable summary from the webhook payload.\n * Agent reads this instead of raw JSON.\n */\n private buildSummary(source: string, body: Record<string, unknown>, headers: Record<string, string | string[] | undefined>): string {\n const lines: string[] = [`[Webhook from ${source}]`]\n\n switch (source) {\n case \"gitlab\": {\n const event = headers[\"x-gitlab-event\"] as string || body.object_kind as string || \"event\"\n const project = (body.project as any)?.path_with_namespace || \"\"\n const user = (body.user as any)?.name || (body.user_username as string) || \"\"\n\n lines.push(`Event: ${event}`)\n if (project) lines.push(`Project: ${project}`)\n if (user) lines.push(`User: ${user}`)\n\n // Push events\n if (body.ref) lines.push(`Ref: ${body.ref}`)\n if (body.commits && Array.isArray(body.commits)) {\n lines.push(`Commits: ${body.commits.length}`)\n for (const c of (body.commits as any[]).slice(0, 3)) {\n lines.push(` - ${c.message?.split(\"\\n\")[0] || \"no message\"} (${c.author?.name || \"\"})`)\n }\n }\n\n // MR events\n const mr = body.object_attributes as any\n if (mr?.title) {\n lines.push(`Title: ${mr.title}`)\n lines.push(`State: ${mr.state || \"\"}`)\n lines.push(`Action: ${mr.action || \"\"}`)\n if (mr.source_branch) lines.push(`Branch: ${mr.source_branch} -> ${mr.target_branch}`)\n if (mr.url) lines.push(`URL: ${mr.url}`)\n }\n\n // Issue events\n if (mr?.iid && !mr?.source_branch) {\n lines.push(`Issue #${mr.iid}: ${mr.title || \"\"}`)\n if (mr.description) lines.push(`Description: ${mr.description.slice(0, 200)}`)\n }\n\n // Pipeline events\n if (body.object_kind === \"pipeline\") {\n const attrs = body.object_attributes as any\n lines.push(`Pipeline: ${attrs?.status || \"\"} (${attrs?.ref || \"\"})`)\n lines.push(`Duration: ${attrs?.duration || 0}s`)\n }\n break\n }\n\n case \"github\": {\n const event = headers[\"x-github-event\"] as string || \"event\"\n const repo = (body.repository as any)?.full_name || \"\"\n const sender = (body.sender as any)?.login || \"\"\n\n lines.push(`Event: ${event}`)\n if (repo) lines.push(`Repository: ${repo}`)\n if (sender) lines.push(`Sender: ${sender}`)\n\n // Push\n if (body.ref) lines.push(`Ref: ${body.ref}`)\n if (body.commits && Array.isArray(body.commits)) {\n for (const c of (body.commits as any[]).slice(0, 3)) {\n lines.push(` - ${c.message?.split(\"\\n\")[0] || \"\"} (${c.author?.name || \"\"})`)\n }\n }\n\n // PR\n const pr = body.pull_request as any\n if (pr) {\n lines.push(`PR #${pr.number}: ${pr.title}`)\n lines.push(`Action: ${body.action}`)\n lines.push(`Branch: ${pr.head?.ref} -> ${pr.base?.ref}`)\n }\n\n // Issue\n const issue = body.issue as any\n if (issue) {\n lines.push(`Issue #${issue.number}: ${issue.title}`)\n lines.push(`Action: ${body.action}`)\n }\n break\n }\n\n case \"stripe\": {\n const type = body.type as string || \"event\"\n const data = (body.data as any)?.object || {}\n\n lines.push(`Event: ${type}`)\n if (data.amount) lines.push(`Amount: ${(data.amount / 100).toFixed(2)} ${data.currency?.toUpperCase() || \"\"}`)\n if (data.customer_email) lines.push(`Customer: ${data.customer_email}`)\n if (data.description) lines.push(`Description: ${data.description}`)\n if (data.status) lines.push(`Status: ${data.status}`)\n break\n }\n\n case \"sentry\": {\n const resource = headers[\"sentry-hook-resource\"] as string || \"event\"\n lines.push(`Resource: ${resource}`)\n\n const data = body.data as any || body\n if (data.error?.title) lines.push(`Error: ${data.error.title}`)\n if (data.error?.culprit) lines.push(`Culprit: ${data.error.culprit}`)\n if (data.error?.metadata?.value) lines.push(`Message: ${data.error.metadata.value}`)\n if (body.url) lines.push(`URL: ${body.url}`)\n break\n }\n\n default:\n lines.push(`Headers: ${JSON.stringify(Object.keys(headers).filter(k => k.startsWith(\"x-\")).slice(0, 5))}`)\n lines.push(`Payload keys: ${Object.keys(body).slice(0, 10).join(\", \")}`)\n // Include raw body summary for unknown sources\n const raw = JSON.stringify(body).slice(0, 500)\n lines.push(`Body: ${raw}`)\n }\n\n return lines.join(\"\\n\")\n }\n\n private async readBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n return new Promise((resolve) => {\n let body = \"\"\n req.on(\"data\", (chunk: Buffer) => (body += chunk.toString()))\n req.on(\"end\", () => {\n try { resolve(body ? JSON.parse(body) : {}) }\n catch { resolve({ raw: body }) }\n })\n req.on(\"error\", () => resolve({}))\n })\n }\n\n private sendJson(res: ServerResponse, status: number, data: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data, null, 2))\n }\n}\n","import path from \"path\"\nimport fs from \"fs-extra\"\nimport { type PackageJson } from \"type-fest\"\n\nexport function getPackageInfo() {\n const packageJsonPath = path.join(\"package.json\")\n\n return fs.readJSONSync(packageJsonPath) as PackageJson\n}\n"],"mappings":"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","names":["A2AClient","baseUrl","token","res","text","metadata","response","body","reader","decoder","buffer","done","value","lines","line","data","taskId","method","params","h","A2AMesh","config","log","peer","A2AClient","interval","results","name","state","healthy","p","timeout","controller","timer","card","e","peerName","text","agentId","agent","url","headers","res","data","skillId","a","z","readFileSync","existsSync","resolve","loadDotEnv","dir","envPath","content","line","trimmed","eqIdx","key","value","providerConfigSchema","agentConfigSchema","telegramAccountSchema","channelsConfigSchema","cronJobSchema","meshPeerSchema","meshConfigSchema","daemonConfigSchema","expandEnvVars","obj","_match","name","result","loadDaemonConfig","configPath","paths","raw","foundPath","p","parsed","e","expanded","issues","i","validateWorkspaces","config","warnings","id","agent","claudeDir","providerName","providerConfig","cron","account","execa","execFile","buildPrompt","agent","task","historyContext","parts","ctx","envLines","isGitLab","isTelegram","peer","handle","role","buildClaudeArgs","prompt","streaming","resumeSessionId","args","parseClaudeJsonOutput","stdout","data","executeClaudeCode","start","stderr","exitCode","resolve","reject","proc","error","parsed","executeClaudeCodeStreaming","onDelta","fullText","lineBuffer","chunk","lines","line","event","block","delta","resultText","result","executeSdk","apiKey","sdk","query","content","q","message","executeOrchestrator","generate","providerName","executeTask","providers","readFileSync","writeFileSync","existsSync","mkdirSync","readdirSync","statSync","resolve","join","relative","dirname","WikiStore","baseDir","log","article","agentId","entry","filename","filepath","frontmatter","k","v","filter","files","f","entries","file","content","fmMatch","fm","body","get","key","path","meta","existing","fullPath","s","r","t","raw","getArray","m","articles","filePath","relPath","query","maxResults","queryLower","results","score","titleLower","contentLower","occurrences","a","b","message","maxArticles","stopWords","words","w","articleScores","word","matches","maxChars","lines","totalChars","header","chunk","backlinks","links","link","target","aliases","index","md","byType","list","type","x","y","access","totalArticles","byAccess","byOwner","entryFiles","dir","callback","full","readFileSync","writeFileSync","mkdirSync","existsSync","resolve","MAX_HISTORY_CHARS","MAX_MESSAGES","SessionStore","baseDir","agentId","channel","chatId","day","key","safe","file","data","session","senderName","content","claudeSessionId","lines","msg","time","totalChars","sum","m","removed","RateLimiter","maxPerMinute","maxPerHour","agentId","now","timestamps","hourAgo","recent","t","minuteAgo","lastMinute","readFileSync","writeFileSync","existsSync","mkdirSync","resolve","TokenTracker","baseDir","agentId","messageLength","responseLength","duration","error","usage","agent","date","path","days","totalTasks","totalTokens","totalErrors","byAgent","id","existing","DEFAULT_CONFIG","CHARS_PER_TOKEN","buildAgentContext","input","config","layers","buildLayers","a","b","parts","totalChars","maxChars","layer","layerMaxChars","trimmed","remaining","budget","name","fallback","buildChannelLayer","buildScopeLayer","supportsDelgation","buildPeersLayer","intentTags","extractIntentTags","artifactLines","history","maxTokens","lines","rules","tags","peer","handle","role","message","lower","channel","AgentRegistry","config","log","SessionStore","WikiStore","RateLimiter","TokenTracker","id","def","text","lower","bestId","bestLen","state","mention","mentionLower","found","agentId","channel","peers","agent","m","task","onDelta","rateCheck","chatId","senderName","wikiArticles","wikiContext","resumeSessionId","sessionHistory","contextInput","historyContext","buildAgentContext","response","executeTask","entryId","error","s","days","readFileSync","writeFileSync","existsSync","mkdirSync","resolve","MAX_ENTRIES","MAX_CONTEXT_CHARS","GroupLog","baseDir","chatId","sender","text","log","lines","chars","i","entry","line","safe","path","data","entries","STREAM_EDIT_INTERVAL_MS","TYPING_INTERVAL_MS","_MessageRouter","registry","config","hooks","log","GroupLog","mesh","adapter","msg","name","hookResult","chatId","agentId","boundAccount","agentName","replyAccountId","typingTimer","canStream","sentMessageId","lastEditTime","onDelta","_delta","fullText","now","preview","groupContext","messageWithContext","response","errorText","responseText","sentResponseId","e","originalMsg","sourceAgentId","responseMessageId","depth","visited","id","def","m","targetAccountId","sourceAccountId","contextMessage","sentId","messageId","text","parseMode","accountId","emoji","sendTyping","textLower","directory","peer","skill","header","account","mentionAgent","MessageRouter","__publicField","escapeHtml","text","markdownToTelegramHtml","md","lines","result","inCodeBlock","codeBlockLang","codeBlockLines","inBlockquote","tableHeaders","inTable","i","line","code","langHint","content","convertInline","headerMatch","cells","c","parts","header","ulMatch","indent","olMatch","num","html","wrapFileRefsInCode","_m","label","url","FILE_EXT_TLDS","insideTag","part","TelegramAdapter","accounts","log","handler","entries","i","accountId","config","me","e","chatId","token","msg","maxLen","text","formatted","markdownToTelegramHtml","params","result","messageId","parseMode","trimmed","emoji","consecutiveErrors","offset","updates","update","mediaInfo","hasPhoto","hasVoice","hasAudio","hasVideo","hasDocument","fileId","mime","filePath","fileUrl","res","buffer","ext","mkdirSync","writeFileSync","randomUUID","resolve","join","mediaDir","fileName","localPath","incoming","backoff","r","method","url","mkdirSync","writeFileSync","resolve","join","randomUUID","WhatsAppAdapter","config","log","senderPhone","groupName","groupJid","route","normalized","pattern","handler","makeWASocket","useMultiFileAuthState","DisconnectReason","baileys","state","saveCreds","version","v","silentLogger","args","data","update","connection","lastDisconnect","qr","qrcode","statusCode","m","msg","jidShort","hasText","me","remoteJid","myJid","myLid","chatUser","text","msgContent","hasImage","hasAudio","hasVideo","hasDocument","hasSticker","hasMedia","jid","isGroup","chatPhone","p","senderName","resolvedAgent","mediaInfo","buffer","mime","ext","mediaDir","fileName","filePath","e","incoming","sentId","chatId","messageId","emoji","writeFileSync","mkdirSync","existsSync","resolve","parseCronField","field","min","max","values","part","i","range","stepStr","step","start","a","b","getNextCronDate","expression","after","timezone","fields","minutes","hours","daysOfMonth","months","daysOfWeek","fmt","candidate","limit","parts","get","type","p","m","h","dom","mon","dow","CronScheduler","config","registry","hooks","log","id","def","job","j","timer","jobId","nextRun","delay","e","hookResult","startedAt","response","result","runDir","filename","createServer","writeFileSync","existsSync","unlinkSync","mkdirSync","resolve","dirname","DiscordAdapter","config","log","handler","Discord","Client","GatewayIntentBits","msg","isMentioned","isDM","text","incoming","e","channel","chatId","messageId","emoji","createServer","GitLabAdapter","config","log","handler","data","req","res","msg","parts","iid","noteableType","project","encodedProject","endpoint","text","noteId","e","event","objectKind","note","user","noteableIid","noteableTitle","agentId","chatId","incoming","attrs","lower","mentions","m","mapping","username","keyword","route","resolve","body","chunk","_Logger","module","minLevel","msg","data","args","clean","a","level","entry","prefix","tag","display","Logger","__publicField","WebhookHandler","registry","config","log","req","res","path","parts","agentId","sourceHint","body","source","summary","response","e","headers","lines","event","project","user","c","mr","attrs","repo","sender","pr","issue","type","data","resource","k","raw","resolve","chunk","status","AgentXDaemon","configPath","logger","Logger","loadDaemonConfig","warnings","validateWorkspaces","w","HookRegistry","loadHooks","AgentRegistry","MessageRouter","WebhookHandler","CronScheduler","A2AMesh","agent","cronJobs","job","status","peer","pidFile","resolve","mkdirSync","dirname","writeFileSync","err","reason","stopping","shutdown","signal","start","r","e","existsSync","unlinkSync","accounts","telegram","TelegramAdapter","whatsapp","WhatsAppAdapter","discord","DiscordAdapter","gitlab","GitLabAdapter","host","portStr","port","createServer","req","res","path","j","body","readBody","response","result","a","agentId","messages","lastUserMsg","m","historyLines","contextPrefix","requestId","content","chunk","tokens","data","reject","path","fs","getPackageInfo","packageJsonPath"]}
|