agentix-cli 0.14.0 → 0.15.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.
Files changed (84) hide show
  1. package/README.md +64 -34
  2. package/dist/agent-LRSSVFDR.js +2 -0
  3. package/dist/board-dashboard-DAPHL6W2.js +4508 -0
  4. package/dist/board-dashboard-DAPHL6W2.js.map +1 -0
  5. package/dist/chunk-3XULOWYB.js +199 -0
  6. package/dist/chunk-3XULOWYB.js.map +1 -0
  7. package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
  8. package/dist/{chunk-XVYMZ4M7.js → chunk-AU37OREL.js} +5 -5
  9. package/dist/chunk-B4Q4X7D5.js +6 -0
  10. package/dist/chunk-B4Q4X7D5.js.map +1 -0
  11. package/dist/chunk-CXY2RYNI.js +11 -0
  12. package/dist/chunk-CXY2RYNI.js.map +1 -0
  13. package/dist/chunk-FIOB3MEX.js +2 -0
  14. package/dist/chunk-HLRVDCNH.js +5 -0
  15. package/dist/{chunk-NJR5WF5E.js.map → chunk-HLRVDCNH.js.map} +1 -1
  16. package/dist/chunk-IEVEMKAI.js +168 -0
  17. package/dist/chunk-IEVEMKAI.js.map +1 -0
  18. package/dist/{chunk-O72LPMBR.js → chunk-RB7265LO.js} +2 -2
  19. package/dist/chunk-RGUYBH77.js +7 -0
  20. package/dist/chunk-RGUYBH77.js.map +1 -0
  21. package/dist/chunk-WVNSWBCU.js +138 -0
  22. package/dist/chunk-WVNSWBCU.js.map +1 -0
  23. package/dist/cli.js +34 -24
  24. package/dist/cli.js.map +1 -1
  25. package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
  26. package/dist/config-DCJYECL4.js +2 -0
  27. package/dist/config-mutator-6EJHSP3Y.js +2 -0
  28. package/dist/debug-3MEUO2P2.js +2 -0
  29. package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
  30. package/dist/index.d.ts +224 -1
  31. package/dist/index.js +11 -11
  32. package/dist/index.js.map +1 -1
  33. package/dist/loader-PTCXLJEX.js +2 -0
  34. package/dist/memory-extract-AD7KMRPC.js +2 -0
  35. package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
  36. package/dist/registry-5ND7GLFJ.js +2 -0
  37. package/dist/scheduler-FCXOXCN5.js +2 -0
  38. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  39. package/dist/{usage-dashboard-M7AFIGMJ.js → usage-dashboard-QX3CF6KK.js} +8 -8
  40. package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
  41. package/dist/whatsapp-state-6D6FHHD2.js +2 -0
  42. package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
  43. package/dist/workspace-setup-ZWTT42XT.js +2 -0
  44. package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
  45. package/package.json +2 -1
  46. package/dist/agent-DW3NYIIL.js +0 -2
  47. package/dist/board-dashboard-6TKE6NMV.js +0 -1435
  48. package/dist/board-dashboard-6TKE6NMV.js.map +0 -1
  49. package/dist/chunk-4YCH6IZV.js +0 -2
  50. package/dist/chunk-DYOBFJLX.js +0 -11
  51. package/dist/chunk-DYOBFJLX.js.map +0 -1
  52. package/dist/chunk-ITZEVXZ4.js +0 -2
  53. package/dist/chunk-ITZEVXZ4.js.map +0 -1
  54. package/dist/chunk-M33CB2H4.js +0 -6
  55. package/dist/chunk-M33CB2H4.js.map +0 -1
  56. package/dist/chunk-NJR5WF5E.js +0 -5
  57. package/dist/chunk-RB73FZXJ.js +0 -133
  58. package/dist/chunk-RB73FZXJ.js.map +0 -1
  59. package/dist/chunk-UPT2UMF6.js +0 -159
  60. package/dist/chunk-UPT2UMF6.js.map +0 -1
  61. package/dist/config-7QJ2QJIC.js +0 -2
  62. package/dist/config-mutator-GAEVGHDD.js +0 -2
  63. package/dist/debug-N3B5NVJU.js +0 -2
  64. package/dist/loader-YQE6XJBT.js +0 -2
  65. package/dist/memory-extract-RGPUPMKU.js +0 -2
  66. package/dist/registry-YVAG4EO5.js +0 -2
  67. package/dist/usage-dashboard-M7AFIGMJ.js.map +0 -1
  68. package/dist/workspace-setup-QQTCJY4B.js +0 -2
  69. /package/dist/{agent-DW3NYIIL.js.map → agent-LRSSVFDR.js.map} +0 -0
  70. /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
  71. /package/dist/{chunk-XVYMZ4M7.js.map → chunk-AU37OREL.js.map} +0 -0
  72. /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
  73. /package/dist/{chunk-O72LPMBR.js.map → chunk-RB7265LO.js.map} +0 -0
  74. /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
  75. /package/dist/{config-7QJ2QJIC.js.map → config-DCJYECL4.js.map} +0 -0
  76. /package/dist/{config-mutator-GAEVGHDD.js.map → config-mutator-6EJHSP3Y.js.map} +0 -0
  77. /package/dist/{debug-N3B5NVJU.js.map → debug-3MEUO2P2.js.map} +0 -0
  78. /package/dist/{heal-OTGT5HHJ.js.map → heal-WKY5I4KB.js.map} +0 -0
  79. /package/dist/{loader-YQE6XJBT.js.map → loader-PTCXLJEX.js.map} +0 -0
  80. /package/dist/{memory-extract-RGPUPMKU.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
  81. /package/dist/{providers-G3WZ4RVJ.js.map → providers-VUIWO3ZK.js.map} +0 -0
  82. /package/dist/{registry-YVAG4EO5.js.map → registry-5ND7GLFJ.js.map} +0 -0
  83. /package/dist/{workspace-setup-QQTCJY4B.js.map → scheduler-FCXOXCN5.js.map} +0 -0
  84. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/agents/runtime.ts","../src/agents/sessions.ts","../src/wiki/store.ts","../src/wiki/hub.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 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\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: errMsg.slice(0, 300),\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): 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\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 // Claude stream-json emits different event types\n // \"assistant\" messages with content contain the text\n if (event.type === \"assistant\" && event.message?.content) {\n for (const block of event.message.content) {\n if (block.type === \"text\" && block.text) {\n const delta = block.text.slice(fullText.length)\n if (delta) {\n fullText = block.text\n onDelta(delta, fullText)\n }\n }\n }\n }\n\n // \"content_block_delta\" for streaming text\n if (event.type === \"content_block_delta\" && event.delta?.text) {\n fullText += event.delta.text\n onDelta(event.delta.text, fullText)\n }\n\n // \"result\" event contains final text\n if (event.type === \"result\" && event.result) {\n const resultText = typeof event.result === \"string\"\n ? event.result\n : event.result\n if (typeof resultText === \"string\" && resultText.length > fullText.length) {\n const delta = resultText.slice(fullText.length)\n fullText = resultText\n if (delta) onDelta(delta, fullText)\n }\n // 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: stderr || `Claude Code exited with code ${result.exitCode}`,\n duration: Date.now() - start,\n }\n }\n\n return {\n content: fullText,\n duration: Date.now() - start,\n usage: streamUsage,\n billedModel: streamBilledModel,\n claudeSessionId: streamSessionId,\n }\n } catch (error: any) {\n return {\n content: fullText || \"\",\n error: 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): Promise<AgentResponse> {\n switch (agent.tier) {\n case \"claude-code\":\n if (onDelta) {\n return executeClaudeCodeStreaming(agent, task, onDelta, historyContext, resumeSessionId)\n }\n return executeClaudeCode(agent, task, historyContext, resumeSessionId)\n\n case \"sdk\": {\n const providerName = agent.provider || \"claude\"\n const apiKey = providers[providerName]?.apiKey\n if (!apiKey) {\n return {\n content: \"\",\n error: `No API key for provider \"${providerName}\". Configure providers.${providerName}.apiKey`,\n }\n }\n return executeSdk(agent, task, apiKey, 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","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","import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join, relative, dirname } from \"path\"\nimport type { WikiArticle, WikiArticleMeta, WikiEntry, WikiIndex, WikiAccess } from \"./types\"\nimport { buildIndex, buildIndexCached, scoreAll } from \"../memory/bm25\"\n\n// --- Wiki Store: filesystem-based knowledge base with permissions ---\n//\n// Structure:\n// .agentx/wiki/\n// _index.md # Master index with aliases\n// _backlinks.json # Reverse link index\n// raw/entries/ # One .md per ingested entry\n// {directories}/ # Articles organized by type\n//\n// Permission model:\n// - private: only owner agent reads/writes\n// - shared: owner writes, listed agents read\n// - public: owner writes, all agents read\n\nexport class WikiStore {\n private baseDir: string\n private rawDir: string\n private log: (...args: unknown[]) => void\n\n constructor(\n baseDir: string = resolve(process.cwd(), \".agentx/wiki\"),\n log: (...args: unknown[]) => void = console.error.bind(console, \"[wiki]\"),\n ) {\n this.baseDir = baseDir\n this.rawDir = resolve(baseDir, \"raw/entries\")\n this.log = log\n\n mkdirSync(this.rawDir, { recursive: true })\n mkdirSync(resolve(baseDir, \"raw\"), { recursive: true })\n\n // Initialize schema if missing\n this.ensureSchema()\n }\n\n // --- Permission checks ---\n\n /**\n * Check if an agent can read an article.\n */\n canRead(article: WikiArticleMeta, agentId?: string): boolean {\n if (article.access === \"public\") return true\n if (!agentId) return false\n if (article.owner === agentId) return true\n if (article.access === \"shared\" && article.sharedWith?.includes(agentId)) return true\n return false\n }\n\n /**\n * Check if an agent can write an article.\n */\n canWrite(article: WikiArticleMeta, agentId: string): boolean {\n return article.owner === agentId\n }\n\n // --- Raw entries (ingestion) ---\n\n /**\n * Add a raw entry (from a conversation, cron result, etc.)\n */\n addEntry(entry: WikiEntry): string {\n const filename = `${entry.date}_${entry.id}.md`\n const filepath = resolve(this.rawDir, filename)\n\n const frontmatter = [\n \"---\",\n `id: ${entry.id}`,\n `date: ${entry.date}`,\n `agent: ${entry.agentId}`,\n `source: ${entry.source}`,\n ]\n if (entry.sourceContext) frontmatter.push(`context: ${entry.sourceContext}`)\n if (entry.meta) {\n for (const [k, v] of Object.entries(entry.meta)) {\n frontmatter.push(`${k}: ${JSON.stringify(v)}`)\n }\n }\n frontmatter.push(\"---\", \"\", entry.content)\n\n writeFileSync(filepath, frontmatter.join(\"\\n\"))\n this.appendLog(\"ingest\", `${entry.id} from ${entry.agentId} via ${entry.source}`)\n return filename\n }\n\n /**\n * List raw entries, optionally filtered by agent or date range.\n */\n listEntries(filter?: { agentId?: string; after?: string; before?: string }): WikiEntry[] {\n if (!existsSync(this.rawDir)) return []\n\n const files = readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).sort()\n const entries: WikiEntry[] = []\n\n for (const file of files) {\n const content = readFileSync(resolve(this.rawDir, file), \"utf-8\")\n const entry = this.parseEntry(content, file)\n if (!entry) continue\n\n if (filter?.agentId && entry.agentId !== filter.agentId) continue\n if (filter?.after && entry.date < filter.after) continue\n if (filter?.before && entry.date > filter.before) continue\n\n entries.push(entry)\n }\n\n return entries\n }\n\n private parseEntry(content: string, filename: string): WikiEntry | null {\n const fmMatch = content.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const body = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim() || \"\"\n }\n\n return {\n id: get(\"id\") || filename.replace(\".md\", \"\"),\n date: get(\"date\"),\n agentId: get(\"agent\"),\n source: get(\"source\"),\n sourceContext: get(\"context\") || undefined,\n content: body,\n }\n }\n\n // --- Articles (compiled knowledge) ---\n\n /**\n * Write or update a wiki article.\n */\n writeArticle(\n path: string,\n meta: WikiArticleMeta,\n content: string,\n agentId: string,\n ): boolean {\n // LLM picks the path. We just write the file.\n const existing = this.readArticle(path)\n if (existing && !this.canWrite(existing.meta, agentId)) {\n this.log(`Permission denied: \"${agentId}\" cannot write \"${path}\" (owner: ${existing.meta.owner})`)\n return false\n }\n\n const fullPath = resolve(this.baseDir, path)\n\n // Version the previous content before overwriting (immutable history)\n if (existing) {\n this.saveVersion(path, fullPath)\n }\n\n mkdirSync(dirname(fullPath), { recursive: true })\n\n const frontmatter = [\n \"---\",\n `title: \"${meta.title}\"`,\n `tags: [${(meta.tags || []).map(t => `\"${t}\"`).join(\", \")}]`,\n `owner: ${meta.owner}`,\n `access: ${meta.access}`,\n ]\n if (meta.sharedWith?.length) {\n frontmatter.push(`shared_with: [${meta.sharedWith.map(s => `\"${s}\"`).join(\", \")}]`)\n }\n frontmatter.push(\n `created: ${meta.created}`,\n `last_updated: ${meta.lastUpdated}`,\n `sources: [${meta.sources.map(s => `\"${s}\"`).join(\", \")}]`,\n )\n frontmatter.push(\"---\", \"\", content)\n\n writeFileSync(fullPath, frontmatter.join(\"\\n\"))\n const action = existing ? \"update\" : \"create\"\n this.appendLog(action, `${meta.title} [${(meta.tags || []).slice(0, 5).join(\", \")}] by ${agentId} at ${path}`)\n return true\n }\n\n // --- Article Versioning ---\n\n /**\n * Save current article content as an immutable version before overwriting.\n * Versions stored in _versions/{article-path}/{timestamp}.md\n */\n private saveVersion(articlePath: string, fullPath: string): void {\n try {\n if (!existsSync(fullPath)) return\n const content = readFileSync(fullPath, \"utf-8\")\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\")\n const versionDir = resolve(this.baseDir, \"_versions\", articlePath.replace(/\\.md$/, \"\"))\n mkdirSync(versionDir, { recursive: true })\n writeFileSync(resolve(versionDir, `${ts}.md`), content)\n } catch {\n // Version save is best-effort\n }\n }\n\n /**\n * List all versions of an article (newest first).\n */\n getVersions(articlePath: string): Array<{ timestamp: string; path: string }> {\n const versionDir = resolve(this.baseDir, \"_versions\", articlePath.replace(/\\.md$/, \"\"))\n if (!existsSync(versionDir)) return []\n\n try {\n return readdirSync(versionDir)\n .filter(f => f.endsWith(\".md\"))\n .sort()\n .reverse()\n .map(f => ({\n timestamp: f.replace(/\\.md$/, \"\").replace(/-/g, (m, i) => i < 19 ? (i === 10 ? \"T\" : i === 13 || i === 16 ? \":\" : \"-\") : m),\n path: resolve(versionDir, f),\n }))\n } catch {\n return []\n }\n }\n\n /**\n * Restore a specific version of an article.\n */\n restoreVersion(articlePath: string, versionTimestamp: string): boolean {\n const versions = this.getVersions(articlePath)\n const version = versions.find(v => v.timestamp.startsWith(versionTimestamp))\n if (!version) return false\n\n try {\n const content = readFileSync(version.path, \"utf-8\")\n const fullPath = resolve(this.baseDir, articlePath)\n // Save current as a version too before restoring\n this.saveVersion(articlePath, fullPath)\n writeFileSync(fullPath, content)\n this.appendLog(\"restore\", `${articlePath} restored to ${versionTimestamp}`)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Read an article. Returns null if not found.\n */\n readArticle(path: string): WikiArticle | null {\n const fullPath = resolve(this.baseDir, path)\n if (!existsSync(fullPath)) return null\n\n const raw = readFileSync(fullPath, \"utf-8\")\n return this.parseArticle(raw, path)\n }\n\n /**\n * Read an article with permission check.\n */\n readArticleAs(path: string, agentId: string): WikiArticle | null {\n const article = this.readArticle(path)\n if (!article) return null\n if (!this.canRead(article.meta, agentId)) return null\n return article\n }\n\n private parseArticle(raw: string, path: string): WikiArticle | null {\n const fmMatch = raw.match(/^---\\n([\\s\\S]*?)\\n---\\n([\\s\\S]*)$/)\n if (!fmMatch) return null\n\n const fm = fmMatch[1]\n const content = fmMatch[2].trim()\n\n const get = (key: string): string => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*(.+)$`, \"m\"))\n return m?.[1]?.trim().replace(/^\"(.*)\"$/, \"$1\") || \"\"\n }\n\n const getArray = (key: string): string[] => {\n const m = fm.match(new RegExp(`^${key}:\\\\s*\\\\[(.*)\\\\]$`, \"m\"))\n if (!m) return []\n return m[1].split(\",\").map(s => s.trim().replace(/^\"(.*)\"$/, \"$1\")).filter(Boolean)\n }\n\n // Collect tags from all legacy fields + explicit tags\n const tags = [\n ...getArray(\"tags\"),\n // Pull legacy fields into tags for backwards compat\n ...(get(\"kind\") ? [get(\"kind\")] : []),\n ...(get(\"type\") ? [get(\"type\")] : []),\n ].filter((v, i, a) => v && a.indexOf(v) === i) // dedupe\n\n return {\n meta: {\n title: get(\"title\"),\n tags,\n owner: get(\"owner\"),\n access: (get(\"access\") as WikiAccess) || \"public\",\n sharedWith: getArray(\"shared_with\"),\n created: get(\"created\"),\n lastUpdated: get(\"last_updated\"),\n sources: getArray(\"sources\"),\n },\n content,\n path,\n }\n }\n\n // --- Index and discovery ---\n\n /**\n * List all articles accessible to an agent.\n */\n listArticles(agentId: string): WikiArticle[] {\n const articles: WikiArticle[] = []\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (article && this.canRead(article.meta, agentId)) {\n articles.push(article)\n }\n })\n return articles\n }\n\n /**\n * Search articles by keyword. Returns articles the agent can read.\n */\n search(query: string, agentId: string, maxResults: number = 10): WikiArticle[] {\n const queryLower = query.toLowerCase()\n const articles: WikiArticle[] = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article || !this.canRead(article.meta, agentId)) return\n articles.push(article)\n })\n\n if (articles.length === 0) return []\n\n // BM25 over article content (cached to disk)\n const docs = articles.map((a) => a.content)\n const cachePath = resolve(this.baseDir, \"_bm25_cache.json\")\n const index = buildIndexCached(docs, cachePath)\n const bm25Scores = scoreAll(query, index)\n const scoreMap = new Map(bm25Scores.map((r) => [r.docIndex, r.score]))\n\n const results = articles.map((article, i) => {\n let score = scoreMap.get(i) ?? 0\n // Additive bonuses for title and tag matches\n if (article.meta.title.toLowerCase().includes(queryLower)) score += 10\n if (article.meta.tags?.some(t => t.toLowerCase().includes(queryLower))) score += 5\n return { article, score }\n })\n\n return results\n .filter((r) => r.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults)\n .map((r) => r.article)\n }\n\n /**\n * Find articles relevant to a message (for context injection).\n * Extracts keywords from the message and searches the wiki.\n */\n findRelevant(message: string, agentId?: string, maxArticles: number = 3): WikiArticle[] {\n const articles: WikiArticle[] = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article || !this.canRead(article.meta, agentId)) return\n articles.push(article)\n })\n\n if (articles.length === 0) return []\n\n // Single BM25 pass over title + tags + content (cached)\n const docs = articles.map(\n (a) => `${a.meta.title} ${(a.meta.tags || []).join(\" \")} ${a.content}`,\n )\n const cachePath = resolve(this.baseDir, \"_bm25_relevance_cache.json\")\n const index = buildIndexCached(docs, cachePath)\n const scored = scoreAll(message, index)\n\n return scored\n .slice(0, maxArticles)\n .map((s) => articles[s.docIndex])\n }\n\n /**\n * Build context string from relevant wiki articles for prompt injection.\n * Token-efficient: includes title + first ~500 chars of each article.\n */\n buildContext(articles: WikiArticle[], maxChars: number = 4000): string {\n if (articles.length === 0) return \"\"\n\n const lines: string[] = [\"[Wiki Knowledge]\"]\n\n let totalChars = 0\n for (const article of articles) {\n const header = `\\n## ${article.meta.title} [${(article.meta.tags || []).slice(0, 3).join(\", \")}]`\n const body = article.content.length > 600\n ? article.content.slice(0, 600) + \"...\"\n : article.content\n\n const chunk = header + \"\\n\" + body\n if (totalChars + chunk.length > maxChars) break\n\n lines.push(chunk)\n totalChars += chunk.length\n }\n\n lines.push(\"\\n[End Wiki Knowledge]\")\n return lines.join(\"\\n\")\n }\n\n /**\n * Rebuild the master index.\n */\n rebuildIndex(): WikiIndex {\n const articles: WikiIndex[\"articles\"] = []\n const backlinks = new Map<string, number>()\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n // Count wikilinks for backlinks\n const links = article.content.match(/\\[\\[([^\\]]+)\\]\\]/g) || []\n for (const link of links) {\n const target = link.replace(/\\[\\[|\\]\\]/g, \"\")\n backlinks.set(target, (backlinks.get(target) || 0) + 1)\n }\n\n // Extract aliases from title and tags\n const aliases = [article.meta.title.toLowerCase()]\n if (article.meta.tags) {\n aliases.push(...article.meta.tags.map(t => t.toLowerCase()))\n }\n\n articles.push({\n path: relPath,\n title: article.meta.title,\n tags: article.meta.tags || [],\n owner: article.meta.owner,\n access: article.meta.access,\n sharedWith: article.meta.sharedWith,\n aliases,\n backlinks: backlinks.get(article.meta.title) || 0,\n sources: article.meta.sources,\n lastUpdated: article.meta.lastUpdated,\n })\n })\n\n const index: WikiIndex = {\n articles,\n lastRebuilt: new Date().toISOString(),\n }\n\n // Write index files\n writeFileSync(\n resolve(this.baseDir, \"_index.json\"),\n JSON.stringify(index, null, 2),\n )\n\n // Write human-readable WIKI.md\n const md = [\"# Wiki Index\", \"\", `Last rebuilt: ${index.lastRebuilt}`, \"\"]\n const byType = new Map<string, typeof articles>()\n for (const a of articles) {\n const type = (a as any).type || a.access || \"public\"\n const list = byType.get(type) || []\n list.push(a)\n byType.set(type, list)\n }\n\n for (const [type, list] of Array.from(byType.entries()).sort()) {\n md.push(`## ${type}`, \"\")\n for (const a of list.sort((x, y) => x.title.localeCompare(y.title))) {\n const access = a.access === \"private\" ? \" (private)\" : a.access === \"shared\" ? \" (shared)\" : \"\"\n md.push(`- [${a.title}](${a.path})${access} — owner: ${a.owner}`)\n }\n md.push(\"\")\n }\n\n writeFileSync(resolve(this.baseDir, \"WIKI.md\"), md.join(\"\\n\"))\n\n this.log(`Index rebuilt: ${articles.length} articles`)\n this.appendLog(\"rebuild-index\", `${articles.length} articles indexed`)\n return index\n }\n\n /**\n * Get wiki stats.\n */\n stats(): {\n totalArticles: number\n totalEntries: number\n articlesByType: Record<string, number>\n articlesByAccess: Record<string, number>\n articlesByOwner: Record<string, number>\n } {\n let totalArticles = 0\n const byType: Record<string, number> = {}\n const byAccess: Record<string, number> = {}\n const byOwner: Record<string, number> = {}\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\") return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n totalArticles++\n for (const tag of (article.meta.tags || [])) {\n byType[tag] = (byType[tag] || 0) + 1\n }\n byAccess[article.meta.access] = (byAccess[article.meta.access] || 0) + 1\n byOwner[article.meta.owner] = (byOwner[article.meta.owner] || 0) + 1\n })\n\n const entryFiles = existsSync(this.rawDir)\n ? readdirSync(this.rawDir).filter(f => f.endsWith(\".md\")).length\n : 0\n\n return {\n totalArticles,\n totalEntries: entryFiles,\n articlesByType: byType,\n articlesByAccess: byAccess,\n articlesByOwner: byOwner,\n }\n }\n\n // --- Helpers ---\n\n // --- Log (chronological, append-only) ---\n\n /**\n * Append an entry to log.md.\n * Format: ## [YYYY-MM-DD HH:MM] action | details\n */\n appendLog(action: string, details: string): void {\n const logPath = resolve(this.baseDir, \"log.md\")\n const timestamp = new Date().toISOString().replace(\"T\", \" \").slice(0, 16)\n const entry = `## [${timestamp}] ${action} | ${details}\\n\\n`\n\n if (!existsSync(logPath)) {\n writeFileSync(logPath, \"# Wiki Log\\n\\nChronological record of all wiki operations.\\n\\n\")\n }\n\n appendFileSync(logPath, entry)\n }\n\n /**\n * Get recent log entries.\n */\n getLog(limit: number = 20): string[] {\n const logPath = resolve(this.baseDir, \"log.md\")\n if (!existsSync(logPath)) return []\n\n const content = readFileSync(logPath, \"utf-8\")\n const entries = content.split(/^## /m).filter(e => e.startsWith(\"[\")).slice(-limit)\n return entries.map(e => e.trim())\n }\n\n // --- Schema ---\n\n /**\n * Create _schema.md if it doesn't exist.\n * This tells the LLM how to operate on the wiki.\n */\n private ensureSchema(): void {\n const schemaPath = resolve(this.baseDir, \"_schema.md\")\n if (existsSync(schemaPath)) return\n\n const schema = `# Wiki Schema\n\nA personal wiki maintained by LLMs. Karpathy pattern: plain files, aggressive tagging,\nstructure emerges from data. The wiki is a compounding artifact.\n\n## Structure\n\n\\`\\`\\`\nwiki/\n _schema.md # This file\n worldview.md # YOUR mental model — the LLM reads this during absorb\n raw/entries/ # Immutable raw sources (conversations, imports, anything)\n [LLM-chosen dirs] # Structure emerges from data — the LLM picks paths\n\\`\\`\\`\n\nThe LLM decides how to organize articles. You describe YOUR world in worldview.md.\n\n## Article Format\n\n\\`\\`\\`yaml\n---\ntitle: \"Staging Deployment Process\"\ntags: [\"deploy\", \"staging\", \"devops\", \"process\", \"2026-04-06\"]\nowner: devops-agent\naccess: public\ncreated: 2026-04-06\nlast_updated: 2026-04-06\nsources: [\"entry-id-1\", \"entry-id-2\"]\n---\n\nArticle content with [[wikilinks]] and section tags.\n\n## Deploy Steps\n<!-- tags: process, runbook, staging -->\nContent specific to this section...\n\\`\\`\\`\n\n## Tags\n\nTags are the primary context-narrowing mechanism. The more tags, the better.\n\nTag aggressively:\n- **Who**: people, agents, teams involved\n- **What**: project, client, topic, technology\n- **When**: dates, periods, milestones\n- **Where**: server, environment, channel\n- **How**: process, decision, pattern\n- **Your terms**: whatever vocabulary makes sense in YOUR world\n\nSection tags (\\`<!-- tags: ... -->\\`) let different parts of an article\nmatch different queries. A deploy article might have sections tagged\n\"runbook\" and \"incident\" separately.\n\n## Operations\n\n### Absorb\nRead raw entries → read worldview.md → tag aggressively → create/update articles.\nAlso identify GAPS: \"We mention X but have no article for it.\"\n\n### Query\nMatch by tags first → then keyword within matched articles → synthesize.\nIf agent is working on a specific project, only send content tagged for that project.\n\n### Lint\nCheck for: orphan articles, broken [[wikilinks]], untagged articles,\narticles over 100 lines that should split, mentioned-but-missing topics.\n\n## Worldview\n\nEdit \\`worldview.md\\` to describe YOUR world. This is not a schema — it's your\nmental model. The LLM reads it during absorb to understand where things go.\n\nExample worldview.md:\n\\`\\`\\`\nI'm the founder of Acme Corp.\nWe build software products.\nOur agents: Marketing Bot, DevOps Bot, Coordinator.\nClients: ClientA (enterprise), ClientB (startup).\nInfrastructure: local dev machine, cloud server.\nWe use GitLab for version control.\n\\`\\`\\`\n`\n\n writeFileSync(schemaPath, schema)\n this.log(\"Created _schema.md\")\n }\n\n // --- Wikilinks and Backlinks ---\n\n /**\n * Extract all [[wikilinks]] from article content.\n */\n extractWikilinks(content: string): string[] {\n const matches = content.match(/\\[\\[([^\\]]+)\\]\\]/g) || []\n return matches.map(m => m.replace(/\\[\\[|\\]\\]/g, \"\"))\n }\n\n /**\n * Build backlinks index: for each article, which other articles link to it.\n */\n buildBacklinks(): Record<string, string[]> {\n const backlinks: Record<string, string[]> = {}\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\" || relPath === \"log.md\") return\n\n const content = readFileSync(filePath, \"utf-8\")\n const links = this.extractWikilinks(content)\n\n for (const link of links) {\n if (!backlinks[link]) backlinks[link] = []\n backlinks[link].push(relPath)\n }\n })\n\n // Save\n writeFileSync(\n resolve(this.baseDir, \"_backlinks.json\"),\n JSON.stringify(backlinks, null, 2),\n )\n\n return backlinks\n }\n\n /**\n * Get backlinks for a specific article title.\n */\n getBacklinks(title: string): string[] {\n const path = resolve(this.baseDir, \"_backlinks.json\")\n if (!existsSync(path)) return []\n try {\n const data = JSON.parse(readFileSync(path, \"utf-8\"))\n return data[title] || []\n } catch {\n return []\n }\n }\n\n // --- Tag-based filtering (Karpathy: tags are the context narrowing mechanism) ---\n\n /**\n * Find articles matching ANY of the given tags.\n * This is how agents get relevant context — only articles tagged with\n * the client/project/topic they're working on.\n */\n findByTags(tags: string[], agentId?: string, limit: number = 10): WikiArticle[] {\n const lowerTags = tags.map(t => t.toLowerCase())\n const results: Array<{ article: WikiArticle; score: number }> = []\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article) return\n if (agentId && !this.canRead(article.meta, agentId)) return\n\n // Score: how many of the requested tags match?\n const articleTags = (article.meta.tags || []).map(t => t.toLowerCase())\n // Also check section tags: <!-- tags: foo, bar -->\n const sectionTags = (article.content.match(/<!--\\s*tags?:\\s*([^>]+)\\s*-->/gi) || [])\n .flatMap(m => m.replace(/<!--\\s*tags?:\\s*|\\s*-->/gi, \"\").split(\",\").map(t => t.trim().toLowerCase()))\n\n const allTags = [...articleTags, ...sectionTags]\n const score = lowerTags.filter(t => allTags.includes(t)).length\n\n if (score > 0) {\n results.push({ article, score })\n }\n })\n\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n .map(r => r.article)\n }\n\n /**\n * Get all unique tags across all articles.\n */\n getAllTags(): Map<string, number> {\n const tagCounts = new Map<string, number>()\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (!article?.meta.tags) return\n\n for (const tag of article.meta.tags) {\n tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1)\n }\n })\n\n return tagCounts\n }\n\n // --- Worldview (user's mental model — input to absorb) ---\n\n /**\n * Get the worldview file. This is the user's description of their world —\n * the LLM uses it during absorb to know where things go.\n * Returns null if not set.\n */\n getWorldview(): string | null {\n const path = resolve(this.baseDir, \"worldview.md\")\n if (!existsSync(path)) return null\n return readFileSync(path, \"utf-8\")\n }\n\n /**\n * Set the worldview. The user describes their world: companies, clients,\n * projects, team, processes. The LLM uses this as context during absorb.\n */\n setWorldview(content: string): void {\n writeFileSync(resolve(this.baseDir, \"worldview.md\"), content)\n this.appendLog(\"worldview\", \"Updated worldview\")\n }\n\n // --- Lint ---\n\n /**\n * Run a lint pass on the wiki. Returns issues found.\n */\n lint(): Array<{ type: string; article: string; message: string }> {\n const issues: Array<{ type: string; article: string; message: string }> = []\n const allTitles = new Set<string>()\n const allLinks = new Map<string, string[]>() // article -> outbound links\n const articlePaths = new Map<string, string>() // title -> path\n\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\") || relPath === \"WIKI.md\" || relPath === \"log.md\") return\n\n const article = this.readArticle(relPath)\n if (!article) return\n\n allTitles.add(article.meta.title)\n articlePaths.set(article.meta.title, relPath)\n\n const links = this.extractWikilinks(article.content)\n allLinks.set(relPath, links)\n\n // Check: article too long\n const lines = article.content.split(\"\\n\").length\n if (lines > 100) {\n issues.push({\n type: \"bloated\",\n article: relPath,\n message: `${lines} lines — consider splitting`,\n })\n }\n\n // Check: no sources\n if (!article.meta.sources?.length) {\n issues.push({\n type: \"unsourced\",\n article: relPath,\n message: \"No sources listed in frontmatter\",\n })\n }\n\n // Check: stub (too short)\n if (lines < 10 && article.content.trim().length < 100) {\n issues.push({\n type: \"stub\",\n article: relPath,\n message: \"Very short article — needs enrichment\",\n })\n }\n })\n\n // Check: broken wikilinks\n for (const [articlePath, links] of allLinks) {\n for (const link of links) {\n if (!allTitles.has(link)) {\n issues.push({\n type: \"broken-link\",\n article: articlePath,\n message: `[[${link}]] — target article not found`,\n })\n }\n }\n }\n\n // Check: orphan articles (no inbound links)\n const backlinks = this.buildBacklinks()\n for (const [title, path] of articlePaths) {\n if (!backlinks[title] || backlinks[title].length === 0) {\n issues.push({\n type: \"orphan\",\n article: path,\n message: `No other articles link to \"${title}\"`,\n })\n }\n }\n\n this.appendLog(\"lint\", `Found ${issues.length} issues`)\n return issues\n }\n\n // --- Absorb summary (for automated cron) ---\n\n /**\n * Get unabsorbed entries (entries not referenced by any article's sources field).\n */\n getUnabsorbedEntries(): WikiEntry[] {\n const allSources = new Set<string>()\n\n // Collect all source IDs referenced by articles\n this.walkDir(this.baseDir, (filePath) => {\n if (!filePath.endsWith(\".md\")) return\n const relPath = relative(this.baseDir, filePath)\n if (relPath.startsWith(\"raw/\") || relPath.startsWith(\"_\")) return\n\n const article = this.readArticle(relPath)\n if (article?.meta.sources) {\n for (const s of article.meta.sources) allSources.add(s)\n }\n })\n\n // Find entries not in any article's sources\n const entries = this.listEntries()\n return entries.filter(e => !allSources.has(e.id))\n }\n\n /**\n * Build an absorb prompt for unprocessed entries.\n * Returns a prompt string an agent can execute to compile entries into articles.\n */\n buildAbsorbPrompt(maxEntries: number = 20): string | null {\n const unabsorbed = this.getUnabsorbedEntries().slice(0, maxEntries)\n if (unabsorbed.length === 0) return null\n\n const entrySummaries = unabsorbed.map(e =>\n `[${e.date} ${e.agentId} via ${e.source}] ${e.content.slice(0, 200)}`\n ).join(\"\\n\\n\")\n\n return `/wiki absorb\\n\\nThere are ${unabsorbed.length} unprocessed entries. Read each, understand what it means, and create or update wiki articles.\\n\\n${entrySummaries}`\n }\n\n private walkDir(dir: string, callback: (filePath: string) => void): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const stat = statSync(full)\n if (stat.isDirectory()) {\n this.walkDir(full, callback)\n } else {\n callback(full)\n }\n }\n }\n}\n","import { WikiStore } from \"./store\"\nimport { resolve } from \"path\"\nimport { existsSync, readdirSync, mkdirSync } from \"fs\"\nimport type { WikiEntry } from \"./types\"\n\nexport type WikiMode = \"flat\" | \"graph\" | \"unified\"\n\n/**\n * WikiHub: manages per-agent wikis with a shared raw entry pool.\n * Supports two compilation modes:\n * - flat: Karpathy pattern — tags, LLM-chosen paths, worldview, gap detection\n * - graph: Knowledge graph — kind, parent, hierarchy, events, entities\n *\n * Both modes share the same raw entries. Articles stored separately:\n * agents/<id>/flat/ ← Karpathy compilation\n * agents/<id>/graph/ ← Knowledge graph compilation\n */\nexport class WikiHub {\n private baseDir: string\n private agentsDir: string\n private mode: WikiMode\n private sharedStore: WikiStore\n private agentStores: Map<string, WikiStore> = new Map()\n private log: (...args: unknown[]) => void\n\n constructor(\n baseDir: string = resolve(process.cwd(), \".agentx/wiki\"),\n log: (...args: unknown[]) => void = console.error.bind(console, \"[wiki-hub]\"),\n mode: WikiMode = \"graph\",\n ) {\n this.baseDir = baseDir\n this.agentsDir = resolve(baseDir, \"agents\")\n this.mode = mode\n this.log = log\n\n mkdirSync(this.agentsDir, { recursive: true })\n this.sharedStore = new WikiStore(baseDir, log)\n }\n\n getMode(): WikiMode { return this.mode }\n\n /**\n * Get or create the wiki store for a specific agent (mode-aware).\n * flat: agents/<id>/flat/\n * graph: agents/<id>/graph/\n */\n getAgentWiki(agentId: string): WikiStore {\n const key = `${agentId}:${this.mode}`\n if (this.agentStores.has(key)) return this.agentStores.get(key)!\n\n const agentDir = resolve(this.agentsDir, agentId, this.mode)\n const store = new WikiStore(agentDir, this.log)\n this.agentStores.set(key, store)\n return store\n }\n\n getSharedStore(): WikiStore {\n return this.sharedStore\n }\n\n listAgents(): string[] {\n const agents = new Set<string>()\n\n const entries = this.sharedStore.listEntries()\n for (const e of entries) agents.add(e.agentId)\n\n if (existsSync(this.agentsDir)) {\n for (const dir of readdirSync(this.agentsDir)) {\n if (!dir.startsWith(\"_\") && !dir.startsWith(\".\")) {\n agents.add(dir)\n }\n }\n }\n\n return [...agents].sort()\n }\n\n getAgentEntries(agentId: string): WikiEntry[] {\n return this.sharedStore.listEntries({ agentId })\n }\n\n getUnabsorbedEntries(agentId: string): WikiEntry[] {\n const agentWiki = this.getAgentWiki(agentId)\n const agentEntries = this.getAgentEntries(agentId)\n\n const absorbedIds = new Set<string>()\n const index = agentWiki.rebuildIndex()\n for (const article of index.articles) {\n if (article.sources) {\n for (const s of article.sources) absorbedIds.add(s)\n }\n }\n\n return agentEntries.filter(e => !absorbedIds.has(e.id))\n }\n\n summary(): AgentWikiSummary[] {\n const agents = this.listAgents()\n return agents.map(agentId => {\n const entries = this.getAgentEntries(agentId)\n const wiki = this.getAgentWiki(agentId)\n const index = wiki.rebuildIndex()\n const unabsorbed = this.getUnabsorbedEntries(agentId)\n\n return {\n agentId,\n totalEntries: entries.length,\n totalArticles: index.articles.length,\n unabsorbed: unabsorbed.length,\n articles: index.articles,\n }\n })\n }\n}\n\nexport interface AgentWikiSummary {\n agentId: string\n totalEntries: number\n totalArticles: number\n unabsorbed: number\n articles: Array<{ title: string; path: string; tags?: string[] }>\n}\n","// --- Rate limiter for agent tasks ---\n// Prevents message floods from exhausting Claude API/subscription.\n// Per-agent, sliding window.\n\nexport class RateLimiter {\n private windows: Map<string, number[]> = new Map()\n private maxPerMinute: number\n private maxPerHour: number\n\n constructor(maxPerMinute: number = 10, maxPerHour: number = 100) {\n this.maxPerMinute = maxPerMinute\n this.maxPerHour = maxPerHour\n }\n\n /**\n * Check if an agent can process a task. Returns true if allowed.\n */\n check(agentId: string): { allowed: boolean; reason?: string } {\n const now = Date.now()\n const timestamps = this.windows.get(agentId) || []\n\n // Clean old entries (older than 1 hour)\n const hourAgo = now - 3600_000\n const recent = timestamps.filter(t => t > hourAgo)\n\n // Check per-minute\n const minuteAgo = now - 60_000\n const lastMinute = recent.filter(t => t > minuteAgo).length\n if (lastMinute >= this.maxPerMinute) {\n return { allowed: false, reason: `Rate limit: ${lastMinute}/${this.maxPerMinute} per minute` }\n }\n\n // Check per-hour\n if (recent.length >= this.maxPerHour) {\n return { allowed: false, reason: `Rate limit: ${recent.length}/${this.maxPerHour} per hour` }\n }\n\n // Record this request\n recent.push(now)\n this.windows.set(agentId, recent)\n\n return { allowed: true }\n }\n\n /**\n * Get current usage for an agent.\n */\n usage(agentId: string): { lastMinute: number; lastHour: number } {\n const now = Date.now()\n const timestamps = this.windows.get(agentId) || []\n return {\n lastMinute: timestamps.filter(t => t > now - 60_000).length,\n lastHour: timestamps.filter(t => t > now - 3600_000).length,\n }\n }\n}\n","// --- 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 // 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 (extracted keywords from message)\n // Lightweight — just tag detection, not LLM-based\n const intentTags = extractIntentTags(input.message)\n if (intentTags.length) {\n layers.push({\n name: \"intent\",\n priority: 5,\n maxTokens: budget(\"intent\", 200),\n content: `[Intent: ${intentTags.join(\", \")}]`,\n tags: intentTags,\n })\n }\n\n // 6. Artifacts (media, reply-to, issue/MR context)\n const artifactLines: string[] = []\n if (input.replyToText) {\n artifactLines.push(`[Replying to]: ${input.replyToText.slice(0, 300)}`)\n }\n if (input.mediaPath) {\n artifactLines.push(`[Attached file: ${input.mediaPath}]`)\n artifactLines.push(`[File type: ${input.mediaType || \"unknown\"}]`)\n if (input.mediaType?.startsWith(\"image/\")) artifactLines.push(\"Please view this image and respond to it.\")\n else if (input.mediaType?.startsWith(\"audio/\")) artifactLines.push(\"Please transcribe this audio and respond.\")\n }\n if (input.issueMR) {\n artifactLines.push(`[${input.issueMR.type} #${input.issueMR.iid}: ${input.issueMR.title}]`)\n }\n if (artifactLines.length) {\n layers.push({\n name: \"artifacts\",\n priority: 6,\n maxTokens: budget(\"artifacts\", 500),\n content: artifactLines.join(\"\\n\"),\n tags: [\"artifacts\", ...(input.mediaType ? [\"media\"] : [])],\n })\n }\n\n // 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 // 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 { 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\"\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\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 /** Active soul profile per agent+chat session: \"agentId:channel:chatId\" → profile name */\n private activeSouls: Map<string, string> = 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 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 * 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 check\n const rateCheck = this.rateLimiter.check(task.agentId)\n if (!rateCheck.allowed) {\n this.log(`[${task.agentId}] ${rateCheck.reason}`)\n return { content: \"\", error: rateCheck.reason }\n }\n\n state.activeTasks++\n state.totalTasks++\n state.lastActive = new Date()\n\n // 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 // 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 // Build structured context — read from per-agent wiki\n const agentWiki = this.wikiHub.getAgentWiki(task.agentId)\n const wikiArticles = agentWiki.findRelevant(task.message, task.agentId, 3)\n const wikiContext = agentWiki.buildContext(wikiArticles)\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 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 try {\n const response = await executeTask(state.def, taskWithSystemPrompt, this.providers, onDelta, historyContext, resumeSessionId)\n\n if (response.error) {\n state.errors++\n this.log(`[${task.agentId}] error: ${response.error}`)\n } else {\n // Record agent response in session\n this.sessions.addAgentMessage(task.agentId, channel, chatId, response.content)\n\n // Store Claude session ID for future --resume\n if (response.claudeSessionId) {\n this.sessions.setClaudeSessionId(task.agentId, channel, chatId, response.claudeSessionId)\n }\n\n // Wiki: export conversation as raw entry for later absorption\n if (response.content.length > 50) {\n try {\n const entryId = `${task.agentId}-${Date.now().toString(36)}`\n this.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 return { content: \"\", error: error.message }\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 // 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 }> {\n return Array.from(this.agents.values()).map((s) => ({\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 }))\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","buildPrompt","agent","task","historyContext","parts","ctx","envLines","isGitLab","isTelegram","peer","handle","role","buildClaudeArgs","prompt","streaming","resumeSessionId","modelOverride","systemPromptAppend","args","model","parseClaudeJsonOutput","stdout","data","usage","billedModel","executeClaudeCode","start","timeoutMs","stderr","exitCode","resolve","reject","error","errMsg","parsed","executeClaudeCodeStreaming","onDelta","fullText","streamBilledModel","streamUsage","streamSessionId","streamTimeoutMs","proc","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","readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","readdirSync","statSync","resolve","join","relative","dirname","WikiStore","baseDir","resolve","log","mkdirSync","article","agentId","entry","filename","filepath","frontmatter","k","v","writeFileSync","filter","existsSync","files","readdirSync","f","entries","file","content","readFileSync","fmMatch","fm","body","get","key","path","meta","existing","fullPath","dirname","t","s","action","articlePath","ts","versionDir","m","i","versionTimestamp","version","raw","getArray","tags","a","articles","filePath","relPath","relative","query","maxResults","queryLower","docs","cachePath","index","buildIndexCached","bm25Scores","scoreAll","scoreMap","r","score","b","message","maxArticles","maxChars","lines","totalChars","header","chunk","backlinks","links","link","target","aliases","md","byType","type","list","x","y","access","totalArticles","byAccess","byOwner","tag","entryFiles","details","logPath","appendFileSync","limit","e","schemaPath","title","lowerTags","results","articleTags","sectionTags","allTags","tagCounts","issues","allTitles","allLinks","articlePaths","allSources","maxEntries","unabsorbed","entrySummaries","dir","callback","full","join","statSync","resolve","existsSync","readdirSync","mkdirSync","WikiHub","baseDir","log","mode","WikiStore","agentId","key","agentDir","store","agents","entries","e","dir","agentWiki","agentEntries","absorbedIds","index","article","s","unabsorbed","RateLimiter","maxPerMinute","maxPerHour","agentId","now","timestamps","hourAgo","recent","t","minuteAgo","lastMinute","DEFAULT_CONFIG","CHARS_PER_TOKEN","buildAgentContext","input","config","layers","buildLayers","a","b","parts","totalChars","maxChars","layer","layerMaxChars","trimmed","remaining","budget","name","fallback","buildChannelLayer","buildScopeLayer","intentTags","extractIntentTags","artifactLines","history","maxTokens","lines","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","globalRegistry","setGlobalRegistry","registry","getGlobalRegistry","AgentRegistry","config","log","SessionStore","WikiHub","MemoryStore","PatternStore","RateLimiter","TokenTracker","MessageQueue","id","def","builder","text","opts","lower","bestId","bestLen","state","mention","mentionLower","found","agentId","channel","peers","agent","m","task","onDelta","qChannel","qChatId","mode","queued","pending","rateCheck","runningTask","chatId","senderName","agentWiki","wikiArticles","wikiContext","relevantMemories","memoryContext","relevantPatterns","patternContext","skillInjection","loadLocalSkills","getAutoInjectSkills","skills","resumeSessionId","compactResult","quality","lost","d","e","sessionHistory","crossChatContext","soulSessionKey","soulSwitch","detectSoulSwitch","activeSoul","bootstrapFiles","loadBootstrapFiles","bootstrapContextText","buildBootstrapContext","systemPromptAppend","s","contextInput","historyContext","buildAgentContext","taskWithSystemPrompt","response","executeTask","entryId","extractMemories","extractPatterns","billedModel","tChannel","tSessionKey","error","idx","r","qm","days"]}
@@ -1,2 +0,0 @@
1
- import{a,b,c,d}from"./chunk-M33CB2H4.js";import"./chunk-ITZEVXZ4.js";import"./chunk-4YCH6IZV.js";export{a as daemonConfigSchema,b as expandEnvVars,c as loadDaemonConfig,d as validateWorkspaces};
2
- //# sourceMappingURL=config-7QJ2QJIC.js.map
@@ -1,2 +0,0 @@
1
- import{a,b,c,d}from"./chunk-NJR5WF5E.js";import"./chunk-M33CB2H4.js";import"./chunk-ITZEVXZ4.js";import"./chunk-4YCH6IZV.js";export{a as applyConfigMutation,c as getAtPath,b as setAtPath,d as unsetAtPath};
2
- //# sourceMappingURL=config-mutator-GAEVGHDD.js.map
@@ -1,2 +0,0 @@
1
- import{a,b,c,d,e}from"./chunk-DSYKYZMT.js";import"./chunk-4YCH6IZV.js";export{e as debug,d as getDebugLogs,c as getDebugState,b as isDebug,a as setDebug};
2
- //# sourceMappingURL=debug-N3B5NVJU.js.map
@@ -1,2 +0,0 @@
1
- import{a,b,c,d,e}from"./chunk-MCJUAE6V.js";import"./chunk-4YCH6IZV.js";export{e as getAutoInjectSkills,a as loadLocalSkills,d as matchSkillsToTask,c as parseSkillContent,b as parseSkillFile};
2
- //# sourceMappingURL=loader-YQE6XJBT.js.map
@@ -1,2 +0,0 @@
1
- import{a}from"./chunk-ZRYBSI5G.js";import"./chunk-4YCH6IZV.js";export{a as extractMemories};
2
- //# sourceMappingURL=memory-extract-RGPUPMKU.js.map
@@ -1,2 +0,0 @@
1
- import{g as a,h as b,i as c}from"./chunk-UPT2UMF6.js";import"./chunk-HI24KAEY.js";import"./chunk-DYOBFJLX.js";import"./chunk-ZRYBSI5G.js";import"./chunk-QOYAX2FT.js";import"./chunk-4YCH6IZV.js";export{c as AgentRegistry,b as getGlobalRegistry,a as setGlobalRegistry};
2
- //# sourceMappingURL=registry-YVAG4EO5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/daemon/usage-dashboard.ts"],"sourcesContent":["import { createServer } from \"http\"\nimport { readdirSync } from \"fs\"\nimport { TokenTracker, type DailyUsage, type AgentUsage, type DailyReport } from \"./token-tracker\"\n\n// --- Usage Dashboard: zero-dep web UI for token cost tracking ---\n\nexport function startUsageDashboard(port: number = 4201): void {\n const tracker = new TokenTracker()\n\n const server = createServer(async (req, res) => {\n const url = new URL(req.url || \"/\", `http://localhost:${port}`)\n const path = url.pathname\n\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n\n if (path === \"/api/usage\") {\n const from = url.searchParams.get(\"from\") || \"\"\n const to = url.searchParams.get(\"to\") || \"\"\n const data = loadUsageRange(tracker, from, to)\n res.writeHead(200, { \"Content-Type\": \"application/json\" })\n res.end(JSON.stringify(data))\n return\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(renderDashboard())\n })\n\n server.listen(port, () => {\n console.log(`\\n Usage dashboard: http://localhost:${port}\\n`)\n })\n}\n\n// --- Data loading ---\n\ninterface UsageDay {\n date: string\n tasks: number\n input: number\n output: number\n cacheRead: number\n cacheCreate: number\n cost: number\n agents: Record<string, { tasks: number; cost: number; input: number; output: number; cacheRead: number; cacheCreate: number; model?: string }>\n}\n\nfunction loadUsageRange(tracker: TokenTracker, from: string, to: string): UsageDay[] {\n const dir = tracker[\"dir\"] // access private dir for file listing\n const files: string[] = []\n\n try {\n for (const f of readdirSync(dir)) {\n if (!f.endsWith(\".json\")) continue\n const date = f.replace(\".json\", \"\")\n if (from && date < from) continue\n if (to && date > to) continue\n files.push(date)\n }\n } catch {\n return []\n }\n\n files.sort()\n\n return files.map((date) => {\n const usage = tracker.getDate(date)\n if (!usage) return null\n\n let tasks = 0, input = 0, output = 0, cacheRead = 0, cacheCreate = 0, cost = 0\n const agents: UsageDay[\"agents\"] = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const agentCost = TokenTracker.calculateCost(agent)\n tasks += agent.tasks\n input += agent.inputTokens || 0\n output += agent.outputTokens || 0\n cacheRead += agent.cacheReadTokens || 0\n cacheCreate += agent.cacheCreateTokens || 0\n cost += agentCost\n agents[id] = {\n tasks: agent.tasks,\n cost: agentCost,\n input: agent.inputTokens || 0,\n output: agent.outputTokens || 0,\n cacheRead: agent.cacheReadTokens || 0,\n cacheCreate: agent.cacheCreateTokens || 0,\n model: agent.model,\n }\n }\n\n return { date, tasks, input, output, cacheRead, cacheCreate, cost, agents }\n }).filter((d): d is UsageDay => d !== null && d.tasks > 0)\n}\n\n// --- HTML rendering ---\n\nfunction renderDashboard(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>AgentX Usage Dashboard</title>\n<style>${CSS}</style>\n</head>\n<body>\n<div class=\"header\">\n <h1>AgentX Usage Dashboard</h1>\n <div class=\"filters\">\n <label>From <input type=\"date\" id=\"from\"></label>\n <label>To <input type=\"date\" id=\"to\"></label>\n <button onclick=\"load()\">Apply</button>\n <button onclick=\"setRange(7)\">7d</button>\n <button onclick=\"setRange(14)\">14d</button>\n <button onclick=\"setRange(30)\">30d</button>\n <button onclick=\"setRange(0)\">All</button>\n </div>\n</div>\n\n<div class=\"stats\" id=\"stats\"></div>\n\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Cost Over Time</h2>\n <canvas id=\"costChart\" height=\"260\"></canvas>\n </div>\n <div class=\"card\">\n <h2>Tokens Over Time</h2>\n <canvas id=\"tokenChart\" height=\"260\"></canvas>\n </div>\n</div>\n\n<div class=\"grid\">\n <div class=\"card\">\n <h2>Cost Per Agent</h2>\n <canvas id=\"agentChart\" height=\"260\"></canvas>\n </div>\n <div class=\"card\">\n <h2>Token Breakdown</h2>\n <canvas id=\"breakdownChart\" height=\"260\"></canvas>\n </div>\n</div>\n\n<div class=\"card\">\n <h2>Daily Detail</h2>\n <table id=\"table\">\n <thead>\n <tr><th>Date</th><th>Tasks</th><th>Input</th><th>Output</th><th>Cache R</th><th>Cache W</th><th>Cost</th><th>Top Agent</th></tr>\n </thead>\n <tbody></tbody>\n </table>\n</div>\n\n<script>${JS}</script>\n</body>\n</html>`\n}\n\n// --- Embedded CSS ---\n\nconst CSS = `\n:root {\n --bg: #0f1117;\n --card: #1a1d27;\n --border: #2a2d3a;\n --text: #e1e4ed;\n --muted: #8b8fa3;\n --accent: #6366f1;\n --green: #22c55e;\n --orange: #f59e0b;\n --red: #ef4444;\n --blue: #3b82f6;\n --purple: #a855f7;\n --cyan: #06b6d4;\n}\n* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 24px; }\n.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-wrap: wrap; gap: 12px; }\nh1 { font-size: 20px; font-weight: 600; }\nh2 { font-size: 14px; font-weight: 500; color: var(--muted); margin-bottom: 16px; text-transform: uppercase; letter-spacing: 0.5px; }\n.filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.filters label { font-size: 13px; color: var(--muted); }\n.filters input { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 6px 10px; border-radius: 6px; font-size: 13px; }\n.filters button { background: var(--card); border: 1px solid var(--border); color: var(--text); padding: 6px 14px; border-radius: 6px; cursor: pointer; font-size: 13px; transition: background 0.15s; }\n.filters button:hover { background: var(--accent); border-color: var(--accent); }\n.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 24px; }\n.stat { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 16px; }\n.stat .value { font-size: 24px; font-weight: 700; margin-bottom: 4px; }\n.stat .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }\n.stat .delta { font-size: 12px; margin-top: 4px; }\n.stat .delta.up { color: var(--red); }\n.stat .delta.down { color: var(--green); }\n.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }\n@media (max-width: 900px) { .grid { grid-template-columns: 1fr; } }\n.card { background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 20px; }\ncanvas { width: 100% !important; }\ntable { width: 100%; border-collapse: collapse; font-size: 13px; }\nth { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--border); color: var(--muted); font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }\ntd { padding: 8px 12px; border-bottom: 1px solid var(--border); }\ntr:hover td { background: rgba(99, 102, 241, 0.05); }\n.cost { color: var(--accent); font-weight: 600; font-variant-numeric: tabular-nums; }\n.tok { font-variant-numeric: tabular-nums; }\n`\n\n// --- Embedded JavaScript ---\n\nconst JS = `\nlet data = [];\n\nfunction fmtTok(n) {\n if (n >= 1e6) return (n/1e6).toFixed(1) + 'M';\n if (n >= 1e3) return (n/1e3).toFixed(0) + 'K';\n return String(n);\n}\nfunction fmtCost(n) { return n < 0.01 ? '$' + n.toFixed(4) : '$' + n.toFixed(2); }\n\nfunction setRange(days) {\n if (days === 0) {\n document.getElementById('from').value = '';\n document.getElementById('to').value = '';\n } else {\n const to = new Date();\n const from = new Date(Date.now() - days * 86400000);\n document.getElementById('from').value = from.toISOString().slice(0,10);\n document.getElementById('to').value = to.toISOString().slice(0,10);\n }\n load();\n}\n\nasync function load() {\n const from = document.getElementById('from').value;\n const to = document.getElementById('to').value;\n const params = new URLSearchParams();\n if (from) params.set('from', from);\n if (to) params.set('to', to);\n const res = await fetch('/api/usage?' + params);\n data = await res.json();\n render();\n}\n\nfunction render() {\n renderStats();\n renderCostChart();\n renderTokenChart();\n renderAgentChart();\n renderBreakdownChart();\n renderTable();\n}\n\nfunction renderStats() {\n const el = document.getElementById('stats');\n const totalCost = data.reduce((s,d) => s + d.cost, 0);\n const totalTasks = data.reduce((s,d) => s + d.tasks, 0);\n const totalInput = data.reduce((s,d) => s + d.input, 0);\n const totalOutput = data.reduce((s,d) => s + d.output, 0);\n const totalCacheR = data.reduce((s,d) => s + d.cacheRead, 0);\n const totalCacheW = data.reduce((s,d) => s + d.cacheCreate, 0);\n const cacheRatio = (totalCacheR + totalCacheW) > 0 ? (totalCacheR / (totalCacheR + totalCacheW) * 100).toFixed(0) : '0';\n const avgDaily = data.length > 0 ? totalCost / data.length : 0;\n\n // Delta: compare last day vs previous\n let delta = '';\n if (data.length >= 2) {\n const last = data[data.length-1].cost;\n const prev = data[data.length-2].cost;\n if (prev > 0) {\n const pct = ((last - prev) / prev * 100).toFixed(0);\n const cls = last > prev ? 'up' : 'down';\n const arrow = last > prev ? '\\\\u2191' : '\\\\u2193';\n delta = '<div class=\"delta ' + cls + '\">' + arrow + ' ' + Math.abs(pct) + '% vs prev day</div>';\n }\n }\n\n el.innerHTML =\n stat(fmtCost(totalCost), 'Total Cost', delta) +\n stat(fmtCost(avgDaily), 'Avg Daily Cost', '') +\n stat(totalTasks.toLocaleString(), 'Total Tasks', '') +\n stat(fmtTok(totalInput + totalOutput), 'Tokens (I/O)', '') +\n stat(fmtTok(totalCacheR), 'Cache Reads', '') +\n stat(cacheRatio + '%', 'Cache Hit Rate', '') +\n stat(data.length.toString(), 'Days Tracked', '');\n}\n\nfunction stat(value, label, extra) {\n return '<div class=\"stat\"><div class=\"value\">' + value + '</div><div class=\"label\">' + label + '</div>' + extra + '</div>';\n}\n\n// --- Canvas chart helpers ---\nfunction drawLine(ctx, points, w, h, color, fill) {\n if (points.length < 2) return;\n const max = Math.max(...points, 0.001);\n const pad = { t: 10, r: 10, b: 30, l: 50 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n\n ctx.strokeStyle = color;\n ctx.lineWidth = 2;\n ctx.beginPath();\n points.forEach((v, i) => {\n const x = pad.l + (i / (points.length - 1)) * cw;\n const y = pad.t + ch - (v / max) * ch;\n i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);\n });\n ctx.stroke();\n\n if (fill) {\n ctx.lineTo(pad.l + cw, pad.t + ch);\n ctx.lineTo(pad.l, pad.t + ch);\n ctx.closePath();\n ctx.fillStyle = fill;\n ctx.fill();\n }\n\n // Y axis labels\n ctx.fillStyle = '#8b8fa3';\n ctx.font = '11px -apple-system, sans-serif';\n ctx.textAlign = 'right';\n for (let i = 0; i <= 4; i++) {\n const v = (max / 4) * i;\n const y = pad.t + ch - (i / 4) * ch;\n ctx.fillText(v >= 1000 ? fmtTok(v) : v < 0.01 ? v.toFixed(4) : v.toFixed(2), pad.l - 6, y + 4);\n ctx.strokeStyle = '#2a2d3a';\n ctx.lineWidth = 0.5;\n ctx.beginPath(); ctx.moveTo(pad.l, y); ctx.lineTo(pad.l + cw, y); ctx.stroke();\n }\n\n // X axis labels (first, middle, last)\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'center';\n if (data.length > 0) {\n const labels = [0, Math.floor(data.length/2), data.length-1];\n labels.forEach(i => {\n if (i < data.length) {\n const x = pad.l + (i / (points.length - 1)) * cw;\n ctx.fillText(data[i].date.slice(5), x, pad.t + ch + 18);\n }\n });\n }\n}\n\nfunction drawBars(ctx, items, w, h, colors) {\n if (items.length === 0) return;\n const max = Math.max(...items.map(i => i.value), 0.001);\n const pad = { t: 10, r: 10, b: 60, l: 60 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n const barW = Math.min(40, (cw / items.length) * 0.7);\n const gap = cw / items.length;\n\n items.forEach((item, i) => {\n const barH = (item.value / max) * ch;\n const x = pad.l + i * gap + (gap - barW) / 2;\n const y = pad.t + ch - barH;\n ctx.fillStyle = colors[i % colors.length];\n ctx.beginPath();\n ctx.roundRect(x, y, barW, barH, 3);\n ctx.fill();\n\n // Label\n ctx.fillStyle = '#8b8fa3';\n ctx.font = '11px -apple-system, sans-serif';\n ctx.textAlign = 'center';\n ctx.save();\n ctx.translate(x + barW/2, pad.t + ch + 12);\n ctx.rotate(-0.4);\n ctx.fillText(item.label.length > 14 ? item.label.slice(0,12) + '..' : item.label, 0, 0);\n ctx.restore();\n\n // Value on top\n ctx.fillStyle = '#e1e4ed';\n ctx.textAlign = 'center';\n ctx.fillText(item.fmt, x + barW/2, y - 6);\n });\n}\n\nfunction drawStacked(ctx, series, labels, w, h, colors) {\n if (labels.length === 0) return;\n const pad = { t: 10, r: 10, b: 30, l: 50 };\n const cw = w - pad.l - pad.r;\n const ch = h - pad.t - pad.b;\n\n // Calculate totals for max\n const totals = labels.map((_, i) => series.reduce((s, sr) => s + sr.values[i], 0));\n const max = Math.max(...totals, 1);\n\n const barW = Math.min(30, (cw / labels.length) * 0.7);\n const gap = cw / labels.length;\n\n labels.forEach((_, li) => {\n let yOffset = 0;\n series.forEach((sr, si) => {\n const val = sr.values[li];\n const barH = (val / max) * ch;\n const x = pad.l + li * gap + (gap - barW) / 2;\n const y = pad.t + ch - yOffset - barH;\n ctx.fillStyle = colors[si % colors.length];\n ctx.fillRect(x, y, barW, barH);\n yOffset += barH;\n });\n });\n\n // Legend\n ctx.font = '11px -apple-system, sans-serif';\n series.forEach((sr, i) => {\n const x = pad.l + i * 100;\n ctx.fillStyle = colors[i % colors.length];\n ctx.fillRect(x, h - 16, 10, 10);\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'left';\n ctx.fillText(sr.name, x + 14, h - 7);\n });\n\n // X axis\n ctx.fillStyle = '#8b8fa3';\n ctx.textAlign = 'center';\n if (data.length > 0) {\n [0, Math.floor(labels.length/2), labels.length-1].forEach(i => {\n if (i < labels.length) {\n const x = pad.l + (i / Math.max(labels.length-1,1)) * cw;\n ctx.fillText(labels[i].slice(5), x, pad.t + ch + 18);\n }\n });\n }\n}\n\nfunction setupCanvas(id) {\n const canvas = document.getElementById(id);\n const dpr = window.devicePixelRatio || 1;\n const rect = canvas.parentElement.getBoundingClientRect();\n const w = rect.width - 40; // account for card padding\n const h = canvas.height;\n canvas.width = w * dpr;\n canvas.height = h * dpr;\n canvas.style.width = w + 'px';\n canvas.style.height = h + 'px';\n const ctx = canvas.getContext('2d');\n ctx.scale(dpr, dpr);\n ctx.clearRect(0, 0, w, h);\n return { ctx, w, h };\n}\n\nfunction renderCostChart() {\n const { ctx, w, h } = setupCanvas('costChart');\n const points = data.map(d => d.cost);\n drawLine(ctx, points, w, h, '#6366f1', 'rgba(99,102,241,0.1)');\n}\n\nfunction renderTokenChart() {\n const { ctx, w, h } = setupCanvas('tokenChart');\n const labels = data.map(d => d.date);\n drawStacked(ctx, [\n { name: 'Input', values: data.map(d => d.input) },\n { name: 'Output', values: data.map(d => d.output) },\n { name: 'Cache R', values: data.map(d => d.cacheRead) },\n { name: 'Cache W', values: data.map(d => d.cacheCreate) },\n ], labels, w, h, ['#3b82f6', '#22c55e', '#06b6d4', '#f59e0b']);\n}\n\nfunction renderAgentChart() {\n const { ctx, w, h } = setupCanvas('agentChart');\n const agentTotals = {};\n data.forEach(d => {\n for (const [id, a] of Object.entries(d.agents)) {\n agentTotals[id] = (agentTotals[id] || 0) + a.cost;\n }\n });\n const items = Object.entries(agentTotals)\n .sort(([,a],[,b]) => b - a)\n .slice(0, 10)\n .map(([id, cost]) => ({ label: id, value: cost, fmt: fmtCost(cost) }));\n drawBars(ctx, items, w, h, ['#6366f1','#3b82f6','#06b6d4','#22c55e','#f59e0b','#ef4444','#a855f7','#ec4899']);\n}\n\nfunction renderBreakdownChart() {\n const { ctx, w, h } = setupCanvas('breakdownChart');\n const totals = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };\n data.forEach(d => { totals.input += d.input; totals.output += d.output; totals.cacheRead += d.cacheRead; totals.cacheCreate += d.cacheCreate; });\n const items = [\n { label: 'Input', value: totals.input, fmt: fmtTok(totals.input) },\n { label: 'Output', value: totals.output, fmt: fmtTok(totals.output) },\n { label: 'Cache Read', value: totals.cacheRead, fmt: fmtTok(totals.cacheRead) },\n { label: 'Cache Write', value: totals.cacheCreate, fmt: fmtTok(totals.cacheCreate) },\n ];\n drawBars(ctx, items, w, h, ['#3b82f6','#22c55e','#06b6d4','#f59e0b']);\n}\n\nfunction renderTable() {\n const tbody = document.querySelector('#table tbody');\n tbody.innerHTML = data.slice().reverse().map(d => {\n const top = Object.entries(d.agents).sort(([,a],[,b]) => b.cost - a.cost)[0];\n const topStr = top ? top[0] + ' (' + fmtCost(top[1].cost) + ')' : '-';\n return '<tr>' +\n '<td>' + d.date + '</td>' +\n '<td>' + d.tasks + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.input) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.output) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.cacheRead) + '</td>' +\n '<td class=\"tok\">' + fmtTok(d.cacheCreate) + '</td>' +\n '<td class=\"cost\">' + fmtCost(d.cost) + '</td>' +\n '<td>' + topStr + '</td>' +\n '</tr>';\n }).join('');\n}\n\n// Auto-load last 30 days\nsetRange(30);\n`\n\nexport default startUsageDashboard\n"],"mappings":"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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8SJC,EAAQjC","names":["createServer","readdirSync","startUsageDashboard","port","tracker","TokenTracker","createServer","req","res","url","path","from","to","data","loadUsageRange","renderDashboard","dir","files","f","readdirSync","date","usage","tasks","input","output","cacheRead","cacheCreate","cost","agents","id","agent","agentCost","d","CSS","JS","usage_dashboard_default"]}
@@ -1,2 +0,0 @@
1
- import{a,b}from"./chunk-QZP65I2H.js";import"./chunk-HI24KAEY.js";import"./chunk-4YCH6IZV.js";export{b as setupAllWorkspaces,a as setupWorkspace};
2
- //# sourceMappingURL=workspace-setup-QQTCJY4B.js.map