agentix-cli 0.16.0 → 0.18.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/dist/agent-RDVGL2TW.js +2 -0
- package/dist/audit-Z47AQFFD.js +3 -0
- package/dist/audit-Z47AQFFD.js.map +1 -0
- package/dist/board-dashboard-KX2CNOSY.js +7282 -0
- package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
- package/dist/chunk-53NRX2NA.js +2 -0
- package/dist/chunk-53NRX2NA.js.map +1 -0
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-ASSJ4Z6Z.js +164 -0
- package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
- package/dist/chunk-BLUXL6HR.js +854 -0
- package/dist/chunk-BLUXL6HR.js.map +1 -0
- package/dist/chunk-BTAEYL3P.js +72 -0
- package/dist/chunk-BTAEYL3P.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/chunk-DJEZ2HVU.js +5 -0
- package/dist/chunk-DJEZ2HVU.js.map +1 -0
- package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
- package/dist/chunk-DSCMCVTV.js.map +1 -0
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HPDHNIA3.js +2 -0
- package/dist/chunk-IM376SD6.js +13 -0
- package/dist/chunk-IM376SD6.js.map +1 -0
- package/dist/chunk-J4WKMKTP.js +5 -0
- package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
- package/dist/chunk-JGXE2YNV.js +46 -0
- package/dist/chunk-JGXE2YNV.js.map +1 -0
- package/dist/chunk-JXEPJ3YD.js +118 -0
- package/dist/chunk-JXEPJ3YD.js.map +1 -0
- package/dist/chunk-LY7JGDV7.js +6 -0
- package/dist/chunk-LY7JGDV7.js.map +1 -0
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/chunk-SR3X25TN.js +3 -0
- package/dist/chunk-SR3X25TN.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
- package/dist/chunk-U364GNQX.js +719 -0
- package/dist/chunk-U364GNQX.js.map +1 -0
- package/dist/chunk-VJUTQAPJ.js +2 -0
- package/dist/chunk-VJUTQAPJ.js.map +1 -0
- package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
- package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -320
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
- package/dist/compaction-S5H6DTOR.js.map +1 -0
- package/dist/config-UOHEG4BW.js +2 -0
- package/dist/config-mutator-KSEBWKJH.js +2 -0
- package/dist/context-planner-2RGHJHSO.js +17 -0
- package/dist/context-planner-2RGHJHSO.js.map +1 -0
- package/dist/debug-TGAP7E7W.js +2 -0
- package/dist/discover-OATU7SHI.js +4 -0
- package/dist/discover-OATU7SHI.js.map +1 -0
- package/dist/editor-chat-FUBQLMAQ.js +137 -0
- package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
- package/dist/heal-YP7QI7D3.js +2 -0
- package/dist/inbox-O5UB3YQN.js +315 -0
- package/dist/inbox-O5UB3YQN.js.map +1 -0
- package/dist/index.d.ts +3390 -1814
- package/dist/index.js +23 -53
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
- package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
- package/dist/loader-BGWA7IRY.js +2 -0
- package/dist/loader-LBHKSXBK.js +2 -0
- package/dist/memory-extract-KKPKAOPH.js +2 -0
- package/dist/processes-7W27G3J3.js +256 -0
- package/dist/processes-7W27G3J3.js.map +1 -0
- package/dist/program-2MDELQLX.js +2 -0
- package/dist/providers-WCQSKTNP.js +2 -0
- package/dist/query-FZAL7AQD.js +2 -0
- package/dist/recipes-LZ56TA3X.js +2 -0
- package/dist/registry-ONJSILH4.js +2 -0
- package/dist/scheduler-XRM2RJIV.js +2 -0
- package/dist/slack-D27BE37Q.js +3 -0
- package/dist/slack-D27BE37Q.js.map +1 -0
- package/dist/store-4UCO56K2.js +2 -0
- package/dist/store-4UCO56K2.js.map +1 -0
- package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
- package/dist/task-store-3NUWLYRY.js +2 -0
- package/dist/task-store-3NUWLYRY.js.map +1 -0
- package/dist/telegram-UWV67KWG.js +3 -0
- package/dist/telegram-UWV67KWG.js.map +1 -0
- package/dist/timers-QB4KYNAE.js +2 -0
- package/dist/timers-QB4KYNAE.js.map +1 -0
- package/dist/types-ECMEC5BZ.js +2 -0
- package/dist/types-ECMEC5BZ.js.map +1 -0
- package/dist/types-TTB6DJUE.js +2 -0
- package/dist/types-TTB6DJUE.js.map +1 -0
- package/dist/usage-dashboard-FQGPPABD.js +803 -0
- package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1450 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-KJ2NRKKQ.js +3 -0
- package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
- package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
- package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
- package/dist/wiki-OUYKM7MB.js.map +1 -0
- package/dist/workspace-setup-6GDBEORT.js +2 -0
- package/dist/workspace-setup-6GDBEORT.js.map +1 -0
- package/package.json +19 -2
- package/dist/agent-BCDPSPKU.js +0 -2
- package/dist/board-dashboard-LA7WW56M.js +0 -5668
- package/dist/board-dashboard-LA7WW56M.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-7FJQGD6U.js +0 -6
- package/dist/chunk-7FJQGD6U.js.map +0 -1
- package/dist/chunk-ABDP7TDL.js +0 -71
- package/dist/chunk-ABDP7TDL.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-FIOB3MEX.js +0 -2
- package/dist/chunk-FLOGK2U7.js.map +0 -1
- package/dist/chunk-HVC6FJNP.js +0 -108
- package/dist/chunk-HVC6FJNP.js.map +0 -1
- package/dist/chunk-K7BE4E35.js +0 -138
- package/dist/chunk-K7BE4E35.js.map +0 -1
- package/dist/chunk-KXJSLLSQ.js +0 -199
- package/dist/chunk-KXJSLLSQ.js.map +0 -1
- package/dist/chunk-KXZMYLHQ.js +0 -46
- package/dist/chunk-KXZMYLHQ.js.map +0 -1
- package/dist/chunk-M7HKBG3V.js +0 -2
- package/dist/chunk-M7HKBG3V.js.map +0 -1
- package/dist/chunk-MCJUAE6V.js +0 -6
- package/dist/chunk-MCJUAE6V.js.map +0 -1
- package/dist/chunk-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/chunk-YI2T5QYB.js +0 -5
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-LN6JEHMF.js +0 -2
- package/dist/config-mutator-24TH7PHM.js +0 -2
- package/dist/debug-3MEUO2P2.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/loader-PTCXLJEX.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/query-PKGYE5HR.js +0 -2
- package/dist/registry-ASC2QS5K.js +0 -2
- package/dist/scheduler-A3TQOCKT.js +0 -2
- package/dist/types-OIFU3KSQ.js +0 -2
- package/dist/usage-dashboard-QX3CF6KK.js +0 -404
- package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
- package/dist/workspace-setup-ZWTT42XT.js +0 -2
- /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
- /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
- /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
- /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
- /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
- /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
- /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
- /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
- /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
- /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
- /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
- /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agents/runtime.ts","../src/agents/error-map.ts","../src/agents/sessions.ts","../src/daemon/rate-limit.ts","../src/agents/context.ts","../src/agents/memory-store.ts","../src/agents/message-queue.ts","../src/agents/patterns.ts","../src/agents/registry.ts"],"sourcesContent":["import { execa } from \"execa\"\nimport { execFile, spawn } from \"child_process\"\nimport { friendlyModelError, renderFriendlyError } from \"./error-map\"\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. \"@my_bot\"\n role?: string // from systemPrompt, first line\n}\n\nexport interface AgentTask {\n message: string\n agentId: string\n /** Per-invocation model override (e.g. cron model). Falls back to agent.model. */\n model?: string\n /** Cacheable text delivered to Claude via --append-system-prompt. Typically\n * agent.systemPrompt + SOUL/IDENTITY/AGENTS.md. Passing stable content\n * here (instead of in the user-message body) keeps it inside Claude's\n * cached system prompt across session resumes. */\n systemPromptAppend?: string\n context?: {\n channel?: string\n sender?: string\n /** Platform user ID (e.g. Telegram user ID) */\n senderId?: string\n /** Platform username (e.g. @username) */\n senderUsername?: string\n group?: string\n /** Stable chat ID for session keying (e.g. \"project:issue:123\" for GitLab) */\n chatId?: 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 /** Verified channel metadata (from adapter) */\n channelMeta?: {\n agents?: Array<{ id: string; name: string; handle?: string }>\n project?: string\n issue?: { type: string; iid: string; title: string }\n facts?: string[]\n }\n }\n}\n\nexport interface AgentResponse {\n content: string\n error?: string\n tokensUsed?: number\n duration?: number\n claudeSessionId?: string\n usage?: TokenUsage // Real token counts from Claude's JSON output\n /** The model Claude actually billed for (from the CLI's init event). When\n * absent, cost reporting should fall back to the model override / agent\n * config. Knowing the billed model is what makes cache-aware pricing\n * trustworthy — a task that ran sonnet but was logged as opus would\n * overstate cost by ~5×. */\n billedModel?: string\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 // NOTE: agent.systemPrompt is no longer injected into the user-message body —\n // it's now delivered via `--append-system-prompt` so Claude's prompt cache\n // retains it across turns instead of us paying cache-create for it on every\n // new session. If a per-task override is needed, use task.context.systemPrompt\n // (which buildClaudeArgs also forwards to --append-system-prompt).\n\n // Inject environment context — skip if historyContext is provided (context engine handles it)\n if (task.context && !historyContext) {\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 if (isGitLab) {\n envLines.push(\"\")\n envLines.push(\"[IMPORTANT: You are responding to a GitLab comment/event]\")\n envLines.push(\"- Keep it short — 3-5 lines for the main message. Humans scan, not read\")\n envLines.push(\"- Lead with the result or action, not a narration of what you plan to do\")\n envLines.push(\"- Use <details><summary>Details</summary>\\\\n\\\\ncontent\\\\n</details> for verbose output (logs, full commands, step-by-step)\")\n envLines.push(\"- Do NOT mention Telegram handles — 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 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 modelOverride?: string,\n /** Static per-agent preamble delivered via --append-system-prompt. Content\n * here is Claude-cached across turns as part of the system prompt, so\n * moving stable content (agent.systemPrompt + SOUL/IDENTITY/AGENTS.md) out\n * of the user-message body and into this arg avoids paying cache-create\n * for it on every new session. */\n systemPromptAppend?: 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 const model = modelOverride || agent.model\n if (model) {\n args.push(\"--model\", model)\n }\n\n if (agent.permissionMode === \"bypassPermissions\") {\n args.push(\"--dangerously-skip-permissions\")\n }\n\n if (systemPromptAppend && systemPromptAppend.trim().length > 0) {\n args.push(\"--append-system-prompt\", systemPromptAppend)\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 */\nexport interface TokenUsage {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n}\n\n/**\n * When the Claude Code CLI returns exit=0 but the run itself errored (e.g.\n * out-of-credits mid-session), it emits a top-level JSON object with\n * `is_error: true` and the raw API error in `result`. Detect + extract so we\n * can run the same friendly-error translator we use for stderr.\n * Returns null when the output is a normal success.\n */\nfunction extractClaudeIsError(stdout: string): string | null {\n try {\n const data = JSON.parse(stdout)\n if (data && data.is_error && typeof data.result === \"string\") return data.result\n if (data && data.error && typeof data.error === \"string\") return data.error\n } catch { /* not JSON — fall through */ }\n return null\n}\n\nfunction parseClaudeJsonOutput(stdout: string): { text: string; sessionId?: string; usage?: TokenUsage; billedModel?: string } {\n try {\n const data = JSON.parse(stdout)\n const usage = data.usage ? {\n inputTokens: data.usage.input_tokens || 0,\n outputTokens: data.usage.output_tokens || 0,\n cacheReadTokens: data.usage.cache_read_input_tokens || 0,\n cacheCreateTokens: data.usage.cache_creation_input_tokens || 0,\n } : undefined\n\n // Claude Code's --output-format json response carries the actual billed\n // model at `model` (or nested under `message.model` depending on CLI ver).\n const billedModel: string | undefined =\n (typeof data.model === \"string\" && data.model) ||\n (typeof data.message?.model === \"string\" && data.message.model) ||\n undefined\n\n return {\n text: data.result || data.content || \"\",\n sessionId: data.session_id,\n usage,\n billedModel,\n }\n } catch {\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 // Always inject context (landscape, rules, channel info) even on resume.\n // Claude CLI --resume carries its own conversation history, but the\n // landscape + rules must be fresh so the agent sees capability updates.\n const prompt = buildPrompt(agent, task, historyContext)\n const args = buildClaudeArgs(agent, prompt, false, resumeSessionId, task.model, task.systemPromptAppend)\n\n try {\n const timeoutMs = Math.max(60_000, (agent.maxExecutionMinutes ?? 20) * 60_000)\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: timeoutMs,\n maxBuffer: 10 * 1024 * 1024,\n env: { ...process.env, HOME: process.env.HOME || \"/home/\" + (process.env.USER || \"user\") },\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 // Close stdin immediately. Claude CLI otherwise waits 3s for input then\n // proceeds with a warning; in cron retries that cascade invalidates the\n // prompt cache between attempts (5× input cost on cache-create).\n proc.stdin?.end()\n })\n\n if (!stdout && exitCode !== 0) {\n // exit 143 = 128+SIGTERM → our own `timeout` killed the process. Surface\n // that as a recognizable \"timed out after Xm\" message so operators can\n // bump agent.maxExecutionMinutes instead of guessing at an opaque 143.\n let errMsg: string\n if (exitCode === 143) {\n errMsg = `Claude Code timed out after ${Math.round(timeoutMs / 60_000)}m (SIGTERM). Bump agent.maxExecutionMinutes for \"${agent.name || \"this agent\"}\" if tasks need longer.`\n } else {\n errMsg = stderr?.trim() || `Claude Code exited with code ${exitCode}`\n }\n return {\n content: \"\",\n error: renderFriendlyError(friendlyModelError(errMsg)),\n duration: Date.now() - start,\n }\n }\n\n // Claude Code sometimes exits 0 but embeds the API error in stdout's\n // \"result\" field when `is_error` is set. Translate that too.\n const apiErrorInStdout = extractClaudeIsError(stdout)\n if (apiErrorInStdout) {\n return {\n content: \"\",\n error: renderFriendlyError(friendlyModelError(apiErrorInStdout)),\n duration: Date.now() - start,\n }\n }\n\n const parsed = parseClaudeJsonOutput(stdout)\n\n return {\n content: parsed.text,\n duration: Date.now() - start,\n claudeSessionId: parsed.sessionId,\n usage: parsed.usage,\n billedModel: parsed.billedModel,\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 onEvent?: (event: any) => void,\n): Promise<AgentResponse> {\n const start = Date.now()\n // Always inject context (landscape, rules, channel info) even on resume.\n // Claude CLI --resume carries its own conversation history, but the\n // landscape + rules must be fresh so the agent sees capability updates.\n const prompt = buildPrompt(agent, task, historyContext)\n const args = buildClaudeArgs(agent, prompt, true, resumeSessionId, task.model, task.systemPromptAppend)\n\n let fullText = \"\"\n // Capture model + usage + session id from the stream events — Claude Code\n // emits a system-init event up front (with the billed model) and a terminal\n // result event with the full usage accounting. Without these we can't\n // attribute cost correctly for streamed tasks.\n let streamBilledModel: string | undefined\n let streamUsage: TokenUsage | undefined\n let streamSessionId: string | undefined\n /** If the terminal `result` event carries is_error, we stash it here and\n * surface the translated message instead of treating `result` as agent text. */\n let streamApiError: string | undefined\n\n try {\n const streamTimeoutMs = Math.max(60_000, (agent.maxExecutionMinutes ?? 20) * 60_000)\n const proc = execa(\"claude\", args, {\n cwd: agent.workspace,\n timeout: streamTimeoutMs,\n reject: false,\n env: process.env,\n // Don't buffer — we'll read stdout line by line\n buffer: false,\n // Close stdin so Claude CLI doesn't wait 3s for input (see executeClaudeCode).\n stdin: \"ignore\",\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 // Surface every parsed event for observability (dashboard streaming).\n // Best-effort — never let a subscriber crash the runtime.\n if (onEvent) {\n try { onEvent(event) } catch { /* */ }\n }\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 (or an API error, when is_error).\n if (event.type === \"result\" && event.result) {\n if (event.is_error && typeof event.result === \"string\") {\n streamApiError = event.result\n } else {\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 // Final event also carries the authoritative usage + model + session.\n if (event.usage) {\n streamUsage = {\n inputTokens: event.usage.input_tokens || 0,\n outputTokens: event.usage.output_tokens || 0,\n cacheReadTokens: event.usage.cache_read_input_tokens || 0,\n cacheCreateTokens: event.usage.cache_creation_input_tokens || 0,\n }\n }\n if (typeof event.model === \"string\") streamBilledModel = event.model\n if (typeof event.session_id === \"string\") streamSessionId = event.session_id\n }\n\n // System-init event (first thing the CLI emits) carries the model\n // it's about to invoke — capture early so we have it even if the\n // run errors before the terminal result.\n if (event.type === \"system\" && event.subtype === \"init\") {\n if (typeof event.model === \"string\") streamBilledModel = event.model\n if (typeof event.session_id === \"string\") streamSessionId = event.session_id\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: renderFriendlyError(friendlyModelError(stderr || `Claude Code exited with code ${result.exitCode}`)),\n duration: Date.now() - start,\n }\n }\n\n if (streamApiError) {\n return {\n content: \"\",\n error: renderFriendlyError(friendlyModelError(streamApiError)),\n duration: Date.now() - start,\n usage: streamUsage,\n billedModel: streamBilledModel,\n claudeSessionId: streamSessionId,\n }\n }\n\n return {\n content: fullText,\n duration: Date.now() - start,\n usage: streamUsage,\n billedModel: streamBilledModel,\n claudeSessionId: streamSessionId,\n }\n } catch (error: any) {\n return {\n content: fullText || \"\",\n error: renderFriendlyError(friendlyModelError(error.message)),\n duration: Date.now() - start,\n usage: streamUsage,\n billedModel: streamBilledModel,\n claudeSessionId: streamSessionId,\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 historyContext?: 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 // Build prompt with full context (landscape, memory, patterns, etc.)\n const prompt = buildPrompt(agent, task, historyContext)\n\n let content = \"\"\n\n const q = query({\n prompt,\n options: {\n model: task.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 historyContext?: 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 // Build full prompt with context (landscape, memory, patterns, etc.)\n const fullTask = historyContext\n ? `${historyContext}\\n\\n${task.message}`\n : task.message\n\n const result = await generate({\n task: fullTask,\n cwd: agent.workspace,\n provider: providerName as any,\n model: task.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 onEvent?: (event: any) => void,\n): Promise<AgentResponse> {\n switch (agent.tier) {\n case \"claude-code\":\n if (onDelta) {\n return executeClaudeCodeStreaming(agent, task, onDelta, historyContext, resumeSessionId, onEvent)\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, historyContext)\n }\n\n case \"orchestrator\": {\n const providerName = agent.provider || \"claude-code\"\n const apiKey = providers[providerName]?.apiKey\n return executeOrchestrator(agent, task, apiKey, historyContext)\n }\n\n default:\n return {\n content: \"\",\n error: `Unknown tier: ${agent.tier}`,\n }\n }\n}\n","// --- Translate Anthropic API / Claude Code errors into human-friendly text ---\n//\n// Raw errors from the Claude Code CLI look like:\n// API Error: 400 {\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\n// \"message\":\"You're out of extra usage. Add more at ...\"}}\n// These are painful to surface in a chat reply or the dashboard. This module\n// parses the structured bit and returns a short, actionable sentence.\n\nexport interface FriendlyError {\n /** One-line, operator-facing message suitable for a dashboard card or chat reply. */\n message: string\n /** Optional next step the user can take. */\n fix?: string\n /** Machine-readable error category for routing / retries. */\n kind:\n | \"out_of_credits\"\n | \"auth\"\n | \"permission\"\n | \"rate_limit\"\n | \"overloaded\"\n | \"context_too_large\"\n | \"invalid_request\"\n | \"upstream_api\"\n | \"timeout\"\n | \"unknown\"\n /** Whether this error is transient — a retry has a reasonable chance of succeeding. */\n retryable: boolean\n /** Source error text retained for logs / debugging. */\n raw?: string\n}\n\n/**\n * Best-effort translator. If we recognise the shape we return a structured\n * FriendlyError; otherwise we hand back the original text wrapped as \"unknown\".\n * Never throws — errors are data in here.\n */\nexport function friendlyModelError(raw: string | undefined | null): FriendlyError {\n const source = (raw || \"\").trim()\n if (!source) {\n return { message: \"Unknown error from the model.\", kind: \"unknown\", retryable: false }\n }\n\n // Timeouts are wrapped by us upstream; pass through.\n if (/timed out after/i.test(source)) {\n return { message: source.slice(0, 300), kind: \"timeout\", retryable: true, raw: source }\n }\n\n // Look for the standard Anthropic JSON envelope anywhere in the string —\n // the CLI prefixes it with \"API Error: <status>\".\n const jsonMatch = source.match(/\\{.*\\}\\s*$/s) || source.match(/\\{[\\s\\S]*\\}/)\n let parsed: any\n if (jsonMatch) {\n try { parsed = JSON.parse(jsonMatch[0]) } catch { /* raw text only */ }\n }\n\n const inner = parsed?.error || parsed\n const errType: string | undefined = typeof inner?.type === \"string\" ? inner.type : undefined\n const errMessage: string | undefined = typeof inner?.message === \"string\" ? inner.message : undefined\n\n const statusMatch = source.match(/API Error:\\s*(\\d{3})/i)\n const status = statusMatch ? parseInt(statusMatch[1], 10) : undefined\n\n // Credits exhausted — the specific case the user flagged.\n if (errMessage && /out of (extra )?usage|out of credit|credit(s)? exhausted|insufficient credit/i.test(errMessage)) {\n return {\n kind: \"out_of_credits\",\n retryable: false,\n message: \"The agent is out of Anthropic credits.\",\n fix: \"Top up at https://console.anthropic.com/settings/billing (or claude.ai/settings/usage for Max subscribers), then retry.\",\n raw: source,\n }\n }\n\n // Context-length issues.\n if (errMessage && /context|prompt is too long|too many tokens|maximum context length/i.test(errMessage)) {\n return {\n kind: \"context_too_large\",\n retryable: false,\n message: \"The conversation has grown past what the model can hold.\",\n fix: \"Clear session history or switch the agent to a bigger context model.\",\n raw: source,\n }\n }\n\n // Authentication & API key issues.\n if (errType === \"authentication_error\" || (errMessage && /authentication|invalid.*api.*key|no api key|not authenticated/i.test(errMessage))) {\n return {\n kind: \"auth\",\n retryable: false,\n message: \"Authentication failed talking to Anthropic.\",\n fix: \"Check ANTHROPIC_API_KEY in .env (or the Claude Code login for claude-code tier agents).\",\n raw: source,\n }\n }\n\n // Permission / forbidden.\n if (errType === \"permission_error\" || status === 403) {\n return {\n kind: \"permission\",\n retryable: false,\n message: errMessage || \"The API key doesn't have permission for this request.\",\n raw: source,\n }\n }\n\n // Rate limit from Anthropic's side.\n if (errType === \"rate_limit_error\" || status === 429) {\n return {\n kind: \"rate_limit\",\n retryable: true,\n message: \"Anthropic's API rate-limited this request.\",\n fix: \"AgentX will retry automatically. If it recurs, lower the agent's concurrency or spread scheduled jobs.\",\n raw: source,\n }\n }\n\n // Server-side overload.\n if (errType === \"overloaded_error\" || status === 529 || status === 503) {\n return {\n kind: \"overloaded\",\n retryable: true,\n message: \"Anthropic's API is temporarily overloaded.\",\n fix: \"AgentX will retry in a moment.\",\n raw: source,\n }\n }\n\n // Generic invalid request — surface the API message verbatim (truncated).\n if (errType === \"invalid_request_error\" || status === 400) {\n return {\n kind: \"invalid_request\",\n retryable: false,\n message: errMessage || \"The model rejected the request as invalid.\",\n raw: source,\n }\n }\n\n // Other Anthropic API errors (5xx catchall).\n if (status && status >= 500) {\n return {\n kind: \"upstream_api\",\n retryable: true,\n message: errMessage || `Anthropic API error (HTTP ${status}).`,\n fix: \"Transient — retry. Check https://status.anthropic.com if it persists.\",\n raw: source,\n }\n }\n\n // Fallback: unknown shape, keep the text short.\n return {\n kind: \"unknown\",\n retryable: false,\n message: source.replace(/\\s+/g, \" \").slice(0, 220),\n raw: source,\n }\n}\n\n/**\n * Render a FriendlyError back to a one-line string (primary message + fix),\n * which is what most call sites want when updating `response.error`.\n */\nexport function renderFriendlyError(f: FriendlyError): string {\n return f.fix ? `${f.message} — ${f.fix}` : f.message\n}\n","import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } 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/** Default stale timeout. Overridable via SessionStore options so each node\n * can trade cache-hit ratio (long timeout → prompt cache survives work\n * pauses) against \"fresh context\" (short timeout → new session rebuilds the\n * prompt from scratch). Agents on Opus pay ~$0.50 cache-create per task,\n * so the higher the timeout, the less often that cost recurs. */\nconst DEFAULT_STALE_SESSION_MINUTES = 120\n\n/** Session has a compacted summary prepended to its messages */\nconst COMPACTION_MARKER = \"[Compacted conversation summary\"\n\n/** Bucket an ISO timestamp into a stable 15-min window label (e.g. \"14:45\").\n * Used in history rendering so the prompt's bucket headers change at most\n * every 15 minutes — the prefix stays byte-stable across intra-bucket\n * messages and the server-side prompt cache is preserved. */\nfunction bucketLabel(iso: string): string {\n const m = iso.slice(11, 16) // HH:MM\n const hh = m.slice(0, 2)\n const mm = parseInt(m.slice(3, 5), 10)\n if (Number.isNaN(mm)) return m\n const qtr = Math.floor(mm / 15) * 15\n return `${hh}:${qtr.toString().padStart(2, \"0\")}`\n}\n\nexport class SessionStore {\n private sessionsDir: string\n private cache: Map<string, Session> = new Map()\n private staleMinutes: number\n\n constructor(baseDir: string = process.cwd(), opts: { staleMinutes?: number } = {}) {\n this.sessionsDir = resolve(baseDir, \".agentx/sessions\")\n if (!existsSync(this.sessionsDir)) {\n mkdirSync(this.sessionsDir, { recursive: true })\n }\n this.staleMinutes = Math.max(1, opts.staleMinutes ?? DEFAULT_STALE_SESSION_MINUTES)\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 // Bucket messages into 15-min windows. Stamping every line with its own\n // HH:MM broke the server-side prompt cache every turn (each new message\n // adds a never-seen timestamp near the tail). A bucket header changes\n // only every 15 min, so most turns within the window preserve cache.\n let currentBucket = \"\"\n for (const msg of session.messages) {\n const bucket = bucketLabel(msg.timestamp)\n if (bucket !== currentBucket) {\n lines.push(`— ${bucket} —`)\n currentBucket = bucket\n }\n const name = msg.name || (msg.role === \"user\" ? \"User\" : \"Agent\")\n lines.push(`${name}: ${msg.content}`)\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 * Check if a Claude session is stale (idle too long for --resume to be useful).\n * When stale, the resumed context will be too far behind — better to start fresh.\n */\n isSessionStale(agentId: string, channel: string, chatId: string): boolean {\n const session = this.getSession(agentId, channel, chatId)\n if (!session.claudeSessionId) return false\n const elapsed = Date.now() - new Date(session.updatedAt).getTime()\n return elapsed > this.staleMinutes * 60 * 1000\n }\n\n /**\n * Build a summary of recent messages from OTHER sessions for the same agent today.\n * This bridges the cross-chat amnesia gap — if someone shared info in a DM,\n * the group session gets a hint about it.\n */\n getCrossSessionSummary(agentId: string, channel: string, chatId: string): string {\n const day = new Date().toISOString().slice(0, 10)\n const currentKey = this.sessionKey(agentId, channel, chatId)\n\n try {\n const files = readdirSync(this.sessionsDir).filter(f =>\n f.startsWith(agentId.replace(/[^a-zA-Z0-9_:-]/g, \"_\")) &&\n f.includes(day) &&\n f.endsWith(\".json\")\n )\n\n const hints: string[] = []\n\n for (const file of files) {\n try {\n const data = JSON.parse(readFileSync(resolve(this.sessionsDir, file), \"utf-8\")) as Session\n if (data.id === currentKey) continue // skip own session\n if (data.messages.length === 0) continue\n\n // Take the last few messages as a hint\n const recent = data.messages.slice(-4)\n const isDM = !data.chatId.startsWith(\"-\") && /^\\d+$/.test(data.chatId)\n const scope = isDM ? \"DM\" : data.chatId\n const lines = recent.map(m => {\n const time = m.timestamp.slice(11, 16)\n return ` [${time}] ${m.name || m.role}: ${m.content.slice(0, 200)}`\n })\n hints.push(`From ${scope} (${data.channel}):\\n${lines.join(\"\\n\")}`)\n } catch {\n // Skip corrupted files\n }\n }\n\n if (hints.length === 0) return \"\"\n\n return [\n \"[Cross-chat context — recent activity from your other conversations today]\",\n ...hints,\n \"[End cross-chat context — the user in THIS chat may be referring to actions from above]\",\n \"\",\n ].join(\"\\n\")\n } catch {\n return \"\"\n }\n }\n\n /**\n * Check if session history needs compaction and compact if so.\n * Call this after adding messages. Returns true if compaction was performed.\n */\n async compactIfNeeded(\n agentId: string,\n channel: string,\n chatId: string,\n memoryStore?: import(\"./memory-store\").MemoryStore,\n ): Promise<{ compacted: boolean; qualityScore?: number; lostEntities?: string[]; drift?: import(\"./drift-detection\").DriftReport }> {\n const session = this.getSession(agentId, channel, chatId)\n const { needsCompaction, compactSession, applyCompaction } = await import(\"./compaction\")\n\n if (!needsCompaction(session.messages)) return { compacted: false }\n\n const result = await compactSession(session.messages, agentId, memoryStore)\n if (result.compactedCount === 0) return { compacted: false }\n\n session.messages = applyCompaction(session.messages, result)\n session.updatedAt = new Date().toISOString()\n\n // Clear Claude session ID — the compacted context needs a fresh session\n delete session.claudeSessionId\n\n this.save(session)\n return {\n compacted: true,\n qualityScore: result.qualityScore,\n lostEntities: result.lostEntities,\n drift: result.drift,\n }\n }\n\n /**\n * Check if current session already has a compacted summary.\n */\n hasCompaction(agentId: string, channel: string, chatId: string): boolean {\n const session = this.getSession(agentId, channel, chatId)\n return session.messages.length > 0 &&\n session.messages[0].content.startsWith(COMPACTION_MARKER)\n }\n\n /**\n * Clear the stored Claude session ID so next invocation starts fresh.\n */\n clearClaudeSessionId(agentId: string, channel: string, chatId: string): void {\n const session = this.getSession(agentId, channel, chatId)\n delete session.claudeSessionId\n session.updatedAt = new Date().toISOString()\n this.save(session)\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. `acquire()` queues callers when the window is\n// full instead of failing them outright — good for chatty channels where\n// dropping a message would look like the bot is broken.\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 right now. Does NOT wait. Call\n * `acquire()` if you want queueing behaviour.\n */\n check(agentId: string): { allowed: boolean; reason?: string; waitMs?: number } {\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 // Per-minute window.\n const minuteAgo = now - 60_000\n const inMinute = recent.filter((t) => t > minuteAgo)\n if (inMinute.length >= this.maxPerMinute) {\n const oldest = inMinute[0]\n const waitMs = Math.max(50, oldest + 60_000 - now)\n return {\n allowed: false,\n reason: `Rate limit: ${inMinute.length}/${this.maxPerMinute} per minute`,\n waitMs,\n }\n }\n\n // Per-hour window.\n if (recent.length >= this.maxPerHour) {\n const oldest = recent[0]\n const waitMs = Math.max(1000, oldest + 3600_000 - now)\n return {\n allowed: false,\n reason: `Rate limit: ${recent.length}/${this.maxPerHour} per hour`,\n waitMs,\n }\n }\n\n // Record this slot and allow.\n recent.push(now)\n this.windows.set(agentId, recent)\n return { allowed: true }\n }\n\n /**\n * Wait until a slot opens for this agent, then record + return ok. If the\n * caller exceeds `maxWaitMs`, give up with the last rate-limit reason. A\n * concurrent task that acquires a slot first will push THIS task's wake-up\n * further out — that's intended: FIFO-ish through the agent's normal\n * message queue, no starvation in practice.\n *\n * `onWait` fires once at the start of waiting so callers can surface the\n * \"queued\" state (logging, dashboard hint, etc).\n */\n async acquire(\n agentId: string,\n opts: { maxWaitMs?: number; onWait?: (reason: string, waitMs: number) => void } = {},\n ): Promise<{ ok: true } | { ok: false; reason: string }> {\n const maxWaitMs = opts.maxWaitMs ?? 5 * 60_000\n const deadline = Date.now() + maxWaitMs\n let firstWait = true\n\n while (true) {\n const r = this.check(agentId)\n if (r.allowed) return { ok: true }\n if (Date.now() >= deadline) {\n return { ok: false, reason: `${r.reason} (waited ${Math.round(maxWaitMs / 1000)}s, giving up)` }\n }\n if (firstWait) {\n firstWait = false\n opts.onWait?.(r.reason!, r.waitMs!)\n }\n // Cap the sleep so concurrent waiters re-check often enough to grab the\n // next slot in turn, rather than all firing at the same instant.\n const remaining = deadline - Date.now()\n const sleepMs = Math.min(r.waitMs ?? 1000, remaining, 5_000)\n await new Promise((resolve) => setTimeout(resolve, sleepMs))\n }\n }\n\n /**\n * Current usage for an agent — used by the dashboard and `agentx usage`.\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","// --- 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. Landscape — world model: team roster, channels, rules (cached at startup)\n// 4. Identity — who the agent is, what it can do\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: 6000,\n layerBudgets: {\n channel: 200,\n scope: 200,\n landscape: 800,\n identity: 200,\n bootstrap: 500,\n intent: 200,\n artifacts: 500,\n memory: 600,\n history: 1200,\n \"cross-chat\": 800,\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 // Bootstrap identity files (from workspace)\n bootstrapContext?: string // from buildBootstrapContext()\n\n // Behavioral patterns (self-improving loop)\n patternContext?: string // from PatternStore.buildContext()\n\n // Auto-injected skills (matched to current message)\n skillInjection?: string // from getAutoInjectSkills()\n\n // Scope layer\n groupName?: string\n projectPath?: string // \"org/my-project\"\n issueMR?: { type: string; iid: string; title: string }\n\n // Identity layer\n agentId: string\n agentName: string\n agentHandle?: string // \"@my_bot\"\n systemPrompt?: string\n\n // Participants\n sender: string\n senderId?: string // platform user ID (e.g. Telegram user ID)\n senderUsername?: string // platform username (e.g. @username)\n senderRole?: string // \"user\", \"agent:other-agent\"\n\n // Landscape (cached world model from LandscapeBuilder)\n landscape?: string\n\n // Channel meta (verified facts from the channel adapter — prevents hallucination)\n channelMeta?: {\n agents?: Array<{ id: string; name: string; handle?: string }>\n project?: string\n issue?: { type: string; iid: string; title: string }\n facts?: string[]\n }\n\n // Artifacts\n mediaPath?: string\n mediaType?: string\n replyToText?: string\n\n // Memory (persistent cross-session facts)\n memoryContext?: string // from MemoryStore.buildContext()\n\n // History\n groupHistory?: string // from GroupLog.buildContext()\n sessionHistory?: string // from SessionStore.buildHistoryContext()\n crossChatContext?: string // from SessionStore.getCrossSessionSummary()\n\n // Wiki\n wikiContext?: string // from WikiStore.buildContext()\n\n // Handover — one-shot note injected on the first message after an\n // operator-initiated handover takes effect. See channels/handover-store.\n handoverNote?: {\n fromAgent: string\n summary?: string\n at: string // ISO timestamp of the handover\n }\n\n // Intent (graph classification — when absent, Intent layer falls back\n // to the legacy regex tag extractor).\n intent?: {\n path: string[] // root → leaf node ids\n pathLabel: string // \"Business › Noqta › DevOps › Review MR\"\n pathId: string // hash — also carried as wiki `graph:<pathId>` tag\n axes?: Record<string, Record<string, string>>\n leaf?: { input?: string; output?: string }\n status: \"pending\" | \"approved\"\n }\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. Landscape (cached world model — team, channels, rules)\n if (input.landscape) {\n layers.push({\n name: \"landscape\",\n priority: 3,\n maxTokens: budget(\"landscape\", 350),\n content: input.landscape,\n tags: [\"landscape\", \"world-model\"],\n })\n }\n\n // 4. Identity (only first line of systemPrompt — agent already has CLAUDE.md)\n if (input.systemPrompt) {\n layers.push({\n name: \"identity\",\n priority: 4,\n maxTokens: budget(\"identity\", 200),\n content: input.systemPrompt.split(\"\\n\")[0],\n tags: [\"identity\", input.agentId],\n })\n }\n\n // 4.5 Bootstrap identity files (SOUL.md, IDENTITY.md, USER.md, AGENTS.md)\n if (input.bootstrapContext) {\n layers.push({\n name: \"bootstrap\",\n priority: 4.5,\n maxTokens: budget(\"bootstrap\", 500),\n content: input.bootstrapContext,\n tags: [\"bootstrap\", \"identity\", \"personality\"],\n })\n }\n\n // 5. Intent — prefer graph classification (hierarchical path) when the\n // caller ran the classifier. Fall back to the regex tag extractor\n // otherwise so legacy installs with graph.enabled=false behave as before.\n if (input.intent && input.intent.path.length > 0) {\n const lines: string[] = [\n `[Intent path: ${input.intent.pathLabel}${input.intent.status === \"pending\" ? \" (pending approval)\" : \"\"}]`,\n ]\n const leafId = input.intent.path[input.intent.path.length - 1]\n const leafAxes = input.intent.axes?.[leafId]\n if (leafAxes && Object.keys(leafAxes).length) {\n const kv = Object.entries(leafAxes)\n .map(([k, v]) => ` ${k}: ${v}`)\n .join(\"\\n\")\n lines.push(kv)\n }\n if (input.intent.leaf?.input) lines.push(` input: ${input.intent.leaf.input}`)\n layers.push({\n name: \"intent\",\n priority: 5,\n maxTokens: budget(\"intent\", 200),\n content: lines.join(\"\\n\"),\n tags: [\"intent\", ...input.intent.path],\n })\n } else {\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\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 // 6.2 Auto-injected skills (matched to current task)\n if (input.skillInjection) {\n layers.push({\n name: \"skills\",\n priority: 6.2,\n maxTokens: budget(\"skills\", 2000),\n content: input.skillInjection,\n tags: [\"skills\", \"auto-inject\"],\n })\n }\n\n // 6.3 Behavioral patterns (self-improving loop)\n if (input.patternContext) {\n layers.push({\n name: \"patterns\",\n priority: 6.3,\n maxTokens: budget(\"patterns\", 400),\n content: input.patternContext,\n tags: [\"patterns\", \"behavioral\", \"self-improving\"],\n })\n }\n\n // 6.5 Agent memory (persistent cross-session facts from Haiku extraction)\n if (input.memoryContext) {\n layers.push({\n name: \"memory\",\n priority: 6.5,\n maxTokens: budget(\"memory\", 600),\n content: input.memoryContext,\n tags: [\"memory\", \"persistent\"],\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 // 7b. Cross-chat context (bridges DM ↔ group amnesia)\n if (input.crossChatContext) {\n layers.push({\n name: \"cross-chat\",\n priority: 7,\n maxTokens: budget(\"cross-chat\", 800),\n content: input.crossChatContext,\n tags: [\"history\", \"cross-chat\"],\n })\n }\n\n // 7c. Handover note — one-shot briefing when this agent is taking over\n // a conversation from another agent. Rendered just before wiki.\n if (input.handoverNote) {\n const lines = [\n `[Handover] You are taking over this conversation from ${input.handoverNote.fromAgent}.`,\n ]\n if (input.handoverNote.summary) {\n lines.push(`Operator summary: ${input.handoverNote.summary}`)\n }\n lines.push(`Handover time: ${input.handoverNote.at}`)\n layers.push({\n name: \"handover\",\n priority: 7.5,\n maxTokens: budget(\"handover\", 400),\n content: lines.join(\"\\n\"),\n tags: [\"handover\", input.handoverNote.fromAgent],\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 if (input.senderId) lines.push(`Telegram user ID: ${input.senderId}`)\n if (input.senderUsername) lines.push(`Username: @${input.senderUsername}`)\n rules.push(\"Format responses using Telegram-compatible markdown\")\n rules.push(\"Be brief — 2-4 sentences max for the main point. Humans scan, not read\")\n rules.push(\"Lead with the action or answer, skip preamble\")\n break\n\n case \"whatsapp\":\n lines.push(`From: ${input.sender}`)\n rules.push(\"Be brief — 2-3 sentences max. WhatsApp is mobile-first\")\n rules.push(\"No rich formatting — plain text only\")\n rules.push(\"Lead with the action or answer, skip preamble\")\n break\n\n case \"gitlab\":\n lines.push(`From: ${input.sender}`)\n rules.push(\"Reply as a short, actionable GitLab comment — 3-5 lines for the main message\")\n rules.push(\"Do NOT mention Telegram handles — they don't work on GitLab\")\n rules.push(\"Do NOT delegate to other agents\")\n rules.push(\"Reference issues with #IID and merge requests with !IID\")\n rules.push(\"Put verbose details (logs, full commands, step-by-step) inside collapsible sections: <details><summary>Title</summary>\\\\n\\\\ncontent\\\\n</details>\")\n rules.push(\"Never narrate what you are about to do — just do it and report the result\")\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 rules.push(\"Be brief — 2-4 sentences for the main point\")\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 // Inject verified channel metadata (prevents hallucination)\n if (input.channelMeta) {\n const meta = input.channelMeta\n if (meta.agents?.length) {\n lines.push(`[Verified bots in this chat: ${meta.agents.map(a => a.handle || a.name || a.id).join(\", \")}]`)\n lines.push(\"Only mention these agents as group members — do NOT assume others are present.\")\n }\n if (meta.project) {\n lines.push(`Project: ${meta.project}`)\n }\n if (meta.issue) {\n lines.push(`${meta.issue.type} #${meta.issue.iid}${meta.issue.title ? `: ${meta.issue.title}` : \"\"}`)\n }\n if (meta.facts?.length) {\n for (const fact of meta.facts) {\n lines.push(`• ${fact}`)\n }\n }\n }\n\n return { name: \"scope\", priority: 2, maxTokens, content: lines.join(\"\\n\"), tags }\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 * Estimate token count from text.\n */\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / CHARS_PER_TOKEN)\n}\n","import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { buildIndexCached, scoreAll } from \"../memory/bm25\"\n\n// --- Persistent agent memory store ---\n// JSONL-based. One file per agent: .agentx/memory/{agentId}.jsonl\n// Extracted by Haiku post-conversation, injected pre-conversation.\n\nexport interface MemoryFact {\n id: string\n agentId: string\n category: \"fact\" | \"secret\" | \"preference\" | \"commitment\" | \"task-state\"\n content: string\n keywords: string[]\n source: { channel: string; chatId: string; sender: string; date: string }\n createdAt: string\n expiresAt?: string\n // Spaced repetition fields\n accessCount?: number\n lastAccessed?: string\n nextReview?: string\n /** Review interval in days — doubles on each successful recall */\n intervalDays?: number\n}\n\nconst MAX_MEMORIES = 200\nconst TASK_STATE_TTL_DAYS = 7\n\n\nexport class MemoryStore {\n private memoryDir: string\n\n constructor(baseDir: string = process.cwd()) {\n this.memoryDir = resolve(baseDir, \".agentx/memory\")\n if (!existsSync(this.memoryDir)) {\n mkdirSync(this.memoryDir, { recursive: true })\n }\n }\n\n private filePath(agentId: string): string {\n return resolve(this.memoryDir, `${agentId}.jsonl`)\n }\n\n addMemory(agentId: string, fact: Omit<MemoryFact, \"id\" | \"createdAt\">): void {\n const entry: MemoryFact = {\n ...fact,\n id: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,\n createdAt: new Date().toISOString(),\n }\n appendFileSync(this.filePath(agentId), JSON.stringify(entry) + \"\\n\")\n }\n\n getAll(agentId: string): MemoryFact[] {\n const file = this.filePath(agentId)\n if (!existsSync(file)) return []\n\n try {\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\").filter(l => l.trim())\n return lines.map(l => {\n try { return JSON.parse(l) as MemoryFact } catch { return null }\n }).filter((m): m is MemoryFact => m !== null)\n } catch {\n return []\n }\n }\n\n findRelevant(message: string, agentId: string, limit: number = 8): MemoryFact[] {\n const memories = this.getAll(agentId)\n if (memories.length === 0) return []\n\n // BM25 over keywords + content (cached)\n const docs = memories.map((m) => `${m.keywords.join(\" \")} ${m.content}`)\n const cachePath = resolve(this.memoryDir, `${agentId}_bm25_cache.json`)\n const index = buildIndexCached(docs, cachePath)\n const bm25Scores = scoreAll(message, index)\n const scoreMap = new Map(bm25Scores.map((r) => [r.docIndex, r.score]))\n\n const scored = memories.map((m, i) => {\n let s = scoreMap.get(i) ?? 0\n // Category boosts (preserved from original)\n if (m.category === \"secret\") s += 2\n if (m.category === \"commitment\") s += 1\n return { memory: m, score: s }\n })\n\n const matched = scored.filter((s) => s.score > 0)\n if (matched.length === 0) return memories.slice(-limit) // fallback: recent\n\n const selected = matched\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map((s) => s.memory)\n\n // Track access for spaced repetition (fire-and-forget)\n if (selected.length > 0) {\n try { this.recordAccess(agentId, selected.map(m => m.id)) } catch {}\n }\n\n return selected\n }\n\n buildContext(memories: MemoryFact[]): string {\n if (memories.length === 0) return \"\"\n\n const lines = [\"[Agent Memory — persistent facts from past conversations]\"]\n let chars = lines[0].length\n\n for (const m of memories) {\n const isDM = !m.source.chatId.startsWith(\"-\") && /^\\d+$/.test(m.source.chatId)\n const scope = isDM ? \"DM\" : m.source.chatId\n const line = `- [${m.category}] ${m.content} (${scope}, ${m.source.date})`\n\n if (chars + line.length > 2400) break\n lines.push(line)\n chars += line.length\n }\n\n lines.push(\"[End Memory]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Record that memories were accessed (for spaced repetition tracking).\n */\n recordAccess(agentId: string, memoryIds: string[]): void {\n if (memoryIds.length === 0) return\n const memories = this.getAll(agentId)\n const idSet = new Set(memoryIds)\n let changed = false\n\n for (const m of memories) {\n if (idSet.has(m.id)) {\n m.accessCount = (m.accessCount || 0) + 1\n m.lastAccessed = new Date().toISOString()\n // Double the review interval (spaced repetition: 1 → 2 → 4 → 8 → 16 days)\n const currentInterval = m.intervalDays || 1\n m.intervalDays = Math.min(currentInterval * 2, 30)\n m.nextReview = new Date(Date.now() + m.intervalDays * 86400000).toISOString()\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(\n this.filePath(agentId),\n memories.map(m => JSON.stringify(m)).join(\"\\n\") + \"\\n\",\n )\n }\n }\n\n /**\n * Get memories due for review (spaced repetition).\n * Returns facts whose nextReview date has passed or that have never been reviewed.\n */\n getDueForReview(agentId: string, limit: number = 5): MemoryFact[] {\n const memories = this.getAll(agentId)\n const now = Date.now()\n\n return memories\n .filter(m => {\n // Skip task-state (ephemeral)\n if (m.category === \"task-state\") return false\n // Never reviewed → due\n if (!m.nextReview) return true\n // Past review date → due\n return new Date(m.nextReview).getTime() <= now\n })\n .sort((a, b) => {\n // Prioritize: never reviewed, then oldest nextReview, then most accessed\n if (!a.nextReview && b.nextReview) return -1\n if (a.nextReview && !b.nextReview) return 1\n if (a.nextReview && b.nextReview) {\n return new Date(a.nextReview).getTime() - new Date(b.nextReview).getTime()\n }\n return (a.accessCount || 0) - (b.accessCount || 0)\n })\n .slice(0, limit)\n }\n\n /**\n * Build a recall prompt for memories due for review.\n * Injected into heartbeat context so the agent proactively recalls facts.\n */\n buildRecallContext(agentId: string): string {\n const due = this.getDueForReview(agentId, 5)\n if (due.length === 0) return \"\"\n\n const lines = [\"[Memory Recall — facts due for review, verify they are still accurate]\"]\n for (const m of due) {\n const age = Math.round((Date.now() - new Date(m.createdAt).getTime()) / 86400000)\n lines.push(`- [${m.category}] ${m.content} (${age}d old, accessed ${m.accessCount || 0}x)`)\n }\n lines.push(\"[If any fact is outdated, note the correction. Otherwise, acknowledge recall.]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Check if a substantially similar memory already exists.\n */\n hasSimilar(agentId: string, content: string): boolean {\n const memories = this.getAll(agentId)\n const contentLower = content.toLowerCase()\n const contentWords = contentLower.split(/\\s+/).filter(w => w.length > 3)\n\n return memories.some(m => {\n const mLower = m.content.toLowerCase()\n // Check for high word overlap\n const overlap = contentWords.filter(w => mLower.includes(w)).length\n return overlap >= contentWords.length * 0.7\n })\n }\n\n /**\n * Prune old/expired memories. Cap at MAX_MEMORIES per agent.\n */\n prune(agentId: string): void {\n let memories = this.getAll(agentId)\n if (memories.length === 0) return\n\n const now = Date.now()\n const ttlMs = TASK_STATE_TTL_DAYS * 24 * 60 * 60 * 1000\n\n // Remove expired task-state memories\n memories = memories.filter(m => {\n if (m.category === \"task-state\" && m.expiresAt) {\n return new Date(m.expiresAt).getTime() > now\n }\n if (m.category === \"task-state\" && !m.expiresAt) {\n return now - new Date(m.createdAt).getTime() < ttlMs\n }\n return true\n })\n\n // Cap at MAX_MEMORIES (keep most recent)\n if (memories.length > MAX_MEMORIES) {\n memories = memories.slice(-MAX_MEMORIES)\n }\n\n // Rewrite file\n const file = this.filePath(agentId)\n writeFileSync(file, memories.map(m => JSON.stringify(m)).join(\"\\n\") + \"\\n\")\n }\n}\n","// --- Message Queue with Modes ---\n//\n// Handles messages arriving while an agent is busy processing.\n// Prevents duplicate runs, lost messages, and race conditions.\n//\n// Modes:\n// collect — batch messages arriving during a run, deliver all at once when done\n// followup — queue each message as a separate follow-up turn after current run\n// drop — silently discard messages while busy (for non-critical channels)\n\nexport type QueueMode = \"collect\" | \"followup\" | \"drop\"\n\nexport interface QueuedMessage {\n text: string\n sender: string\n timestamp: number\n channel: string\n chatId: string\n /** Full context from the original message, preserved for re-routing */\n originalContext?: Record<string, unknown>\n}\n\ninterface SessionQueue {\n mode: QueueMode\n /** Messages waiting to be processed */\n pending: QueuedMessage[]\n /** Whether an agent run is currently in progress for this session */\n running: boolean\n /** Callback to invoke when a queued message should be processed */\n onFlush?: (messages: QueuedMessage[]) => Promise<void>\n}\n\n/**\n * Per-agent, per-session message queue.\n * Keyed by `agentId:channel:chatId`.\n */\nexport class MessageQueue {\n private queues: Map<string, SessionQueue> = new Map()\n private defaultMode: QueueMode\n\n constructor(defaultMode: QueueMode = \"collect\") {\n this.defaultMode = defaultMode\n }\n\n private key(agentId: string, channel: string, chatId: string): string {\n return `${agentId}:${channel}:${chatId}`\n }\n\n private getOrCreate(agentId: string, channel: string, chatId: string): SessionQueue {\n const k = this.key(agentId, channel, chatId)\n if (!this.queues.has(k)) {\n this.queues.set(k, {\n mode: this.defaultMode,\n pending: [],\n running: false,\n })\n }\n return this.queues.get(k)!\n }\n\n /**\n * Set the queue mode for a specific session.\n */\n setMode(agentId: string, channel: string, chatId: string, mode: QueueMode): void {\n this.getOrCreate(agentId, channel, chatId).mode = mode\n }\n\n /**\n * Register a flush callback for when queued messages should be processed.\n */\n onFlush(\n agentId: string,\n channel: string,\n chatId: string,\n callback: (messages: QueuedMessage[]) => Promise<void>,\n ): void {\n this.getOrCreate(agentId, channel, chatId).onFlush = callback\n }\n\n /**\n * Check if an agent is currently busy for a given session.\n */\n isBusy(agentId: string, channel: string, chatId: string): boolean {\n return this.getOrCreate(agentId, channel, chatId).running\n }\n\n /**\n * Mark the start of an agent run. Returns false if already running.\n */\n markRunning(agentId: string, channel: string, chatId: string): boolean {\n const q = this.getOrCreate(agentId, channel, chatId)\n if (q.running) return false\n q.running = true\n return true\n }\n\n /**\n * Enqueue a message that arrived while the agent is busy.\n * Returns the queue mode applied (or null if not busy and no queueing needed).\n */\n enqueue(\n agentId: string,\n channel: string,\n chatId: string,\n message: QueuedMessage,\n ): QueueMode | null {\n const q = this.getOrCreate(agentId, channel, chatId)\n\n if (!q.running) return null // Not busy, process normally\n\n switch (q.mode) {\n case \"drop\":\n return \"drop\"\n\n case \"collect\":\n case \"followup\":\n q.pending.push(message)\n return q.mode\n }\n }\n\n /**\n * Mark the end of an agent run. Flushes pending messages based on mode.\n * Returns messages to process (if any).\n */\n async markDone(\n agentId: string,\n channel: string,\n chatId: string,\n ): Promise<QueuedMessage[]> {\n const q = this.getOrCreate(agentId, channel, chatId)\n q.running = false\n\n if (q.pending.length === 0) return []\n\n const messages = [...q.pending]\n q.pending = []\n\n switch (q.mode) {\n case \"collect\": {\n // Batch all pending messages into a single combined message\n const combined: QueuedMessage = {\n text: messages.map((m) =>\n `[${m.sender}, ${new Date(m.timestamp).toISOString().slice(11, 16)}]: ${m.text}`\n ).join(\"\\n\"),\n sender: messages[messages.length - 1].sender,\n timestamp: Date.now(),\n channel: messages[0].channel,\n chatId: messages[0].chatId,\n originalContext: messages[messages.length - 1].originalContext,\n }\n\n // Fire flush callback with combined message\n if (q.onFlush) {\n await q.onFlush([combined])\n }\n return [combined]\n }\n\n case \"followup\":\n // Process each message as a separate follow-up turn\n if (q.onFlush) {\n await q.onFlush(messages)\n }\n return messages\n\n case \"drop\":\n return []\n\n default:\n return messages\n }\n }\n\n /**\n * Get the number of pending messages for a session.\n */\n pendingCount(agentId: string, channel: string, chatId: string): number {\n return this.getOrCreate(agentId, channel, chatId).pending.length\n }\n\n /**\n * Clear all queued messages for a session (e.g., on session reset).\n */\n clear(agentId: string, channel: string, chatId: string): void {\n const k = this.key(agentId, channel, chatId)\n this.queues.delete(k)\n }\n\n /**\n * Get queue stats for monitoring.\n */\n stats(): Array<{ key: string; mode: QueueMode; pending: number; running: boolean }> {\n return Array.from(this.queues.entries()).map(([key, q]) => ({\n key,\n mode: q.mode,\n pending: q.pending.length,\n running: q.running,\n }))\n }\n}\n","import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { buildIndexCached, scoreAll } from \"../memory/bm25\"\n\n// --- Behavioral Pattern Store ---\n//\n// Agents learn HOW to work, not just WHAT they know.\n// After each response, Haiku extracts behavioral patterns:\n// \"When user asks about deployment, check CI status first\"\n// \"Code reviews should mention test coverage\"\n//\n// Before each prompt, relevant patterns are injected so the agent\n// improves over time — a self-correcting loop.\n\nexport interface BehavioralPattern {\n id: string\n agentId: string\n pattern: string // the learned behavior\n context: string // when this applies\n confidence: number // 0-1, increases with validation\n accessCount: number\n createdAt: string\n lastUsed?: string\n source: { channel: string; date: string }\n}\n\nconst MAX_PATTERNS = 50\nconst EXTRACTION_MODEL = \"claude-haiku-4-20250514\"\n\nconst PATTERN_EXTRACTION_PROMPT = `You are a behavioral pattern extractor. Given a conversation between a user and an AI agent, extract behavioral patterns — lessons about HOW to approach tasks, not facts about the world.\n\nExtract patterns like:\n- \"When asked about X, the approach that works is Y\"\n- \"User prefers Z style of communication\"\n- \"For deployment tasks, always check A before doing B\"\n- \"This codebase uses pattern X, so generate code following X\"\n\nSKIP:\n- Factual information (names, dates, URLs)\n- Routine task completions\n- Greetings or small talk\n\nOutput a JSON array:\n[{\"pattern\":\"the behavioral lesson\",\"context\":\"when this applies\",\"confidence\":0.5}]\n\nIf nothing worth extracting, output: []`\n\nexport class PatternStore {\n private patternsDir: string\n\n constructor(baseDir: string = process.cwd()) {\n this.patternsDir = resolve(baseDir, \".agentx/patterns\")\n if (!existsSync(this.patternsDir)) {\n mkdirSync(this.patternsDir, { recursive: true })\n }\n }\n\n private filePath(agentId: string): string {\n return resolve(this.patternsDir, `${agentId}.jsonl`)\n }\n\n getAll(agentId: string): BehavioralPattern[] {\n const file = this.filePath(agentId)\n if (!existsSync(file)) return []\n try {\n return readFileSync(file, \"utf-8\")\n .split(\"\\n\")\n .filter(l => l.trim())\n .map(l => { try { return JSON.parse(l) } catch { return null } })\n .filter((p): p is BehavioralPattern => p !== null)\n } catch {\n return []\n }\n }\n\n addPattern(agentId: string, pattern: Omit<BehavioralPattern, \"id\" | \"createdAt\" | \"accessCount\">): void {\n const entry: BehavioralPattern = {\n ...pattern,\n id: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,\n accessCount: 0,\n createdAt: new Date().toISOString(),\n }\n appendFileSync(this.filePath(agentId), JSON.stringify(entry) + \"\\n\")\n }\n\n /**\n * Find patterns relevant to the current task.\n */\n findRelevant(message: string, agentId: string, limit: number = 5): BehavioralPattern[] {\n const patterns = this.getAll(agentId)\n if (patterns.length === 0) return []\n\n const docs = patterns.map(p => `${p.context} ${p.pattern}`)\n const cachePath = resolve(this.patternsDir, `${agentId}_bm25_cache.json`)\n const index = buildIndexCached(docs, cachePath)\n const scored = scoreAll(message, index)\n\n const results = scored\n .slice(0, limit)\n .map(s => patterns[s.docIndex])\n .filter(p => p.confidence >= 0.3) // only confident patterns\n\n // Track access\n if (results.length > 0) {\n this.recordAccess(agentId, results.map(p => p.id))\n }\n\n return results\n }\n\n /**\n * Build context string from relevant patterns for prompt injection.\n */\n buildContext(patterns: BehavioralPattern[]): string {\n if (patterns.length === 0) return \"\"\n\n const lines = [\"[Learned Patterns — behavioral lessons from past interactions]\"]\n for (const p of patterns) {\n lines.push(`- ${p.pattern} (context: ${p.context}, confidence: ${Math.round(p.confidence * 100)}%)`)\n }\n lines.push(\"[Apply these patterns when relevant to the current task]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Check if a similar pattern already exists.\n */\n hasSimilar(agentId: string, pattern: string): boolean {\n const existing = this.getAll(agentId)\n const words = pattern.toLowerCase().split(/\\s+/).filter(w => w.length > 3)\n return existing.some(p => {\n const pWords = p.pattern.toLowerCase()\n const overlap = words.filter(w => pWords.includes(w)).length\n return overlap >= words.length * 0.6\n })\n }\n\n private recordAccess(agentId: string, ids: string[]): void {\n const patterns = this.getAll(agentId)\n const idSet = new Set(ids)\n let changed = false\n for (const p of patterns) {\n if (idSet.has(p.id)) {\n p.accessCount++\n p.lastUsed = new Date().toISOString()\n // Boost confidence slightly on each use (max 1.0)\n p.confidence = Math.min(1.0, p.confidence + 0.05)\n changed = true\n }\n }\n if (changed) {\n writeFileSync(this.filePath(agentId), patterns.map(p => JSON.stringify(p)).join(\"\\n\") + \"\\n\")\n }\n }\n\n /**\n * Prune low-confidence or old patterns. Cap at MAX_PATTERNS.\n */\n prune(agentId: string): void {\n let patterns = this.getAll(agentId)\n if (patterns.length <= MAX_PATTERNS) return\n\n // Sort by confidence * accessCount (most valuable first)\n patterns.sort((a, b) => (b.confidence * b.accessCount) - (a.confidence * a.accessCount))\n patterns = patterns.slice(0, MAX_PATTERNS)\n writeFileSync(this.filePath(agentId), patterns.map(p => JSON.stringify(p)).join(\"\\n\") + \"\\n\")\n }\n}\n\n/**\n * Extract behavioral patterns from a conversation (fire-and-forget).\n */\nexport async function extractPatterns(\n agentId: string,\n userMessage: string,\n agentResponse: string,\n source: { channel: string; date: string },\n store: PatternStore,\n): Promise<void> {\n if (userMessage.length < 30 && agentResponse.length < 100) return\n\n const { createProvider } = await import(\"@/agent/providers\")\n const provider = createProvider(\"claude\")\n\n const result = await provider.generate(\n [\n { role: \"system\", content: PATTERN_EXTRACTION_PROMPT },\n { role: \"user\", content: `User: ${userMessage.slice(0, 500)}\\n\\nAgent: ${agentResponse.slice(0, 1000)}` },\n ],\n { model: EXTRACTION_MODEL, maxTokens: 512 },\n )\n\n const jsonMatch = result.content.trim().match(/\\[[\\s\\S]*\\]/)\n if (!jsonMatch) return\n\n const patterns = JSON.parse(jsonMatch[0]) as Array<{\n pattern: string\n context: string\n confidence: number\n }>\n\n if (!Array.isArray(patterns) || patterns.length === 0) return\n\n for (const p of patterns) {\n if (store.hasSimilar(agentId, p.pattern)) continue\n store.addPattern(agentId, {\n agentId,\n pattern: p.pattern,\n context: p.context,\n confidence: p.confidence || 0.5,\n source,\n })\n }\n\n // Probabilistic pruning\n if (Math.random() < 0.05) store.prune(agentId)\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 { WikiHub } from \"@/wiki\"\nimport { RateLimiter } from \"@/daemon/rate-limit\"\nimport { TokenTracker } from \"@/daemon/token-tracker\"\nimport { buildAgentContext, type ContextInput } from \"./context\"\nimport { Classifier, GraphStore, type ClassifyResult } from \"@/graph\"\nimport { HandoverStore } from \"@/channels/handover-store\"\nimport { MemoryStore } from \"./memory-store\"\nimport { extractMemories } from \"./memory-extract\"\nimport { MessageQueue, type QueueMode, type QueuedMessage } from \"./message-queue\"\nimport { loadBootstrapFiles, buildBootstrapContext, detectSoulSwitch, listSoulProfiles } from \"./bootstrap\"\nimport { PatternStore, extractPatterns } from \"./patterns\"\nimport type { LandscapeBuilder } from \"./landscape\"\nimport { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Agent Registry: lifecycle management + concurrency control ---\n\n/** Global singleton for sub-agent spawning from tools */\nlet globalRegistry: AgentRegistry | undefined\n\nexport function setGlobalRegistry(registry: AgentRegistry): void {\n globalRegistry = registry\n}\n\nexport function getGlobalRegistry(): AgentRegistry | undefined {\n return globalRegistry\n}\n\nexport interface RunningTask {\n /** Unique id for this execution (timestamp-based). */\n id: string\n /** First 200 chars of the user message, for display. */\n messagePreview: string\n /** Origin channel (telegram, whatsapp, gitlab, api, cron, business, a2a, …). */\n channel: string\n /** Group / chat / issue id from which the task arrived. */\n chatId?: string\n /** Sender id/name (user, agent id, \"cron:<id>\", \"mesh:<peer>\", …). */\n sender?: string\n /** Wall-clock start. */\n startedAt: Date\n}\n\ntype TaskOutputSubscriber = (chunk: string) => void\n\ninterface TaskOutput {\n agentId: string\n buffer: string\n subscribers: Set<TaskOutputSubscriber>\n done: boolean\n endedAt?: Date\n}\n\n/** Cap per-task buffer to keep memory bounded; recent tail wins. */\nconst TASK_OUTPUT_BUFFER_MAX = 64 * 1024\n/** Keep finished outputs around briefly so a late opener still gets the tail. */\nconst TASK_OUTPUT_TTL_MS = 5 * 60 * 1000\n\n/** Persisted record of a finished task, written to .agentx/task-history. */\nexport interface TaskRecord {\n id: string\n agentId: string\n channel: string\n chatId?: string\n sender?: string\n message: string\n startedAt: string\n endedAt: string\n durationMs: number\n ok: boolean\n error?: string\n /** Final agent text (one-shot, may be empty if streaming captured it). */\n responseText: string\n /** Terminal-style transcript captured from stream-json events. */\n transcript: string\n}\n\nconst TASK_HISTORY_DIR = \".agentx/task-history\"\n/** Default retention for persisted task records. Set for business audit trails;\n * bump via the `dashboard.taskHistoryRetentionDays` config field if you need more. */\nconst TASK_HISTORY_RETENTION_DAYS = 30\n\n/** Truncate long values so the buffer doesn't blow up on huge tool inputs/outputs. */\nfunction clip(s: string, max = 800): string {\n if (s.length <= max) return s\n return s.slice(0, max) + ` …[+${s.length - max} chars]`\n}\n\n/**\n * Phase 3 wiki layer — compact pointer to the `agentx wiki query` tool.\n * The wiki is the cross-agent institutional-knowledge source of truth.\n * Agents call it themselves when the question is about \"who / what\n * happened / what we decided / how we do X.\" Empty when the agent has no\n * catalog yet, so new installs see no noise.\n *\n * Uses an absolute CLI path because `agentx` is not on PATH in the agent\n * workspaces. `process.argv[1]` points at the running daemon's cli.js.\n */\nfunction buildWikiQueryHint(agentWiki: ReturnType<WikiHub[\"getAgentWiki\"]>, agentId: string): string {\n let articleCount = 0\n try {\n articleCount = agentWiki.listArticles(agentId).length\n } catch {\n return \"\"\n }\n if (articleCount === 0) return \"\"\n const catalogPath = resolve(agentWiki.baseDir, \"_index.md\")\n if (!existsSync(catalogPath)) return \"\"\n const cli = process.argv[1] || \"dist/cli.js\"\n const wikiDir = agentWiki.baseDir.replace(new RegExp(`/agents/${agentId}(/[^/]+)?$`), \"\")\n return [\n \"[Institutional Wiki — cross-agent source of truth]\",\n `A curated wiki with ${articleCount} article${articleCount === 1 ? \"\" : \"s\"} grouped by type: person, project, place, concept, event, decision, pattern.`,\n \"\",\n \"The wiki is the canonical source for: who people are (team, clients, agents), past events (incidents, deploys), decisions and their reasoning, documented procedures, patterns.\",\n \"\",\n \"BEFORE you grep the workspace, search memory, or answer from your own prior context — if the question is about who / what happened / what we decided / how we do X, call:\",\n ` node ${cli} wiki query \"the user's question\" --dir ${wikiDir} --agent ${agentId}`,\n \"\",\n \"It walks the catalog + wikilink graph and returns a cited answer. Your workspace memory is local to you; the wiki is shared and authoritative. Prefer the wiki for institutional questions; prefer your workspace for code-level questions.\",\n \"[End Institutional Wiki]\",\n ].join(\"\\n\")\n}\n\n/**\n * Format raw Claude Code stream-json events into a terminal-style transcript\n * for the dashboard streaming modal. Returns \"\" for events we don't surface.\n *\n * Stateful so we can emit only the *delta* of assistant text across consecutive\n * `assistant` snapshot events (Claude re-sends the cumulative content each tick).\n */\nfunction makeStreamEventFormatter(): (event: any) => string {\n let textSeen = \"\"\n return (event: any): string => {\n if (!event || typeof event !== \"object\") return \"\"\n const t = event.type\n if (t === \"system\" && event.subtype === \"init\") {\n const m = event.model ? ` model=${event.model}` : \"\"\n const sid = event.session_id ? ` session=${event.session_id.slice(0, 8)}` : \"\"\n return `· init${m}${sid}\\n`\n }\n if (t === \"assistant\" && event.message?.content) {\n let out = \"\"\n for (const block of event.message.content) {\n if (block.type === \"text\" && typeof block.text === \"string\") {\n if (block.text.length > textSeen.length) {\n out += block.text.slice(textSeen.length)\n textSeen = block.text\n }\n } else if (block.type === \"thinking\" && typeof block.thinking === \"string\") {\n out += `\\n💭 ${clip(block.thinking, 600)}\\n`\n } else if (block.type === \"tool_use\") {\n const name = block.name || \"tool\"\n const input = block.input ? clip(JSON.stringify(block.input), 400) : \"\"\n out += `\\n→ ${name}(${input})\\n`\n }\n }\n return out\n }\n if (t === \"user\" && event.message?.content) {\n let out = \"\"\n for (const block of event.message.content) {\n if (block.type === \"tool_result\") {\n const c = Array.isArray(block.content)\n ? block.content.map((b: any) => (typeof b?.text === \"string\" ? b.text : \"\")).join(\"\")\n : typeof block.content === \"string\" ? block.content : \"\"\n const flag = block.is_error ? \"← [error] \" : \"← \"\n out += `${flag}${clip(c, 800)}\\n`\n }\n }\n return out\n }\n if (t === \"result\") {\n const dur = event.duration_ms ? ` (${Math.round(event.duration_ms / 1000)}s)` : \"\"\n return `· done${dur}\\n`\n }\n return \"\"\n }\n}\n\ninterface AgentState {\n id: string\n def: AgentDef\n activeTasks: number\n totalTasks: number\n lastActive?: Date\n errors: number\n runningTasks: RunningTask[]\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 wikiHub: WikiHub\n private memoryStore: MemoryStore\n private patternStore: PatternStore\n private rateLimiter: RateLimiter\n private tokenTracker: TokenTracker\n private landscape?: LandscapeBuilder\n private messageQueue: MessageQueue\n /** Intent Knowledge Graph classifier. Null when graph.enabled=false. */\n private classifier?: Classifier\n private graphStore?: GraphStore\n /** Runtime handover store — shared with MessageRouter via file on disk. */\n private handoverStore: HandoverStore = new HandoverStore()\n /** Active soul profile per agent+chat session: \"agentId:channel:chatId\" → profile name */\n private activeSouls: Map<string, string> = new Map()\n /** Live output captured per running task id — drives the dashboard streaming modal. */\n private taskOutputs: Map<string, TaskOutput> = new Map()\n /** Last completed task summary per agent — single-line blurb for the dashboard card. */\n private lastSummaries: Map<string, { text: string; at: Date; ok: boolean }> = new Map()\n /** 24-hour sparkline cache per agent — recomputed from disk at most once a minute. */\n private sparklineCache: Map<string, { hourly: number[]; at: number }> = new Map()\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(process.cwd(), { staleMinutes: config.session.staleMinutes })\n this.wikiHub = new WikiHub(undefined, undefined, \"unified\")\n this.memoryStore = new MemoryStore()\n this.patternStore = new PatternStore()\n this.rateLimiter = new RateLimiter()\n this.tokenTracker = new TokenTracker()\n this.messageQueue = new MessageQueue()\n\n if (config.graph?.enabled) {\n this.graphStore = new GraphStore({\n baseDir: resolve(process.cwd(), config.graph.baseDir),\n log: (...a) => log(\"[graph]\", ...a),\n })\n const draftAgent = config.graph.draftAgent || config.dashboard?.draftAgent\n this.classifier = new Classifier({\n store: this.graphStore,\n daemonUrl: config.dashboard.daemonUrl,\n token: config.dashboard.token,\n draftAgent,\n autoApproveStructure: config.graph.autoApproveStructure,\n autoApproveConfidence: config.graph.autoApproveConfidence,\n log: (...a) => log(\"[classifier]\", ...a),\n })\n }\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 runningTasks: [],\n })\n }\n }\n\n /**\n * Set landscape builder (called after mesh init).\n */\n setLandscape(builder: LandscapeBuilder): void {\n this.landscape = builder\n }\n\n /**\n * If there's an active handover routing TO this agent for this (channel,\n * chatId) pair AND the operator's summary hasn't been consumed yet, pull\n * + clear it so the target agent sees the note exactly once.\n */\n private buildHandoverNote(\n agentId: string,\n channel: string,\n chatId: string,\n ): ContextInput[\"handoverNote\"] {\n const o = this.handoverStore.get(channel, chatId)\n if (!o || o.toAgent !== agentId) return undefined\n const summary = this.handoverStore.consumeSummary(channel, chatId)\n // If already consumed on a prior message, skip — route remains active\n // but no repeated briefing in every turn.\n if (!summary && o.summaryConsumedAt) return undefined\n return {\n fromAgent: o.fromAgent,\n summary: summary || o.summary,\n at: o.createdAt,\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., \"@my_bot\" -> \"my-agent\").\n * Returns the agent with the longest (most specific) mention match.\n *\n * When `atMentionsOnly` is true, only `@`-prefixed mentions are considered.\n * This is used for messages originating from another bot (cross-daemon\n * Telegram cascades, for example) where we don't want bare-word matches\n * like \"nadia\" in prose or \"devops-mtgl\" quoted in a reply to trigger\n * agents spuriously. Intentional handoffs still work because agents\n * write explicit `@noqta_X_bot` handles.\n */\n findByMention(text: string, opts: { atMentionsOnly?: boolean } = {}): 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 if (opts.atMentionsOnly && !mention.startsWith(\"@\")) continue\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 /** Wiki hub accessor. */\n getWikiHub(): WikiHub { return this.wikiHub }\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. \"@my_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 // Build session key for queue management\n const qChannel = task.context?.channel || \"api\"\n const qChatId = task.context?.chatId || task.context?.group || task.context?.sender || \"default\"\n\n if (state.activeTasks >= state.def.maxConcurrent) {\n // Agent is busy — try to queue the message instead of rejecting\n const mode = (state.def.queueMode as QueueMode) || \"collect\"\n const queued = this.messageQueue.enqueue(task.agentId, qChannel, qChatId, {\n text: task.message,\n sender: task.context?.sender || \"User\",\n timestamp: Date.now(),\n channel: qChannel,\n chatId: qChatId,\n originalContext: task.context as Record<string, unknown>,\n })\n\n if (queued === \"drop\") {\n this.log(`[${task.agentId}] busy, message dropped (mode: drop)`)\n return { content: \"\", error: `Agent \"${task.agentId}\" is busy — message dropped` }\n }\n\n if (queued) {\n const pending = this.messageQueue.pendingCount(task.agentId, qChannel, qChatId)\n this.log(`[${task.agentId}] busy, message queued (mode: ${queued}, pending: ${pending})`)\n return {\n content: \"\",\n error: `__queued__:${queued}:${pending}`,\n }\n }\n }\n\n // Rate limit — WAIT for a slot instead of failing. Dropping messages looks\n // like the bot is broken; queueing for ~5 minutes gives chatty channels a\n // smooth experience and still bails if something's genuinely stuck.\n const rateResult = await this.rateLimiter.acquire(task.agentId, {\n maxWaitMs: 5 * 60_000,\n onWait: (reason, waitMs) => {\n this.log(`[${task.agentId}] ${reason} — queued, resumes in ~${Math.max(1, Math.round(waitMs / 1000))}s`)\n },\n })\n if (!rateResult.ok) {\n this.log(`[${task.agentId}] ${rateResult.reason}`)\n return { content: \"\", error: rateResult.reason }\n }\n\n state.activeTasks++\n state.totalTasks++\n state.lastActive = new Date()\n\n // Track the running task for live visibility (/agents endpoint surfaces this).\n const runningTask: RunningTask = {\n id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n messagePreview: (task.message || \"\").slice(0, 200),\n channel: task.context?.channel || \"api\",\n chatId: task.context?.chatId || task.context?.group,\n sender: task.context?.sender,\n startedAt: new Date(),\n }\n state.runningTasks.push(runningTask)\n\n // Output capture for the dashboard streaming modal. We never *force*\n // streaming on a caller that didn't ask for it — that would change the\n // runtime mode (stream-json vs json) for every task in the system. So:\n // - Caller passed onDelta → wrap it to also fan-out to dashboard subscribers (live).\n // We also install onEvent so the dashboard sees tool calls, tool results,\n // and system events — i.e. everything you'd see in a real terminal.\n // - Caller did NOT pass onDelta → leave runtime in non-streaming mode and\n // post the final response.content as a single chunk after execution.\n const output: TaskOutput = { agentId: task.agentId, buffer: \"\", subscribers: new Set(), done: false }\n this.taskOutputs.set(runningTask.id, output)\n const pushToBuffer = (chunk: string) => {\n if (!chunk) return\n output.buffer += chunk\n if (output.buffer.length > TASK_OUTPUT_BUFFER_MAX) {\n output.buffer = output.buffer.slice(output.buffer.length - TASK_OUTPUT_BUFFER_MAX)\n }\n for (const sub of output.subscribers) {\n try { sub(chunk) } catch { /* subscriber crashed — ignore */ }\n }\n }\n let onEvent: ((event: any) => void) | undefined\n if (onDelta) {\n // Caller's onDelta still fires only for assistant text (unchanged).\n // Dashboard subscribers see the formatted stream-json firehose via onEvent\n // — this is what makes the modal feel like a live terminal.\n const formatter = makeStreamEventFormatter()\n onEvent = (event: any) => {\n const formatted = formatter(event)\n if (formatted) pushToBuffer(formatted)\n }\n }\n\n // Mark session as running in the message queue\n this.messageQueue.markRunning(task.agentId, qChannel, qChatId)\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?.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 // Classify the message through the intent graph when enabled. Skip for\n // a2a traffic — the classifier itself dispatches through /task, and\n // re-classifying its own prompts would recurse forever. Any classifier\n // failure (bad LLM output, schema rejection, network error) must never\n // propagate — the main task still has to run.\n let intent: ClassifyResult | undefined\n if (this.classifier && channel !== \"a2a\") {\n try {\n intent = (await this.classifier.classify({\n text: task.message,\n channel,\n sender: task.context?.sender,\n agentId: task.agentId,\n })) || undefined\n } catch (e: any) {\n this.log(`[classifier] classify failed for ${task.agentId}: ${e?.message || e}`)\n }\n }\n\n // Wiki context — Phase 3 Farzapedia alignment: instead of preloading BM25\n // hits (the old shallow-RAG path), we inject a short pointer to the\n // `agentx wiki query` tool. The agent decides WHEN institutional knowledge\n // matters and invokes the agentic query itself — walking _index.md, picking\n // candidates, following wikilinks 2–3 hops. Zero retrieval cost on messages\n // that don't need it.\n const agentWiki = this.wikiHub.getAgentWiki(task.agentId)\n const wikiContext = buildWikiQueryHint(agentWiki, task.agentId)\n\n // Load persistent agent memory (cross-session facts)\n const relevantMemories = this.memoryStore.findRelevant(task.message, task.agentId, 8)\n const memoryContext = this.memoryStore.buildContext(relevantMemories)\n\n // Load behavioral patterns (self-improving loop)\n const relevantPatterns = this.patternStore.findRelevant(task.message, task.agentId, 5)\n const patternContext = this.patternStore.buildContext(relevantPatterns)\n\n // Auto-inject skills matched to current message\n let skillInjection = \"\"\n try {\n const { loadLocalSkills, getAutoInjectSkills } = await import(\"@/agent/skills/loader\")\n const skills = await loadLocalSkills(state.def.workspace)\n skillInjection = getAutoInjectSkills(skills, task.message)\n } catch {\n // Skill loading is optional\n }\n\n // Decide whether to resume or start fresh\n let resumeSessionId = state.def.tier === \"claude-code\"\n ? this.sessions.getClaudeSessionId(task.agentId, channel, chatId)\n : undefined\n\n // If session is stale (idle > 15min), start fresh with full context rebuild\n if (resumeSessionId && this.sessions.isSessionStale(task.agentId, channel, chatId)) {\n this.log(`[${task.agentId}] session stale for ${channel}:${chatId}, starting fresh`)\n this.sessions.clearClaudeSessionId(task.agentId, channel, chatId)\n resumeSessionId = undefined\n }\n\n // Compact session if history is getting too long (summarize older messages)\n try {\n const compactResult = await this.sessions.compactIfNeeded(\n task.agentId, channel, chatId, this.memoryStore,\n )\n if (compactResult.compacted) {\n const quality = compactResult.qualityScore ?? \"?\"\n const lost = compactResult.lostEntities?.length ?? 0\n this.log(`[${task.agentId}] session compacted for ${channel}:${chatId} (quality: ${quality}%, ${lost} entities lost)`)\n if (compactResult.lostEntities?.length) {\n this.log(`[${task.agentId}] lost entities: ${compactResult.lostEntities.slice(0, 5).join(\", \")}`)\n }\n if (compactResult.drift) {\n const d = compactResult.drift\n this.log(`[${task.agentId}] DRIFT DETECTED: score=${d.overallScore} (lexicon=${d.lexiconDecay}, tools=${d.toolShift}, semantic=${d.semanticDrift})`)\n if (d.lostWords.length) this.log(`[${task.agentId}] lost domain words: ${d.lostWords.slice(0, 5).join(\", \")}`)\n }\n resumeSessionId = undefined\n }\n } catch (e: any) {\n this.log(`[${task.agentId}] compaction failed (non-fatal): ${e.message}`)\n }\n\n const sessionHistory = !resumeSessionId\n ? this.sessions.buildHistoryContext(task.agentId, channel, chatId)\n : undefined\n\n // Bridge cross-chat amnesia: inject context from other chats (DM ↔ group)\n const crossChatContext = this.sessions.getCrossSessionSummary(task.agentId, channel, chatId)\n\n // Soul switching: detect /soul command and track active profile\n const soulSessionKey = `${task.agentId}:${channel}:${chatId}`\n const soulSwitch = detectSoulSwitch(task.message)\n if (soulSwitch) {\n if (soulSwitch === \"default\") {\n this.activeSouls.delete(soulSessionKey)\n this.log(`[${task.agentId}] Soul reset to default`)\n } else {\n this.activeSouls.set(soulSessionKey, soulSwitch)\n this.log(`[${task.agentId}] Soul switched to: ${soulSwitch}`)\n }\n }\n const activeSoul = this.activeSouls.get(soulSessionKey)\n\n // Load bootstrap identity files (SOUL.md or SOUL.{profile}.md).\n // These are delivered via --append-system-prompt to Claude Code so they\n // live inside the cached system prompt — we no longer append them into\n // the per-turn user-message context (which would pay cache-create on\n // every new session). The context builder still gets a flag-empty\n // bootstrapContext so the `[Identity]` / `[Personality]` block is\n // suppressed in the rendered context (Claude sees them once, cached).\n const bootstrapFiles = loadBootstrapFiles(state.def.workspace, activeSoul)\n const bootstrapContextText = buildBootstrapContext(bootstrapFiles)\n\n // Compose the cacheable system-prompt preamble. Order matters for cache\n // stability: agent.systemPrompt is the most stable (config-defined),\n // bootstrap files follow. Soul-switching mid-session produces a new\n // append-text and a new Claude cache key; that's by design — the rare\n // switch is worth a one-time cache-create cost.\n const systemPromptAppend = [\n state.def.systemPrompt || \"\",\n bootstrapContextText || \"\",\n ].filter((s) => s.trim().length > 0).join(\"\\n\\n\") || undefined\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 senderId: task.context?.senderId,\n senderUsername: task.context?.senderUsername,\n landscape: this.landscape?.getForAgent(task.agentId),\n channelMeta: task.context?.channelMeta,\n mediaPath: task.context?.mediaPath,\n mediaType: task.context?.mediaType,\n replyToText: task.context?.replyToText,\n // bootstrapContext intentionally omitted — delivered via system prompt.\n patternContext: patternContext || undefined,\n skillInjection: skillInjection || undefined,\n groupHistory: task.context?.group ? undefined : undefined, // group log is injected by router\n sessionHistory,\n memoryContext: memoryContext || undefined,\n crossChatContext: crossChatContext || undefined,\n wikiContext,\n handoverNote: this.buildHandoverNote(task.agentId, channel, chatId),\n intent: intent\n ? {\n path: intent.path,\n pathLabel: intent.pathLabel,\n pathId: intent.pathId,\n axes: intent.axes,\n leaf: intent.leaf,\n status: intent.status,\n }\n : undefined,\n message: task.message,\n }\n\n const historyContext = buildAgentContext(contextInput)\n\n // Attach the cacheable preamble onto the task so runtime.ts can forward\n // it to Claude CLI's --append-system-prompt arg.\n const taskWithSystemPrompt: AgentTask = { ...task, systemPromptAppend }\n\n let finalResponse: AgentResponse | undefined\n try {\n const response = await executeTask(state.def, taskWithSystemPrompt, this.providers, onDelta, historyContext, resumeSessionId, onEvent)\n finalResponse = response\n\n // For non-streaming runs (no caller onDelta) we never captured incremental\n // chunks — surface the final response in one shot so the dashboard modal\n // shows something meaningful when subscribers are attached.\n if (!onDelta && (response.content || response.error)) {\n const finalText = response.error ? `[error] ${response.error}` : response.content\n output.buffer = finalText\n for (const sub of output.subscribers) {\n try { sub(finalText) } catch { /* */ }\n }\n }\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.wikiHub.getSharedStore().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 // Memory: extract and store memorable facts via Haiku (fire-and-forget)\n extractMemories(\n task.agentId,\n task.message,\n response.content,\n { channel, chatId, sender: senderName },\n this.memoryStore,\n ).catch(() => {})\n\n // Patterns: extract behavioral patterns (fire-and-forget)\n extractPatterns(\n task.agentId,\n task.message,\n response.content,\n { channel, date: new Date().toISOString().slice(0, 10) },\n this.patternStore,\n ).catch(() => {})\n }\n\n // Track token usage (real counts if available, estimate otherwise).\n // Model priority for pricing: what the API ACTUALLY billed (from the\n // CLI init / result event) > per-task override (cron model) > agent\n // config default. Without this, cron-overridden runs get priced at\n // the wrong rate and cache-aware cost reports mislead operators.\n const billedModel = response.billedModel || task.model || state.def.model\n const tChannel = task.context?.channel\n // Session key = \"channel:chatId\" — opaque to the tracker, just needs\n // to be unique per (conversation, day). Lets us derive avg-tasks/\n // session later so retry-heavy traffic becomes visible.\n const tSessionKey = tChannel && (task.context?.chatId || task.context?.group || task.context?.sender)\n ? `${tChannel}:${task.context?.chatId || task.context?.group || task.context?.sender}`\n : undefined\n this.tokenTracker.record(\n task.agentId,\n response.duration || 0,\n response.usage,\n task.message.length,\n response.content.length,\n false,\n billedModel,\n tChannel,\n tSessionKey,\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 finalResponse = { content: \"\", error: error.message }\n return finalResponse\n } finally {\n state.activeTasks--\n // Remove this run from the running-tasks list.\n const idx = state.runningTasks.findIndex((r) => r.id === runningTask.id)\n if (idx !== -1) state.runningTasks.splice(idx, 1)\n\n // Notify any open dashboard streams that this task has finished, then\n // schedule the buffer for cleanup so memory doesn't grow unbounded.\n output.done = true\n output.endedAt = new Date()\n for (const sub of output.subscribers) {\n try { sub(\"\\n[task finished]\\n\") } catch { /* ignore */ }\n }\n output.subscribers.clear()\n setTimeout(() => this.taskOutputs.delete(runningTask.id), TASK_OUTPUT_TTL_MS).unref?.()\n\n // Persist a TaskRecord for the dashboard's \"Recent activities\" panel.\n // Best-effort — disk failures must not affect task semantics.\n try {\n const endedAt = output.endedAt\n const record: TaskRecord = {\n id: runningTask.id,\n agentId: task.agentId,\n channel: runningTask.channel,\n chatId: runningTask.chatId,\n sender: runningTask.sender,\n message: task.message || \"\",\n startedAt: runningTask.startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: endedAt.getTime() - runningTask.startedAt.getTime(),\n ok: !finalResponse?.error,\n error: finalResponse?.error,\n responseText: finalResponse?.content || \"\",\n transcript: output.buffer,\n }\n this.persistTaskRecord(record)\n // Stash a one-line summary so the dashboard card can show \"what the\n // agent did last\" without reading from disk on every snapshot.\n const rawSummary = (record.responseText || record.error || \"\").trim()\n if (rawSummary) {\n const firstLine = rawSummary.split(/\\r?\\n/)[0].slice(0, 140)\n this.lastSummaries.set(task.agentId, { text: firstLine, at: endedAt, ok: record.ok })\n }\n } catch (e: any) {\n this.log(`[${task.agentId}] task history persist failed: ${e?.message}`)\n }\n\n // Flush queued messages that arrived while this run was in progress\n this.messageQueue.markDone(task.agentId, qChannel, qChatId)\n .then((queued) => {\n if (queued.length === 0) return\n this.log(`[${task.agentId}] flushing ${queued.length} queued message(s)`)\n // Re-execute each queued message as a new task\n for (const qm of queued) {\n this.execute({\n message: qm.text,\n agentId: task.agentId,\n context: (qm.originalContext as AgentTask[\"context\"]) || {\n channel: qm.channel,\n sender: qm.sender,\n chatId: qm.chatId,\n },\n }).catch((e) => {\n this.log(`[${task.agentId}] queued message failed: ${e.message}`)\n })\n }\n })\n .catch((e) => {\n this.log(`[${task.agentId}] queue flush failed: ${e.message}`)\n })\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 model?: string\n workspace: string\n active: number\n total: number\n errors: number\n lastActive?: Date\n runningTasks: RunningTask[]\n lastSummary?: { text: string; at: string; ok: boolean }\n hourlyTasks?: number[]\n }> {\n return Array.from(this.agents.values()).map((s) => {\n const summary = this.lastSummaries.get(s.id)\n return {\n id: s.id,\n name: s.def.name,\n tier: s.def.tier,\n model: s.def.model,\n workspace: s.def.workspace,\n active: s.activeTasks,\n total: s.totalTasks,\n errors: s.errors,\n lastActive: s.lastActive,\n runningTasks: s.runningTasks,\n lastSummary: summary ? { text: summary.text, at: summary.at.toISOString(), ok: summary.ok } : undefined,\n hourlyTasks: this.getHourlySparkline(s.id, 24),\n }\n })\n }\n\n /**\n * Write a finished task record to disk under .agentx/task-history/<agent>/<yyyy-mm-dd>/.\n * Side-effects only — call sites should treat failure as best-effort.\n */\n private persistTaskRecord(record: TaskRecord): void {\n const day = record.endedAt.slice(0, 10)\n const dir = resolve(process.cwd(), TASK_HISTORY_DIR, this.safe(record.agentId), day)\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const file = resolve(dir, `${this.safe(record.id)}.json`)\n writeFileSync(file, JSON.stringify(record, null, 2), \"utf-8\")\n }\n\n /**\n * Compute a 24-hour hourly task-count sparkline for an agent from persisted\n * history. Returns an array of 24 numbers: buckets[0] = 23-24h ago,\n * buckets[23] = now-hour. Cached per-agent for 60s to keep snapshot load\n * reasonable when the dashboard polls every 2s.\n */\n getHourlySparkline(agentId: string, hours: number = 24): number[] {\n const now = Date.now()\n const cached = this.sparklineCache.get(agentId)\n if (cached && now - cached.at < 60_000 && cached.hourly.length === hours) return cached.hourly\n\n const buckets = new Array<number>(hours).fill(0)\n const windowStart = now - hours * 3600_000\n const root = resolve(process.cwd(), TASK_HISTORY_DIR, this.safe(agentId))\n if (!existsSync(root)) {\n this.sparklineCache.set(agentId, { hourly: buckets, at: now })\n return buckets\n }\n // Only need the last 2 day folders — crossing midnight still fits.\n const today = new Date(now).toISOString().slice(0, 10)\n const yesterday = new Date(now - 86400_000).toISOString().slice(0, 10)\n const dayToday = new Date(now); dayToday.setUTCHours(0, 0, 0, 0)\n for (const day of [yesterday, today]) {\n const dayDir = resolve(root, day)\n if (!existsSync(dayDir)) continue\n try {\n for (const f of readdirSync(dayDir)) {\n if (!f.endsWith(\".json\")) continue\n // Task id is timestamp-prefixed (e.g. 1776447897061-xxx.json) — pull\n // the millis without parsing each file.\n const tsStr = f.split(\"-\")[0]\n const ts = parseInt(tsStr, 10)\n if (!Number.isFinite(ts) || ts < windowStart || ts > now) continue\n const offsetHours = Math.floor((now - ts) / 3600_000)\n const idx = hours - 1 - offsetHours\n if (idx >= 0 && idx < hours) buckets[idx]++\n }\n } catch { /* skip unreadable */ }\n }\n this.sparklineCache.set(agentId, { hourly: buckets, at: now })\n return buckets\n }\n\n /**\n * Rebuild the in-memory lastSummaries cache from disk so dashboard cards\n * show the most recent \"what did they do\" line even right after a restart.\n * Called once on daemon startup — one file read per agent.\n */\n hydrateLastSummariesFromDisk(): number {\n const root = resolve(process.cwd(), TASK_HISTORY_DIR)\n if (!existsSync(root)) return 0\n let loaded = 0\n for (const agentDir of readdirSync(root)) {\n const agentPath = resolve(root, agentDir)\n let days: string[]\n try { days = readdirSync(agentPath).filter((d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d)).sort().reverse() } catch { continue }\n outer: for (const day of days) {\n const dayDir = resolve(agentPath, day)\n let files: string[]\n try { files = readdirSync(dayDir).filter((f) => f.endsWith(\".json\")).sort().reverse() } catch { continue }\n for (const f of files) {\n try {\n const rec = JSON.parse(readFileSync(resolve(dayDir, f), \"utf-8\")) as TaskRecord\n const raw = (rec.responseText || rec.error || \"\").trim()\n if (!raw) continue\n const firstLine = raw.split(/\\r?\\n/)[0].slice(0, 140)\n this.lastSummaries.set(rec.agentId, { text: firstLine, at: new Date(rec.endedAt), ok: rec.ok })\n loaded++\n break outer\n } catch { /* skip corrupt file */ }\n }\n }\n }\n return loaded\n }\n\n /**\n * Drop history folders older than retention window. Cheap startup-time\n * sweep — once a day is plenty, but doing it on every daemon boot is fine.\n */\n pruneTaskHistory(retentionDays = TASK_HISTORY_RETENTION_DAYS): number {\n const root = resolve(process.cwd(), TASK_HISTORY_DIR)\n if (!existsSync(root)) return 0\n const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000\n let removed = 0\n for (const agentDir of readdirSync(root)) {\n const agentPath = resolve(root, agentDir)\n let dayEntries: string[]\n try { dayEntries = readdirSync(agentPath) } catch { continue }\n for (const day of dayEntries) {\n const ts = Date.parse(day + \"T00:00:00Z\")\n if (Number.isNaN(ts) || ts >= cutoff) continue\n try { rmSync(resolve(agentPath, day), { recursive: true, force: true }); removed++ } catch { /* */ }\n }\n }\n return removed\n }\n\n /**\n * Newest-first list of task records for one agent, capped at `limit`.\n * Walks at most the last few day folders so this is O(limit), not O(history).\n */\n listTaskHistory(agentId: string, limit = 50): Array<Omit<TaskRecord, \"transcript\" | \"responseText\">> {\n const root = resolve(process.cwd(), TASK_HISTORY_DIR, this.safe(agentId))\n if (!existsSync(root)) return []\n const days = readdirSync(root).filter((d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d)).sort().reverse()\n const out: Array<Omit<TaskRecord, \"transcript\" | \"responseText\">> = []\n for (const day of days) {\n if (out.length >= limit) break\n const dayDir = resolve(root, day)\n let files: string[]\n try { files = readdirSync(dayDir).filter((f) => f.endsWith(\".json\")) } catch { continue }\n // Sort by file mtime desc — task ids are timestamp-prefixed so lexicographic also works.\n files.sort().reverse()\n for (const f of files) {\n if (out.length >= limit) break\n try {\n const rec = JSON.parse(readFileSync(resolve(dayDir, f), \"utf-8\")) as TaskRecord\n const { transcript: _t, responseText: _r, ...summary } = rec\n out.push(summary)\n } catch { /* skip corrupt */ }\n }\n }\n return out\n }\n\n /**\n * Look up one task's full record (transcript included) across the retention window.\n */\n getTaskRecord(agentId: string, taskId: string): TaskRecord | null {\n const root = resolve(process.cwd(), TASK_HISTORY_DIR, this.safe(agentId))\n if (!existsSync(root)) return null\n const safeId = this.safe(taskId)\n const days = readdirSync(root).filter((d) => /^\\d{4}-\\d{2}-\\d{2}$/.test(d)).sort().reverse()\n for (const day of days) {\n const file = resolve(root, day, `${safeId}.json`)\n if (existsSync(file)) {\n try { return JSON.parse(readFileSync(file, \"utf-8\")) as TaskRecord } catch { return null }\n }\n }\n return null\n }\n\n private safe(s: string): string {\n return s.replace(/[^a-zA-Z0-9_.:-]/g, \"_\")\n }\n\n /**\n * Subscribe to live output for a running task. Returns the buffer that has\n * accumulated so far plus an unsubscribe handle. If the task is unknown,\n * returns null. If the task already finished, the subscriber is given the\n * tail buffer and immediately ended.\n */\n subscribeToTaskOutput(taskId: string, sub: TaskOutputSubscriber): { initial: string; done: boolean; unsubscribe: () => void } | null {\n const out = this.taskOutputs.get(taskId)\n if (!out) return null\n if (out.done) return { initial: out.buffer, done: true, unsubscribe: () => {} }\n out.subscribers.add(sub)\n return {\n initial: out.buffer,\n done: false,\n unsubscribe: () => { out.subscribers.delete(sub) },\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 /**\n * Get the token tracker instance (for daemon hooks).\n */\n getTokenTracker(): TokenTracker {\n return this.tokenTracker\n }\n}\n"],"mappings":"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","names":["execa","execFile","friendlyModelError","raw","source","jsonMatch","parsed","inner","errType","errMessage","statusMatch","status","renderFriendlyError","f","buildPrompt","agent","task","historyContext","parts","ctx","envLines","isGitLab","isTelegram","peer","handle","role","buildClaudeArgs","prompt","streaming","resumeSessionId","modelOverride","systemPromptAppend","args","model","extractClaudeIsError","stdout","data","parseClaudeJsonOutput","usage","billedModel","executeClaudeCode","start","timeoutMs","stderr","exitCode","resolve","reject","execFile","error","errMsg","renderFriendlyError","friendlyModelError","apiErrorInStdout","parsed","executeClaudeCodeStreaming","onDelta","onEvent","fullText","streamBilledModel","streamUsage","streamSessionId","streamApiError","streamTimeoutMs","proc","execa","lineBuffer","chunk","lines","line","event","block","delta","resultText","result","executeSdk","apiKey","sdk","query","content","q","message","executeOrchestrator","generate","providerName","fullTask","executeTask","providers","readFileSync","writeFileSync","mkdirSync","existsSync","readdirSync","resolve","MAX_HISTORY_CHARS","MAX_MESSAGES","DEFAULT_STALE_SESSION_MINUTES","COMPACTION_MARKER","bucketLabel","iso","m","hh","mm","qtr","SessionStore","baseDir","opts","agentId","channel","chatId","day","key","safe","file","data","session","senderName","content","claudeSessionId","lines","currentBucket","msg","bucket","name","currentKey","files","f","hints","recent","scope","memoryStore","needsCompaction","compactSession","applyCompaction","result","totalChars","sum","removed","RateLimiter","maxPerMinute","maxPerHour","agentId","now","timestamps","hourAgo","recent","t","minuteAgo","inMinute","oldest","waitMs","opts","maxWaitMs","deadline","firstWait","r","remaining","sleepMs","resolve","DEFAULT_CONFIG","CHARS_PER_TOKEN","buildAgentContext","input","config","layers","buildLayers","a","b","parts","totalChars","maxChars","layer","layerMaxChars","trimmed","remaining","budget","name","fallback","buildChannelLayer","buildScopeLayer","lines","leafId","leafAxes","kv","k","v","intentTags","extractIntentTags","artifactLines","history","maxTokens","rules","tags","meta","fact","message","lower","readFileSync","writeFileSync","appendFileSync","mkdirSync","existsSync","resolve","MAX_MEMORIES","TASK_STATE_TTL_DAYS","MemoryStore","baseDir","resolve","existsSync","mkdirSync","agentId","fact","entry","appendFileSync","file","readFileSync","l","m","message","limit","memories","docs","cachePath","index","buildIndexCached","bm25Scores","scoreAll","scoreMap","r","matched","i","s","selected","a","b","lines","chars","scope","line","memoryIds","idSet","changed","currentInterval","writeFileSync","now","due","age","content","contentWords","w","mLower","ttlMs","MessageQueue","defaultMode","agentId","channel","chatId","k","mode","callback","q","message","messages","combined","m","key","readFileSync","writeFileSync","appendFileSync","mkdirSync","existsSync","resolve","MAX_PATTERNS","EXTRACTION_MODEL","PATTERN_EXTRACTION_PROMPT","PatternStore","baseDir","resolve","existsSync","mkdirSync","agentId","file","readFileSync","l","p","pattern","entry","appendFileSync","message","limit","patterns","docs","cachePath","index","buildIndexCached","results","scoreAll","s","lines","existing","words","w","pWords","ids","idSet","changed","writeFileSync","a","b","extractPatterns","userMessage","agentResponse","source","store","createProvider","jsonMatch","existsSync","mkdirSync","readdirSync","readFileSync","rmSync","writeFileSync","resolve","globalRegistry","setGlobalRegistry","registry","getGlobalRegistry","TASK_OUTPUT_BUFFER_MAX","TASK_OUTPUT_TTL_MS","TASK_HISTORY_DIR","TASK_HISTORY_RETENTION_DAYS","clip","s","max","buildWikiQueryHint","agentWiki","agentId","articleCount","catalogPath","cli","wikiDir","makeStreamEventFormatter","textSeen","event","t","m","sid","out","block","name","input","c","b","flag","AgentRegistry","HandoverStore","config","log","SessionStore","WikiHub","MemoryStore","PatternStore","RateLimiter","TokenTracker","MessageQueue","GraphStore","a","draftAgent","Classifier","id","def","builder","channel","chatId","o","summary","text","opts","lower","bestId","bestLen","state","mention","mentionLower","found","peers","agent","task","onDelta","qChannel","qChatId","mode","queued","pending","rateResult","reason","waitMs","runningTask","output","pushToBuffer","chunk","sub","onEvent","formatter","formatted","senderName","intent","e","wikiContext","relevantMemories","memoryContext","relevantPatterns","patternContext","skillInjection","loadLocalSkills","getAutoInjectSkills","skills","resumeSessionId","compactResult","quality","lost","d","sessionHistory","crossChatContext","soulSessionKey","soulSwitch","detectSoulSwitch","activeSoul","bootstrapFiles","loadBootstrapFiles","bootstrapContextText","buildBootstrapContext","systemPromptAppend","contextInput","historyContext","buildAgentContext","taskWithSystemPrompt","finalResponse","response","executeTask","finalText","entryId","extractMemories","extractPatterns","billedModel","tChannel","tSessionKey","error","idx","r","endedAt","record","rawSummary","firstLine","qm","day","dir","file","hours","now","cached","buckets","windowStart","root","today","yesterday","dayDir","f","tsStr","ts","offsetHours","loaded","agentDir","agentPath","days","outer","files","rec","raw","retentionDays","cutoff","removed","dayEntries","limit","_t","_r","taskId","safeId"]}
|
package/dist/chunk-CXY2RYNI.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import{readFileSync as A,writeFileSync as E,appendFileSync as K,existsSync as O,mkdirSync as j}from"fs";import{resolve as _}from"path";var w={"claude-opus-4-6":{input:5,output:25,cacheRead:.5,cacheCreate:6.25},"claude-opus-4-5":{input:5,output:25,cacheRead:.5,cacheCreate:6.25},"claude-opus":{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},"claude-sonnet":{input:3,output:15,cacheRead:.3,cacheCreate:3.75},"claude-haiku":{input:1,output:5,cacheRead:.1,cacheCreate:1.25}},H=2e5,F=1.5;function W(f){let e=f.toLowerCase();return e.includes("opus")?e.includes("4-6")||e.includes("4.6")||e.includes("opus-4-6")?"claude-opus-4-6":e.includes("4-5")||e.includes("4.5")||e.includes("opus-4-5")?"claude-opus-4-5":"claude-opus":e.includes("haiku")?"claude-haiku":"claude-sonnet"}var x=500,G=".agentx/pricing/custom.json",I=class{dir;cache=null;pricing;constructor(e=_(process.cwd(),".agentx/usage")){this.dir=e,j(this.dir,{recursive:!0}),this.pricing=J()}getPricing(){return this.pricing}static calculateCost(e,r,s=w){let h=W(r||e.model||"claude-sonnet"),a=s[h]||s["claude-sonnet"],p=e.tier2InputTokens||0,C=e.tier2OutputTokens||0,T=e.tier2CacheReadTokens||0,n=e.tier2CacheCreateTokens||0,i=(e.inputTokens||0)/1e6*a.input+(e.outputTokens||0)/1e6*a.output+(e.cacheReadTokens||0)/1e6*a.cacheRead+(e.cacheCreateTokens||0)/1e6*a.cacheCreate,t=p/1e6*a.input+C/1e6*a.output+T/1e6*a.cacheRead+n/1e6*a.cacheCreate;return i+t*F}cost(e,r){return I.calculateCost(e,r,this.pricing)}record(e,r,s,h,a,p,C,T,n){let i=this.today(),t=i.agents[e]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0,byChannel:{},sessionKeys:[]};t.tasks++,t.totalDuration+=r,C&&(t.model=C);let b=0,g=0,d=0,k=0,c=0,m=0,o=0,R=0;s?s.inputTokens+s.cacheReadTokens+s.cacheCreateTokens>H?(c=s.inputTokens,m=s.outputTokens,o=s.cacheReadTokens,R=s.cacheCreateTokens):(b=s.inputTokens,g=s.outputTokens,d=s.cacheReadTokens,k=s.cacheCreateTokens):h!==void 0&&a!==void 0&&(b=Math.ceil(h/4),g=Math.ceil(a/4)),t.inputTokens+=b,t.outputTokens+=g,t.cacheReadTokens+=d,t.cacheCreateTokens+=k,t.tier2InputTokens=(t.tier2InputTokens||0)+c,t.tier2OutputTokens=(t.tier2OutputTokens||0)+m,t.tier2CacheReadTokens=(t.tier2CacheReadTokens||0)+o,t.tier2CacheCreateTokens=(t.tier2CacheCreateTokens||0)+R,p&&t.errors++,t.byChannel||(t.byChannel={});let y=(T||"other").toLowerCase(),l=t.byChannel[y]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0};l.tasks++,l.totalDuration+=r,l.inputTokens+=b,l.outputTokens+=g,l.cacheReadTokens+=d,l.cacheCreateTokens+=k,l.tier2InputTokens+=c,l.tier2OutputTokens+=m,l.tier2CacheReadTokens+=o,l.tier2CacheCreateTokens+=R,p&&l.errors++,t.byChannel[y]=l,n&&(t.sessionKeys||(t.sessionKeys=[]),t.sessionKeys.includes(n)||(t.sessionKeys.push(n),t.sessionKeys.length>x&&t.sessionKeys.splice(0,t.sessionKeys.length-x))),i.agents[e]=t,this.save(i)}today(){let e=new Date().toISOString().slice(0,10);if(this.cache?.date===e)return this.cache;let r=this.filePath(e);if(O(r))try{return this.cache=JSON.parse(A(r,"utf-8")),this.cache}catch{}return this.cache={date:e,agents:{}},this.cache}getDate(e){let r=this.filePath(e);if(!O(r))return null;try{return JSON.parse(A(r,"utf-8"))}catch{return null}}generateDailyReport(e){let r=e||new Date().toISOString().slice(0,10),s=this.getDate(r);if(!s)return null;let h=0,a=0,p=0,C=0,T=0,n=0,i="",t=0,b={},g={},d={};for(let[k,c]of Object.entries(s.agents)){let m=this.cost(c);b[k]=m,h+=c.tasks,a+=(c.inputTokens||0)+(c.tier2InputTokens||0),p+=(c.outputTokens||0)+(c.tier2OutputTokens||0),C+=(c.cacheReadTokens||0)+(c.tier2CacheReadTokens||0),T+=(c.cacheCreateTokens||0)+(c.tier2CacheCreateTokens||0),n+=m,m>t&&(t=m,i=k);let o=c.sessionKeys?.length||0;g[k]={sessions:o,avgTasksPerSession:o>0?c.tasks/o:c.tasks};for(let[R,y]of Object.entries(c.byChannel||{})){let l=this.cost(y,c.model);d[R]=(d[R]||0)+l}}return{date:r,totalTasks:h,totalInput:a,totalOutput:p,totalCacheRead:C,totalCacheCreate:T,totalCost:n,topAgent:i,topCost:t,agentCosts:b,agentSessionStats:g,byChannel:d}}appendToTokenCosts(e,r){let s=r||_(process.cwd(),".agentx/TOKEN_COSTS.md"),h=n=>n<.01?n.toFixed(4):n.toFixed(2),a=n=>n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(0)}K`:String(n),p=Object.entries(e.agentCosts).sort(([,n],[,i])=>i-n).map(([n,i])=>{let t=e.agentSessionStats[n],b=t&&t.sessions>0?` (${t.sessions}s, ${t.avgTasksPerSession.toFixed(1)}t/s)`:"";return`${n}: $${h(i)}${b}`}).join(", "),C=Object.entries(e.byChannel).sort(([,n],[,i])=>i-n).map(([n,i])=>`${n}: $${h(i)}`).join(", "),T=`| ${e.date} | ${e.totalTasks} | ${a(e.totalInput)} | ${a(e.totalOutput)} | ${a(e.totalCacheRead)} | ${a(e.totalCacheCreate)} | $${h(e.totalCost)} | ${p} | ${C} |`;O(s)?K(s,T+`
|
|
2
|
-
`):E(s,`# Token Costs
|
|
3
|
-
|
|
4
|
-
Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).
|
|
5
|
-
Agent breakdown: \`agent: $cost (Ns, T.Tt/s)\` \u2014 sessions, tasks/session.
|
|
6
|
-
|
|
7
|
-
| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |
|
|
8
|
-
|------|-------|-------|--------|---------|---------|------|-----------|-------------|
|
|
9
|
-
`+T+`
|
|
10
|
-
`)}summary(e=7){let r=0,s=0,h=0,a=0,p=0,C=0,T=0,n={},i={};for(let d=0;d<e;d++){let k=new Date(Date.now()-d*864e5).toISOString().slice(0,10),c=this.getDate(k);if(c)for(let[m,o]of Object.entries(c.agents)){let R=this.cost(o);r+=o.tasks;let y=(o.inputTokens||0)+(o.tier2InputTokens||0),l=(o.outputTokens||0)+(o.tier2OutputTokens||0),S=(o.cacheReadTokens||0)+(o.tier2CacheReadTokens||0),v=(o.cacheCreateTokens||0)+(o.tier2CacheCreateTokens||0);s+=y,h+=l,a+=S,p+=v,C+=o.errors,T+=R;let u=n[m]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0,sessions:0};u.tasks+=o.tasks,u.input+=y,u.output+=l,u.cacheRead+=S,u.cacheCreate+=v,u.total=u.input+u.output+u.cacheRead+u.cacheCreate,u.totalDuration+=o.totalDuration,u.avgDuration=u.totalDuration/u.tasks,u.cost+=R,u.sessions+=o.sessionKeys?.length||0,o.model&&(u.model=o.model),n[m]=u;for(let[P,$]of Object.entries(o.byChannel||{})){let N=this.cost($,o.model),D=i[P]||{tasks:0,cost:0};D.tasks+=$.tasks,D.cost+=N,i[P]=D}}}let t=s+h+a+p,b=a+p>0?a/(a+p):0,g={};for(let[d,k]of Object.entries(n))g[d]={sessions:k.sessions,avgTasksPerSession:k.sessions>0?k.tasks/k.sessions:k.tasks};return{totalTasks:r,totalTokens:t,totalInput:s,totalOutput:h,totalCacheRead:a,totalCacheCreate:p,cacheHitRatio:b,totalErrors:C,totalCost:T,byAgent:n,byChannel:i,sessionStats:g}}filePath(e){return _(this.dir,`${e}.json`)}save(e){try{E(this.filePath(e.date),JSON.stringify(e,null,2))}catch{}}};function J(){let f=JSON.parse(JSON.stringify(w));try{let e=_(process.cwd(),G);if(!O(e))return f;let r=JSON.parse(A(e,"utf-8"));for(let[s,h]of Object.entries(r))f[s]||(f[s]={input:0,output:0,cacheRead:0,cacheCreate:0}),Object.assign(f[s],h)}catch{}return f}export{I as a};
|
|
11
|
-
//# sourceMappingURL=chunk-CXY2RYNI.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/token-tracker.ts"],"sourcesContent":["import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks token usage per agent per day with cache-aware, tier-aware cost\n// calculation. Persisted to .agentx/usage/{YYYY-MM-DD}.json.\n//\n// Design notes:\n// - Pricing is per-model-FAMILY (opus/sonnet/haiku). Exact model name\n// variants map via getModelFamily(). Override the table by dropping\n// .agentx/pricing/custom.json (same shape) — merged over defaults at\n// construction, lets operators adjust without a rebuild.\n// - Anthropic bills 1.5× input/output when a single request's total input\n// (input + cacheCreate + cacheRead) exceeds 200K tokens. We split each\n// recorded task into tier1/tier2 buckets and bill them separately so\n// long-context sessions are not undercounted.\n// - Per-channel breakdowns let operators see \"GitLab drove 70% of today's\n// spend\" — the lever for channel-specific session + context tuning.\n// - Session tracking (unique chatId:channel pairs seen today per agent)\n// gives an avg-tasks-per-session proxy — when it spikes, it's usually\n// retry/clarification chains, a signal the context budget or model\n// capability is under-sized for that traffic.\n\n// Anthropic pricing per million tokens (as of April 2026, 5-min ephemeral).\n// Keys here must match getModelFamily() output.\n//\n// IMPORTANT: Opus 4.6 is 3× cheaper than Opus 4.1. Haiku 4.5 is more expensive\n// than Haiku 3.5. Our getModelFamily() resolves the exact version so the cost\n// report matches what Anthropic actually bills. Source: LiteLLM pricing cache\n// at ~/.cache/codeburn/litellm-pricing.json + codeburn/src/models.ts.\n//\n// Cache multipliers (consistent across all models):\n// cache write (5-min ephemeral) = 1.25 × input\n// cache read = 0.10 × input\nexport const CACHE_AWARE_PRICING: Record<string, { input: number; output: number; cacheRead: number; cacheCreate: number }> = {\n // Opus 4.6 / 4.5 — $5/$25 per MTok\n \"claude-opus-4-6\": { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },\n \"claude-opus-4-5\": { input: 5, output: 25, cacheRead: 0.5, cacheCreate: 6.25 },\n // Opus 4.1 and earlier — $15/$75 per MTok (legacy, keep for historical runs)\n \"claude-opus\": { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },\n // Sonnet 4.6 / 4.5 — $3/$15 per MTok\n \"claude-sonnet\": { input: 3, output: 15, cacheRead: 0.3, cacheCreate: 3.75 },\n // Haiku 4.5 — $1/$5 per MTok (more expensive than Haiku 3.5)\n \"claude-haiku\": { input: 1, output: 5, cacheRead: 0.1, cacheCreate: 1.25 },\n}\n\n/** Per-request input size (input + cacheCreate + cacheRead) above this\n * threshold triggers Anthropic's extended-context tier: all four rates\n * get multiplied. Applies to Opus 4.x and Sonnet 4.x 1M variants. */\nexport const CONTEXT_TIER_THRESHOLD = 200_000\nexport const TIER2_MULTIPLIER = 1.5\n\nexport function getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) {\n // Opus 4.6 and 4.5 are 3× cheaper than 4.1 — must distinguish.\n if (lower.includes(\"4-6\") || lower.includes(\"4.6\") || lower.includes(\"opus-4-6\")) return \"claude-opus-4-6\"\n if (lower.includes(\"4-5\") || lower.includes(\"4.5\") || lower.includes(\"opus-4-5\")) return \"claude-opus-4-5\"\n return \"claude-opus\" // legacy 4.1 and earlier\n }\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\"\n}\n\nexport interface ChannelUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n tier2InputTokens: number\n tier2OutputTokens: number\n tier2CacheReadTokens: number\n tier2CacheCreateTokens: number\n totalDuration: number\n errors: number\n}\n\nexport interface AgentUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n /** Tokens from requests whose input crossed CONTEXT_TIER_THRESHOLD (billed at\n * TIER2_MULTIPLIER × normal rate). Kept separate from the tier1 buckets so\n * cost math stays honest when a session accumulates past 200K. */\n tier2InputTokens?: number\n tier2OutputTokens?: number\n tier2CacheReadTokens?: number\n tier2CacheCreateTokens?: number\n totalDuration: number\n errors: number\n model?: string\n /** Per-channel breakdown so we can attribute cost to Telegram vs GitLab vs WhatsApp. */\n byChannel?: Record<string, ChannelUsage>\n /** Unique session identifiers (channel:chatId) this agent participated in\n * today. Used to derive avg tasks/session. Capped at 500 entries to keep\n * the daily JSON small. */\n sessionKeys?: string[]\n}\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\nexport interface DailyReport {\n date: string\n totalTasks: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n totalCost: number\n topAgent: string\n topCost: number\n agentCosts: Record<string, number>\n /** Per-agent avg tasks/session — high values suggest retry chains. */\n agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n /** Aggregate cost by channel across all agents. */\n byChannel: Record<string, number>\n}\n\n/** Shape of a per-request usage report coming from the runtime. Keeps the\n * record() signature tidy and lets callers opt into tier2 accounting. */\nexport interface TaskUsage {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n}\n\nconst SESSION_KEYS_MAX = 500\nconst PRICING_OVERRIDE_PATH = \".agentx/pricing/custom.json\"\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n private pricing: typeof CACHE_AWARE_PRICING\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n this.pricing = loadPricingWithOverrides()\n }\n\n /** Returns the active pricing table (hardcoded defaults with any per-model\n * overrides merged in). Useful for UI / debugging. */\n getPricing(): typeof CACHE_AWARE_PRICING {\n return this.pricing\n }\n\n /**\n * Calculate cost for a slice of usage. Accepts either a full AgentUsage or\n * a narrowed ChannelUsage — both expose the same token buckets we price on.\n * Tier2 tokens are billed at TIER2_MULTIPLIER × the normal rate.\n */\n static calculateCost(usage: AgentUsage | ChannelUsage, model?: string, pricingTable: typeof CACHE_AWARE_PRICING = CACHE_AWARE_PRICING): number {\n const family = getModelFamily(model || (usage as AgentUsage).model || \"claude-sonnet\")\n const p = pricingTable[family] || pricingTable[\"claude-sonnet\"]\n const tier2In = (usage as any).tier2InputTokens || 0\n const tier2Out = (usage as any).tier2OutputTokens || 0\n const tier2CR = (usage as any).tier2CacheReadTokens || 0\n const tier2CW = (usage as any).tier2CacheCreateTokens || 0\n\n const tier1 =\n ((usage.inputTokens || 0) / 1_000_000) * p.input +\n ((usage.outputTokens || 0) / 1_000_000) * p.output +\n ((usage.cacheReadTokens || 0) / 1_000_000) * p.cacheRead +\n ((usage.cacheCreateTokens || 0) / 1_000_000) * p.cacheCreate\n\n const tier2 =\n (tier2In / 1_000_000) * p.input +\n (tier2Out / 1_000_000) * p.output +\n (tier2CR / 1_000_000) * p.cacheRead +\n (tier2CW / 1_000_000) * p.cacheCreate\n\n return tier1 + tier2 * TIER2_MULTIPLIER\n }\n\n /**\n * Instance-level cost calculation — uses the tracker's resolved pricing\n * table so any custom override file is honored. Prefer this over the\n * static variant when available.\n */\n cost(usage: AgentUsage | ChannelUsage, model?: string): number {\n return TokenTracker.calculateCost(usage, model, this.pricing)\n }\n\n /**\n * Record a task execution with real or estimated token counts.\n *\n * `channel` attributes the cost to a surface (telegram/gitlab/whatsapp/cron).\n * `sessionKey` is an opaque \"this chat on this day\" identifier — we use it\n * to count unique sessions so an avg-tasks-per-session metric falls out.\n */\n record(\n agentId: string,\n duration: number,\n realUsage?: TaskUsage,\n messageLength?: number,\n responseLength?: number,\n error?: boolean,\n model?: string,\n channel?: string,\n sessionKey?: string,\n ): void {\n const daily = this.today()\n const agent = daily.agents[agentId] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0, byChannel: {}, sessionKeys: [],\n }\n\n agent.tasks++\n agent.totalDuration += duration\n if (model) agent.model = model\n\n // Split this request into tier1/tier2 based on its TOTAL input size\n // (input + cacheCreate + cacheRead). Output inherits the tier of its\n // request. Estimated paths (no realUsage) are always tier1 — they're\n // too small to reach the threshold.\n let i1 = 0, o1 = 0, cr1 = 0, cw1 = 0, i2 = 0, o2 = 0, cr2 = 0, cw2 = 0\n if (realUsage) {\n const totalInput = realUsage.inputTokens + realUsage.cacheReadTokens + realUsage.cacheCreateTokens\n if (totalInput > CONTEXT_TIER_THRESHOLD) {\n i2 = realUsage.inputTokens\n o2 = realUsage.outputTokens\n cr2 = realUsage.cacheReadTokens\n cw2 = realUsage.cacheCreateTokens\n } else {\n i1 = realUsage.inputTokens\n o1 = realUsage.outputTokens\n cr1 = realUsage.cacheReadTokens\n cw1 = realUsage.cacheCreateTokens\n }\n } else if (messageLength !== undefined && responseLength !== undefined) {\n i1 = Math.ceil(messageLength / 4)\n o1 = Math.ceil(responseLength / 4)\n }\n\n agent.inputTokens += i1\n agent.outputTokens += o1\n agent.cacheReadTokens += cr1\n agent.cacheCreateTokens += cw1\n agent.tier2InputTokens = (agent.tier2InputTokens || 0) + i2\n agent.tier2OutputTokens = (agent.tier2OutputTokens || 0) + o2\n agent.tier2CacheReadTokens = (agent.tier2CacheReadTokens || 0) + cr2\n agent.tier2CacheCreateTokens = (agent.tier2CacheCreateTokens || 0) + cw2\n\n if (error) agent.errors++\n\n // Per-channel attribution. Unknown channel falls into \"other\".\n if (!agent.byChannel) agent.byChannel = {}\n const chKey = (channel || \"other\").toLowerCase()\n const ch = agent.byChannel[chKey] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0,\n }\n ch.tasks++\n ch.totalDuration += duration\n ch.inputTokens += i1; ch.outputTokens += o1\n ch.cacheReadTokens += cr1; ch.cacheCreateTokens += cw1\n ch.tier2InputTokens += i2; ch.tier2OutputTokens += o2\n ch.tier2CacheReadTokens += cr2; ch.tier2CacheCreateTokens += cw2\n if (error) ch.errors++\n agent.byChannel[chKey] = ch\n\n // Session tracking — dedup sessions by opaque key (normally channel:chatId).\n if (sessionKey) {\n if (!agent.sessionKeys) agent.sessionKeys = []\n if (!agent.sessionKeys.includes(sessionKey)) {\n agent.sessionKeys.push(sessionKey)\n if (agent.sessionKeys.length > SESSION_KEYS_MAX) {\n agent.sessionKeys.splice(0, agent.sessionKeys.length - SESSION_KEYS_MAX)\n }\n }\n }\n\n daily.agents[agentId] = agent\n this.save(daily)\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 * Generate a daily cost report for a given date. Includes per-agent,\n * per-channel costs and session statistics.\n */\n generateDailyReport(date?: string): DailyReport | null {\n const targetDate = date || new Date().toISOString().slice(0, 10)\n const usage = this.getDate(targetDate)\n if (!usage) return null\n\n let totalTasks = 0, totalInput = 0, totalOutput = 0\n let totalCacheRead = 0, totalCacheCreate = 0, totalCost = 0\n let topAgent = \"\", topCost = 0\n const agentCosts: Record<string, number> = {}\n const agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n const byChannel: Record<string, number> = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = this.cost(agent)\n agentCosts[id] = cost\n totalTasks += agent.tasks\n totalInput += (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n totalOutput += (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n totalCacheRead += (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n totalCacheCreate += (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalCost += cost\n if (cost > topCost) { topCost = cost; topAgent = id }\n\n const sessions = agent.sessionKeys?.length || 0\n agentSessionStats[id] = {\n sessions,\n avgTasksPerSession: sessions > 0 ? agent.tasks / sessions : agent.tasks,\n }\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n byChannel[chKey] = (byChannel[chKey] || 0) + chCost\n }\n }\n\n return {\n date: targetDate, totalTasks, totalInput, totalOutput,\n totalCacheRead, totalCacheCreate, totalCost, topAgent, topCost, agentCosts,\n agentSessionStats, byChannel,\n }\n }\n\n /**\n * Append a daily report row to TOKEN_COSTS.md. Adds channel + session\n * breakdown inline for at-a-glance spend attribution.\n */\n appendToTokenCosts(report: DailyReport, filePath?: string): void {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n const fmt = (n: number) => n < 0.01 ? n.toFixed(4) : n.toFixed(2)\n const fmtTok = (n: number) =>\n n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` :\n n >= 1_000 ? `${(n / 1_000).toFixed(0)}K` :\n String(n)\n\n const agentBreakdown = Object.entries(report.agentCosts)\n .sort(([, a], [, b]) => b - a)\n .map(([id, cost]) => {\n const stats = report.agentSessionStats[id]\n const sessTag = stats && stats.sessions > 0 ? ` (${stats.sessions}s, ${stats.avgTasksPerSession.toFixed(1)}t/s)` : \"\"\n return `${id}: $${fmt(cost)}${sessTag}`\n })\n .join(\", \")\n\n const channelBreakdown = Object.entries(report.byChannel)\n .sort(([, a], [, b]) => b - a)\n .map(([ch, cost]) => `${ch}: $${fmt(cost)}`)\n .join(\", \")\n\n const row = `| ${report.date} | ${report.totalTasks} | ${fmtTok(report.totalInput)} | ${fmtTok(report.totalOutput)} | ${fmtTok(report.totalCacheRead)} | ${fmtTok(report.totalCacheCreate)} | $${fmt(report.totalCost)} | ${agentBreakdown} | ${channelBreakdown} |`\n\n if (!existsSync(file)) {\n const header =\n \"# Token Costs\\n\\n\" +\n \"Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).\\n\" +\n \"Agent breakdown: `agent: $cost (Ns, T.Tt/s)` — sessions, tasks/session.\\n\\n\" +\n \"| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |\\n\" +\n \"|------|-------|-------|--------|---------|---------|------|-----------|-------------|\\n\"\n writeFileSync(file, header + row + \"\\n\")\n } else {\n appendFileSync(file, row + \"\\n\")\n }\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n cacheHitRatio: number\n totalErrors: number\n totalCost: number\n byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; cost: number }>\n byChannel: Record<string, { tasks: number; cost: number }>\n sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n } {\n let totalTasks = 0\n let totalInput = 0\n let totalOutput = 0\n let totalCacheRead = 0\n let totalCacheCreate = 0\n let totalErrors = 0\n let totalCost = 0\n const byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; totalDuration: number; cost: number; model?: string; sessions: number }> = {}\n const byChannel: Record<string, { tasks: number; cost: 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 const cost = this.cost(agent)\n totalTasks += agent.tasks\n const agentInput = (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n const agentOutput = (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n const agentCR = (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n const agentCW = (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalInput += agentInput\n totalOutput += agentOutput\n totalCacheRead += agentCR\n totalCacheCreate += agentCW\n totalErrors += agent.errors\n totalCost += cost\n\n const existing = byAgent[id] || { tasks: 0, input: 0, output: 0, cacheRead: 0, cacheCreate: 0, total: 0, avgDuration: 0, totalDuration: 0, cost: 0, sessions: 0 }\n existing.tasks += agent.tasks\n existing.input += agentInput\n existing.output += agentOutput\n existing.cacheRead += agentCR\n existing.cacheCreate += agentCW\n existing.total = existing.input + existing.output + existing.cacheRead + existing.cacheCreate\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n existing.cost += cost\n existing.sessions += agent.sessionKeys?.length || 0\n if (agent.model) existing.model = agent.model\n byAgent[id] = existing\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n const acc = byChannel[chKey] || { tasks: 0, cost: 0 }\n acc.tasks += ch.tasks\n acc.cost += chCost\n byChannel[chKey] = acc\n }\n }\n }\n\n const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheCreate\n const cacheHitRatio = (totalCacheRead + totalCacheCreate) > 0\n ? totalCacheRead / (totalCacheRead + totalCacheCreate)\n : 0\n\n const sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n for (const [id, a] of Object.entries(byAgent)) {\n sessionStats[id] = {\n sessions: a.sessions,\n avgTasksPerSession: a.sessions > 0 ? a.tasks / a.sessions : a.tasks,\n }\n }\n\n return { totalTasks, totalTokens, totalInput, totalOutput, totalCacheRead, totalCacheCreate, cacheHitRatio, totalErrors, totalCost, byAgent, byChannel, sessionStats }\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\n/**\n * Load CACHE_AWARE_PRICING and merge any operator-supplied override. The\n * override file is optional; operators tweak rates (e.g. when Anthropic\n * publishes a price change) without rebuilding the daemon.\n */\nfunction loadPricingWithOverrides(): typeof CACHE_AWARE_PRICING {\n const result = JSON.parse(JSON.stringify(CACHE_AWARE_PRICING))\n try {\n const p = resolve(process.cwd(), PRICING_OVERRIDE_PATH)\n if (!existsSync(p)) return result\n const raw = JSON.parse(readFileSync(p, \"utf-8\"))\n for (const [family, rates] of Object.entries(raw)) {\n if (!result[family]) result[family] = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 }\n Object.assign(result[family], rates as object)\n }\n } catch {\n // Malformed override file — stay with defaults rather than crash at boot.\n }\n return result\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","resolve","CACHE_AWARE_PRICING","CONTEXT_TIER_THRESHOLD","TIER2_MULTIPLIER","getModelFamily","model","lower","SESSION_KEYS_MAX","PRICING_OVERRIDE_PATH","TokenTracker","baseDir","loadPricingWithOverrides","usage","pricingTable","family","p","tier2In","tier2Out","tier2CR","tier2CW","tier1","tier2","agentId","duration","realUsage","messageLength","responseLength","error","channel","sessionKey","daily","agent","i1","o1","cr1","cw1","i2","o2","cr2","cw2","chKey","ch","date","path","targetDate","totalTasks","totalInput","totalOutput","totalCacheRead","totalCacheCreate","totalCost","topAgent","topCost","agentCosts","agentSessionStats","byChannel","id","cost","sessions","chCost","report","filePath","file","fmt","fmtTok","agentBreakdown","a","b","stats","sessTag","channelBreakdown","row","days","totalErrors","byAgent","i","agentInput","agentOutput","agentCR","agentCW","existing","acc","totalTokens","cacheHitRatio","sessionStats","result","raw","rates"]}
|
package/dist/chunk-FIOB3MEX.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var e=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var i=Object.prototype.hasOwnProperty;var j=(a,b,c)=>b in a?e(a,b,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[b]=c;var l=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+a+'" is not supported')});var m=(a,b)=>()=>(a&&(b=a(a=0)),b);var n=(a,b)=>{for(var c in b)e(a,c,{get:b[c],enumerable:!0})},k=(a,b,c,f)=>{if(b&&typeof b=="object"||typeof b=="function")for(let d of h(b))!i.call(a,d)&&d!==c&&e(a,d,{get:()=>b[d],enumerable:!(f=g(b,d))||f.enumerable});return a};var o=a=>k(e({},"__esModule",{value:!0}),a);var p=(a,b,c)=>(j(a,typeof b!="symbol"?b+"":b,c),c);export{l as a,m as b,n as c,o as d,p as e};
|
|
2
|
-
//# sourceMappingURL=chunk-FIOB3MEX.js.map
|