agentix-cli 0.8.0 → 0.9.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 (58) hide show
  1. package/README.md +337 -116
  2. package/dist/agent-QYQVGKLM.js +2 -0
  3. package/dist/chunk-34UAFPK4.js +10 -0
  4. package/dist/chunk-34UAFPK4.js.map +1 -0
  5. package/dist/{chunk-X7UN6JAA.js → chunk-6MKAXEE5.js} +2 -2
  6. package/dist/chunk-BAIQJHQF.js +64 -0
  7. package/dist/chunk-BAIQJHQF.js.map +1 -0
  8. package/dist/chunk-BHDLKX3G.js +6 -0
  9. package/dist/chunk-BHDLKX3G.js.map +1 -0
  10. package/dist/chunk-CJ45Y2IR.js +12 -0
  11. package/dist/chunk-CJ45Y2IR.js.map +1 -0
  12. package/dist/chunk-DSYKYZMT.js +2 -0
  13. package/dist/chunk-DSYKYZMT.js.map +1 -0
  14. package/dist/chunk-IVVVYXPH.js +136 -0
  15. package/dist/chunk-IVVVYXPH.js.map +1 -0
  16. package/dist/{chunk-MGMZNJCE.js → chunk-KXZMYLHQ.js} +27 -27
  17. package/dist/chunk-KXZMYLHQ.js.map +1 -0
  18. package/dist/chunk-SBUX74OU.js +108 -0
  19. package/dist/chunk-SBUX74OU.js.map +1 -0
  20. package/dist/chunk-X32247GM.js +2 -0
  21. package/dist/chunk-X32247GM.js.map +1 -0
  22. package/dist/chunk-ZRYBSI5G.js +23 -0
  23. package/dist/chunk-ZRYBSI5G.js.map +1 -0
  24. package/dist/cli.js +280 -36
  25. package/dist/cli.js.map +1 -1
  26. package/dist/compaction-E3MQRLTL.js +28 -0
  27. package/dist/compaction-E3MQRLTL.js.map +1 -0
  28. package/dist/config-MSWKC466.js +2 -0
  29. package/dist/debug-N3B5NVJU.js +2 -0
  30. package/dist/heal-OTGT5HHJ.js +2 -0
  31. package/dist/heal-OTGT5HHJ.js.map +1 -0
  32. package/dist/index.d.ts +712 -239
  33. package/dist/index.js +2 -2
  34. package/dist/index.js.map +1 -1
  35. package/dist/memory-extract-RGPUPMKU.js +2 -0
  36. package/dist/memory-extract-RGPUPMKU.js.map +1 -0
  37. package/dist/{providers-AVYG63KK.js → providers-G3WZ4RVJ.js} +2 -2
  38. package/dist/providers-G3WZ4RVJ.js.map +1 -0
  39. package/dist/registry-FP6FUOXF.js +2 -0
  40. package/dist/registry-FP6FUOXF.js.map +1 -0
  41. package/dist/subagent-WV7QKQ3L.js +3 -0
  42. package/dist/subagent-WV7QKQ3L.js.map +1 -0
  43. package/dist/usage-dashboard-2TJQBFSH.js +404 -0
  44. package/dist/usage-dashboard-2TJQBFSH.js.map +1 -0
  45. package/package.json +4 -1
  46. package/dist/agent-K2YOEOJ5.js +0 -2
  47. package/dist/chunk-F73GPYCO.js +0 -106
  48. package/dist/chunk-F73GPYCO.js.map +0 -1
  49. package/dist/chunk-MGMZNJCE.js.map +0 -1
  50. package/dist/chunk-NGKOM4ZZ.js +0 -137
  51. package/dist/chunk-NGKOM4ZZ.js.map +0 -1
  52. package/dist/chunk-Z4GC5D6D.js +0 -12
  53. package/dist/chunk-Z4GC5D6D.js.map +0 -1
  54. package/dist/heal-UV5A6B5T.js +0 -2
  55. /package/dist/{agent-K2YOEOJ5.js.map → agent-QYQVGKLM.js.map} +0 -0
  56. /package/dist/{chunk-X7UN6JAA.js.map → chunk-6MKAXEE5.js.map} +0 -0
  57. /package/dist/{heal-UV5A6B5T.js.map → config-MSWKC466.js.map} +0 -0
  58. /package/dist/{providers-AVYG63KK.js.map → debug-N3B5NVJU.js.map} +0 -0
package/README.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # AgentX
2
2
 
3
- **Self-hosted multi-agent orchestrator.** Routes messages from Telegram, WhatsApp, Discord, GitLab, crons, webhooks, and cross-machine A2A mesh to AI agents running on Claude Code, OpenAI, Ollama, or any LLM provider.
3
+ **Self-hosted multi-agent orchestrator.** Routes messages from Telegram, WhatsApp, Discord, GitLab, crons, webhooks, and cross-machine mesh to AI agents running on Claude Code, OpenAI, Ollama, or any LLM provider.
4
4
 
5
- > **Experimental.** Built as a self-hosted, bring-your-own-key alternative to third-party AI orchestrators affected by [Anthropic's updated terms of use](https://www.anthropic.com/policies). You run it on your own machines with your own API keys or Claude subscription.
5
+ ## Why AgentX?
6
+
7
+ Existing multi-agent frameworks (CrewAI, AutoGen, LangGraph) are SDK-first, Python-heavy, and cloud-dependent. AgentX is **infrastructure-first** — closer to systemd for AI agents than to another framework.
8
+
9
+ - **BYOAI** — Bring your own AI. Claude, OpenAI, Ollama, whatever
10
+ - **Agents = directories** — Each agent is a workspace with config files. No code required
11
+ - **Mesh federation** — Agents on different machines collaborate over Tailscale/VPN
12
+ - **Channel routing** — Telegram, GitLab, WhatsApp, Discord, webhooks — built in
13
+ - **Cross-channel messaging** — Agents receive on one channel, send to another via `/send` API
14
+ - **Wiki memory** — Agents build compounding knowledge from conversations
15
+ - **Live monitoring** — Real-time SSE event stream + debug mode with categories
6
16
 
7
17
  ## Install
8
18
 
@@ -10,16 +20,17 @@
10
20
  npm install -g agentix-cli
11
21
  ```
12
22
 
13
- ## Quick start
23
+ ## Quick Start
14
24
 
15
25
  ```bash
16
- agentx init # Create config + workspace
17
- agentx agent add # Add an agent (interactive)
18
- agentx channel add # Add a channel (Telegram/WhatsApp/Discord/GitLab)
19
- agentx daemon start # Start
26
+ agentx init # Create config + workspace
27
+ agentx agent add # Add an agent (interactive)
28
+ agentx channel add # Add a channel (Telegram/WhatsApp/Discord/GitLab)
29
+ agentx daemon start # Start the daemon
30
+ agentx daemon watch # Live activity feed (color-coded)
20
31
  ```
21
32
 
22
- ## How it works
33
+ ## Architecture
23
34
 
24
35
  ```
25
36
  Telegram ──┐
@@ -31,63 +42,257 @@ agentx daemon start # Start
31
42
  A2A mesh ──┘
32
43
  │
33
44
  Context Engine
34
- (8 layers, token-budgeted)
45
+ (10 layers, token-budgeted)
46
+ │
47
+ ┌─────────┼─────────┐
48
+ Wiki Memory Bootstrap
49
+ (per-agent) (Haiku) (SOUL.md)
35
50
  ```
36
51
 
37
52
  Each agent = a workspace directory with Claude Code configuration (`.claude/`, `CLAUDE.md`, skills, hooks, MCP servers). AgentX orchestrates when and where agents run.
38
53
 
54
+ ## Communication Matrix
55
+
56
+ AgentX supports every communication path: human-to-agent, agent-to-human, agent-to-agent, and cross-channel — across all channels.
57
+
58
+ | Path | Telegram | WhatsApp | Discord | GitLab | API |
59
+ |------|:--------:|:--------:|:-------:|:------:|:---:|
60
+ | **H2A** (human to agent) | mention/DM | route | mention/DM | @username | POST /task |
61
+ | **A2H reply** | streaming | text | text | per-agent token | JSON |
62
+ | **A2H initiate** | /send | /send | /send | /send | - |
63
+ | **A2A delegation** | per-account bot chain | shared number + name prefix | shared + name prefix | @mention webhook | - |
64
+ | **Cross-channel** | /send | /send | /send | /send | - |
65
+ | **Cross-mesh** | mesh task | mesh task | mesh task | mesh task | mesh task |
66
+
67
+ ### Cross-Channel Messaging
68
+
69
+ Agents can send messages to ANY channel proactively — not just reply:
70
+
71
+ ```bash
72
+ # Agent on GitLab sends a notification to Telegram
73
+ curl -X POST http://localhost:19900/send \
74
+ -H "Content-Type: application/json" \
75
+ -d '{"channel":"telegram","chatId":"-1001234567890","text":"Deploy complete!","agentId":"devops"}'
76
+ ```
77
+
78
+ This enables **H2A2H chains**: a human asks an agent on GitLab to notify someone on WhatsApp. The agent calls `/send` to deliver the message cross-channel.
79
+
39
80
  ## Features
40
81
 
41
82
  ### Channels
42
- - **Telegram** — Multi-account bots, streaming responses, HTML formatting, typing indicators, seen reactions, bot-to-bot delegation, media handling (photos, voice, audio, video, documents)
43
- - **WhatsApp** — Baileys integration, QR pairing, self-chat mode, per-contact/group agent routing
44
- - **Discord** — Bot with mention-based routing, DM support
45
- - **GitLab** — Webhook channel: comments, issues, MRs, pipeline events route to agents. Agents reply as GitLab comments. @mention-based agent resolution in comments.
46
- - **Webhooks** — Generic `POST /webhook/:agentId` endpoint for Stripe, Sentry, GitHub, etc.
47
-
48
- ### Core
49
- - **Multi-agent** — Named agents with custom permissions, concurrency limits, mention-based routing
50
- - **Context engine** — 8-layer structured context with per-layer token budgets (channel, scope, identity, peers, intent, artifacts, history, wiki)
51
- - **Wiki knowledge base** — Karpathy/Farzapedia-inspired Markdown wiki with permissions (private/shared/public). Agents compile conversations into articles.
52
- - **Session continuity** — `--resume SESSION_ID` for Claude Code, conversation history injection for other tiers
53
- - **Bot-to-bot** — Agents mention each other on Telegram, conversation chains with loop prevention (visited set + max depth)
54
- - **Group context** — Persistent group conversation log, agents see last 30 messages when mentioned
55
- - **Media handling** — Photos, voice messages, audio, video, documents downloaded and passed to agent
56
- - **Reply-to context** — When replying to a message, agent sees the original text
57
-
58
- ### Operations
59
- - **Cron scheduler** — Timezone-aware recurring tasks with run logging
60
- - **A2A mesh** — Cross-machine agent communication over Tailscale/VPN
61
- - **Rate limiting** — Per-agent, 10/min, 100/hour (configurable)
62
- - **Token tracking** — Estimated tokens per agent per day, 7-day summaries
63
- - **Process management** — PID file, graceful shutdown, uncaught exception handlers
64
- - **72 unit tests** — Config, sessions, wiki, group log, context engine, telegram format
65
-
66
- ## CLI Commands
67
-
68
- ### Daemon
83
+
84
+ | Channel | Highlights |
85
+ |---------|-----------|
86
+ | **Telegram** | Multi-account bots, streaming edits, bot-to-bot delegation, media handling |
87
+ | **WhatsApp** | Baileys integration, QR pairing, per-contact/group routing, agent delegation (shared number, name-prefixed) |
88
+ | **Discord** | Mention-based routing, DM support, agent delegation |
89
+ | **GitLab** | Per-agent identity via PAT tokens, @mention routing resolved from API, bot-to-bot handoff, cascade prevention |
90
+ | **Webhooks** | Generic `POST /webhook/:agentId` for Stripe, Sentry, GitHub, etc. |
91
+
92
+ ### GitLab Integration
93
+
94
+ Agents participate in GitLab as first-class team members:
95
+ - **Per-agent identity** — Each agent has its own GitLab user and PAT. Agent usernames resolved from tokens at startup via API (not manual config)
96
+ - **Deterministic @mention routing** — `@coding-agent` routes to that agent. Username-to-agent map built from token resolution
97
+ - **Bot-to-bot handoff** — QA agent can `@mention` devops in its review, devops picks it up automatically
98
+ - **Eye reaction** — Agents react with 👀 using their own token (never the global token)
99
+ - **Cascade prevention** — Hidden signature `<!-- agentx:agentId -->`, sent-note dedup, bot-user detection
100
+ - **Human mention filtering** — If @mentioned user isn't a known agent, the note is ignored
101
+
102
+ ### Context Compaction
103
+
104
+ Long conversations don't lose context. When session history exceeds threshold:
105
+ 1. **Memory flush** — Haiku extracts memorable facts before compaction
106
+ 2. **Summarize** — Older messages compressed into a structured summary
107
+ 3. **Preserve** — Last 6 messages kept verbatim, tool call pairs kept intact
108
+
109
+ ### Message Queue
110
+
111
+ When an agent is busy, incoming messages are queued instead of dropped:
112
+
113
+ | Mode | Behavior |
114
+ |------|----------|
115
+ | `collect` (default) | Batch all messages, deliver as one when agent finishes |
116
+ | `followup` | Process each queued message as a separate follow-up turn |
117
+ | `drop` | Silently discard (for non-critical channels) |
118
+
119
+ Configure per agent: `agents.<id>.queueMode`
120
+
121
+ ### Sub-Agent Spawning
122
+
123
+ Agents can spawn background sub-agents for parallel work:
124
+ - `spawn_agent` tool available in orchestrator tier
125
+ - Depth-limited (max 3 levels) to prevent infinite chains
126
+ - Timeout enforcement per spawn
127
+ - Results returned to parent agent
128
+
129
+ ### Bootstrap Identity Files
130
+
131
+ Structured workspace files loaded into agent context automatically:
132
+
133
+ | File | Purpose | Lifecycle |
134
+ |------|---------|-----------|
135
+ | `SOUL.md` | Personality, tone, boundaries | Persistent |
136
+ | `IDENTITY.md` | Name, role, tagline | Persistent |
137
+ | `USER.md` | User profile, preferences | Persistent |
138
+ | `AGENTS.md` | Operating rules, standing orders | Persistent |
139
+ | `BOOTSTRAP.md` | First-run ritual | Auto-deleted after first load |
140
+
141
+ ### Heartbeat
142
+
143
+ Periodic in-session check-ins (unlike cron which creates isolated sessions):
144
+
145
+ ```jsonc
146
+ "agents": {
147
+ "assistant": {
148
+ "heartbeat": {
149
+ "enabled": true,
150
+ "intervalMinutes": 30,
151
+ "prompt": "Check inbox, pending tasks, and system health."
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ Heartbeat runs in the agent's existing session context, preserving conversation history and memory.
158
+
159
+ ### Cron with Retry & Missed Run Detection
160
+
161
+ Scheduled jobs with production-grade reliability:
162
+ - **Exponential backoff** on failure: 30s, 1m, 5m, 15m, 60m (up to 5 retries)
163
+ - **Missed run detection** — On startup, compares saved last-run times against schedule and catches up
164
+ - **Failure notifications** — Configure per-job `notify` destination (any channel)
165
+ - **Auto-disable** after 3 consecutive failures (`onError: "disable"`)
166
+ - **Health endpoint** — `GET /crons/health` returns healthy/failing/disabled/missed counts
167
+
168
+ ```jsonc
169
+ "crons": {
170
+ "daily-report": {
171
+ "schedule": "0 9 * * *",
172
+ "agent": "assistant",
173
+ "prompt": "Generate today's status report.",
174
+ "notify": {
175
+ "channel": "telegram",
176
+ "chatId": "-1001234567890"
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### Smart Block Streaming
183
+
184
+ Intelligent chunking for message delivery across channels:
185
+ - **minChars** threshold — no fragment spam
186
+ - **Paragraph-aware breaks** — never splits inside code fences
187
+ - **Per-channel defaults** — Telegram (60 chars), WhatsApp (40 chars + human pacing), Discord (80 chars)
188
+ - **Coalescing** — idle debounce merges consecutive small blocks
189
+
190
+ ### Observability & Debug Mode
191
+
192
+ Toggle verbose debug logging at runtime:
193
+
69
194
  ```bash
70
- agentx daemon start [--detach] # Start (foreground or background)
71
- agentx daemon stop # Graceful shutdown
72
- agentx daemon status # Agents, crons, mesh health
73
- agentx daemon logs [-f] # Tail logs
74
- agentx daemon send <agent> <msg> # Send task to agent
75
- agentx daemon send <agent> <msg> --peer server-2 # Remote agent
76
- agentx daemon deploy <host> -i key [--restart] # Deploy (runs tests first)
195
+ # Enable debug for specific categories
196
+ curl -X POST http://localhost:19900/debug/on?categories=webhook,agent
197
+
198
+ # Check debug state + recent log entries
199
+ curl http://localhost:19900/debug
200
+
201
+ # Disable
202
+ curl -X POST http://localhost:19900/debug/off
203
+
204
+ # Or via environment variable
205
+ AGENTX_DEBUG=webhook,agent agentx daemon start
77
206
  ```
78
207
 
79
- ### Management
208
+ **Categories:** `webhook`, `agent`, `channel`, `cron`, `mesh`, `context`, `memory`, `config`, `all`
209
+
210
+ ### Live Monitoring
211
+
212
+ ```bash
213
+ agentx daemon watch # Color-coded live feed
214
+ ```
215
+
216
+ Or connect via SSE:
217
+ ```bash
218
+ curl -N http://localhost:19900/events
219
+ ```
220
+
221
+ ### Wiki Knowledge Base
222
+
223
+ Each agent has a personal wiki — a compounding knowledge artifact built from conversations.
224
+
225
+ 1. **Ingest** — Every conversation saved as raw entry
226
+ 2. **Absorb** — LLM compiles entries into wiki articles
227
+ 3. **Query** — Agents get relevant context filtered by tags
228
+ 4. **Sync** — Federated wiki view across mesh peers
229
+
230
+ Three compilation modes: `unified` (default), `flat`, `graph`. Wikipedia-style UI at `agentx wiki serve`.
231
+
232
+ ### Persistent Agent Memory
233
+
234
+ Haiku-powered cross-session memory:
235
+ - Facts, preferences, commitments extracted after each conversation
236
+ - BM25 full-text search across memories
237
+ - Injected into context for future conversations
238
+ - Auto-pruning with configurable expiry
239
+
240
+ ### Skills
241
+
242
+ Reusable capabilities installed per-agent or globally:
243
+
244
+ ```bash
245
+ agentx skill add skills/my-skill --all
246
+ agentx skill list
247
+ ```
248
+
249
+ ### Mesh Federation
250
+
251
+ Agents on different machines collaborate:
252
+
253
+ ```bash
254
+ agentx mesh add server-2 http://100.x.x.x:19900
255
+ agentx daemon send devops "check disk space" --peer server-2
256
+ ```
257
+
258
+ - Static or mDNS peer discovery
259
+ - Health checks every 60s
260
+ - Wiki sync across peers
261
+ - Cross-machine agent delegation via A2A protocol
262
+
263
+ ## CLI Reference
264
+
80
265
  ```bash
81
- agentx agent add / list / remove <id>
82
- agentx channel add / list # Telegram, WhatsApp, Discord, GitLab
83
- agentx cron add / list / enable / disable <id>
84
- agentx mesh add / list / remove <name>
85
- agentx skill add <path> [--agent X] [--all] / list
266
+ # Daemon
267
+ agentx daemon start [--detach]
268
+ agentx daemon stop
269
+ agentx daemon status
270
+ agentx daemon watch
271
+ agentx daemon logs [-f]
272
+ agentx daemon send <agent> <msg> [--peer <name>]
273
+ agentx daemon deploy <host> -i key [--restart]
274
+
275
+ # Wiki
276
+ agentx wiki status [--mode M]
277
+ agentx wiki absorb [--agent X] [--mode M] [--dry-run]
278
+ agentx wiki serve [--mode M] [--peer URL]
279
+ agentx wiki sync [--peer URL]
280
+ agentx wiki compare --agent X
281
+ agentx wiki lint [--agent X]
282
+ agentx wiki search <query>
283
+
284
+ # Management
285
+ agentx agent add | list | remove <id>
286
+ agentx channel add | list
287
+ agentx cron add | list | enable | disable <id>
288
+ agentx mesh add | list | remove <name>
289
+ agentx skill add <path> [--agent X] [--all] | list
86
290
  agentx hook add <agent>
87
- agentx config check # Validate config + workspaces
88
- agentx config show # Print resolved config
89
- agentx migrate openclaw [path] [--dry-run]
291
+ agentx config check | show
90
292
  agentx init [--force]
293
+
294
+ # Usage
295
+ agentx usage [report] [--days N]
91
296
  ```
92
297
 
93
298
  ## Configuration
@@ -96,7 +301,7 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
96
301
 
97
302
  ```jsonc
98
303
  {
99
- "node": { "id": "my-machine", "name": "My Machine", "bind": "127.0.0.1:18800" },
304
+ "node": { "id": "my-machine", "name": "My Machine", "bind": "0.0.0.0:19900" },
100
305
 
101
306
  "agents": {
102
307
  "assistant": {
@@ -104,9 +309,15 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
104
309
  "workspace": "/path/to/workspace",
105
310
  "tier": "claude-code", // "sdk" or "orchestrator"
106
311
  "model": "claude-sonnet-4-6",
107
- "mentions": ["@my_bot"],
312
+ "mentions": ["@my_bot", "my-gitlab-user"],
108
313
  "maxConcurrent": 2,
109
- "systemPrompt": "You are a helpful assistant."
314
+ "queueMode": "collect", // "followup" or "drop"
315
+ "systemPrompt": "You are a helpful assistant.",
316
+ "heartbeat": {
317
+ "enabled": false,
318
+ "intervalMinutes": 30,
319
+ "prompt": "Check inbox and pending tasks."
320
+ }
110
321
  }
111
322
  },
112
323
 
@@ -118,19 +329,6 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
118
329
  },
119
330
  "policy": { "dm": "pair", "group": "mention-required" }
120
331
  },
121
- "whatsapp": {
122
- "enabled": true,
123
- "defaultAgent": "assistant",
124
- "routes": [
125
- { "contact": "+1234567890", "agent": "assistant" },
126
- { "group": "Team Chat", "agent": "devops" }
127
- ]
128
- },
129
- "discord": {
130
- "enabled": true,
131
- "token": "${DISCORD_BOT_TOKEN}",
132
- "agentBinding": "assistant"
133
- },
134
332
  "gitlab": {
135
333
  "enabled": true,
136
334
  "host": "https://gitlab.example.com",
@@ -138,11 +336,14 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
138
336
  "webhookPort": 18810,
139
337
  "routes": [
140
338
  { "project": "team/project-a", "agent": "pm-a" },
141
- { "project": "*", "agent": "atlas" }
339
+ { "project": "*", "agent": "default-agent" }
142
340
  ],
143
341
  "agentMappings": [
144
- { "agentId": "coder", "gitlabUsernames": ["coder-bot"], "keywords": ["coder"] },
145
- { "agentId": "devops", "gitlabUsernames": ["devops-bot"], "keywords": ["deploy"] }
342
+ {
343
+ "agentId": "coder",
344
+ "gitlabUsernames": ["coder-bot"],
345
+ "token": "${CODER_GITLAB_TOKEN}"
346
+ }
146
347
  ]
147
348
  }
148
349
  },
@@ -153,65 +354,65 @@ Single `agentx.json`. Environment variables expanded (`${VAR_NAME}`). Auto-loads
153
354
  "schedule": "0 9 * * *",
154
355
  "timezone": "UTC",
155
356
  "agent": "assistant",
156
- "prompt": "Generate today's status report."
357
+ "prompt": "Generate today's status report.",
358
+ "onError": "notify",
359
+ "notify": {
360
+ "channel": "telegram",
361
+ "chatId": "-1001234567890"
362
+ }
157
363
  }
158
364
  },
159
365
 
160
366
  "mesh": {
161
367
  "enabled": true,
162
368
  "peers": [
163
- { "url": "http://100.67.108.119:19900", "name": "server-2" }
369
+ { "url": "http://100.x.x.x:19900", "name": "server-2" }
164
370
  ]
165
371
  }
166
372
  }
167
373
  ```
168
374
 
169
- ## Context Engine
170
-
171
- Every agent prompt is built from 8 structured layers, each with a token budget:
172
-
173
- | Layer | Priority | Budget | Content |
174
- |-------|----------|--------|---------|
175
- | Channel | 1 | 200 | Channel type + rules (GitLab: no @handles, use GFM) |
176
- | Scope | 2 | 200 | Group name, project path, or DM |
177
- | Identity | 3 | 300 | Agent system prompt (first line) |
178
- | Peers | 4 | 400 | Team roster with handles (Telegram only) |
179
- | Intent | 5 | 200 | Extracted from message: deploy, review, bugfix... |
180
- | Artifacts | 6 | 500 | Media, reply-to text, issue/MR references |
181
- | History | 7 | 1200 | Group conversation or session history |
182
- | Wiki | 8 | 1000 | Relevant knowledge articles |
183
-
184
- Total budget: 4000 tokens. Lower layers truncated if over budget.
185
-
186
375
  ## HTTP API
187
376
 
188
377
  | Endpoint | Method | Description |
189
378
  |----------|--------|-------------|
190
- | `/health` | GET | Status, agents, crons, mesh, today's usage |
379
+ | `/health` | GET | Status, agents, crons, mesh, usage |
380
+ | `/events` | GET | SSE live event stream |
191
381
  | `/agents` | GET | List agents |
382
+ | `/channels` | GET | List registered channels |
192
383
  | `/crons` | GET | List cron jobs |
193
- | `/mesh` | GET | Mesh peers |
194
- | `/usage` | GET | 7-day token usage per agent |
384
+ | `/crons/health` | GET | Cron health: healthy/failing/disabled/missed |
195
385
  | `/task` | POST | `{ "agent": "id", "message": "..." }` |
386
+ | `/send` | POST | `{ "channel": "telegram", "chatId": "...", "text": "...", "agentId": "..." }` |
196
387
  | `/mesh/task` | POST | `{ "peer": "name", "message": "..." }` |
197
- | `/webhook/:agentId[/:source]` | POST | Webhook callback (GitLab, GitHub, Stripe, Sentry) |
198
- | `/v1/chat/completions` | POST | OpenAI-compatible endpoint (ElevenLabs, Cursor, any client) |
199
- | `/llm/:agentId/v1/chat/completions` | POST | OpenAI-compatible with explicit agent |
388
+ | `/webhook/:agentId[/:source]` | POST | Webhook callback |
389
+ | `/v1/chat/completions` | POST | OpenAI-compatible endpoint |
390
+ | `/debug` | GET | Debug state + recent log entries |
391
+ | `/debug/on` | POST | Enable debug (`?categories=webhook,agent`) |
392
+ | `/debug/off` | POST | Disable debug |
200
393
  | `/.well-known/agent-card.json` | GET | A2A agent discovery |
201
394
 
202
- ## OpenAI-Compatible Endpoint
395
+ ## Context Engine
203
396
 
204
- Any agent can be used as an LLM backend for ElevenLabs Conversational AI, Cursor, or any OpenAI-compatible client.
397
+ Every agent prompt is built from structured layers, each with a token budget:
205
398
 
206
- ```bash
207
- curl -X POST http://your-server:18800/v1/chat/completions \
208
- -H "Content-Type: application/json" \
209
- -d '{"model": "atlas", "messages": [{"role": "user", "content": "Hello"}]}'
210
- ```
399
+ | Layer | Priority | Budget | Content |
400
+ |-------|----------|--------|---------|
401
+ | Channel | 1 | 200 | Channel type + formatting rules |
402
+ | Scope | 2 | 200 | Group name, project path, or DM |
403
+ | Landscape | 3 | 800 | Team roster, mesh peers, rules, /send API docs |
404
+ | Identity | 4 | 200 | Agent system prompt |
405
+ | Bootstrap | 4.5 | 500 | SOUL.md, IDENTITY.md, AGENTS.md |
406
+ | Intent | 5 | 200 | Extracted: deploy, review, bugfix... |
407
+ | Artifacts | 6 | 500 | Media, reply-to text, issue refs |
408
+ | Memory | 6.5 | 600 | Cross-session facts (Haiku-extracted) |
409
+ | History | 7 | 1200 | Conversation or session history |
410
+ | Cross-chat | 7 | 800 | Context from other active chats |
411
+ | Wiki | 8 | 1000 | Tag-matched knowledge articles |
211
412
 
212
- Supports streaming (`"stream": true`) with SSE. The `model` field maps to the agent ID.
413
+ Total budget: 6000 tokens. Context always injected, even on resumed sessions.
213
414
 
214
- ## Three execution tiers
415
+ ## Three Execution Tiers
215
416
 
216
417
  | Tier | How | Auth | Best for |
217
418
  |------|-----|------|----------|
@@ -219,25 +420,45 @@ Supports streaming (`"stream": true`) with SSE. The `model` field maps to the ag
219
420
  | `sdk` | Claude Agent SDK | API key | Programmatic control, headless servers |
220
421
  | `orchestrator` | AgentX's own loop | Any provider key | Non-Claude providers (OpenAI, Ollama) |
221
422
 
222
- ## Migrating from OpenClaw
423
+ ## Daemon Management
424
+
425
+ Single-instance guard via PID file — prevents orphan processes on restart:
223
426
 
224
427
  ```bash
225
- agentx migrate openclaw # Auto-imports agents, channels, crons, skills
428
+ agentx daemon start # Checks .agentx/daemon.pid, exits if another is running
429
+ agentx daemon stop # Graceful shutdown, saves cron last-run times
430
+ systemctl --user restart agentx-daemon.service # Safe — PID guard prevents duplicates
226
431
  ```
227
432
 
228
- ## Use cases
433
+ ## Use Cases
229
434
 
230
435
  - **Team of Telegram bots** — each project gets its own bot + agent
231
- - **GitLab code review** — comment on MR, agent reviews and replies as a GitLab comment
232
- - **WhatsApp assistant** — message yourself, agent replies in self-chat
233
- - **Scheduled content** — cron jobs generate blog posts, reports, social media drafts
234
- - **Multi-machine swarm** — agents on MacBook + server collaborate via mesh
235
- - **Webhook automation** — Sentry error → DevOps agent investigates, Stripe payment → billing agent processes
236
- - **Bot-to-bot delegation** — Nadia mentions @devops, DevOps picks up and responds
436
+ - **GitLab CI/CD team** — coder, QA, devops, PM agents collaborate on issues via @mentions
437
+ - **Cross-channel notifications** — GitLab agent notifies humans on Telegram/WhatsApp
438
+ - **WhatsApp delegation** — shared phone number, agents identified by name prefix
439
+ - **Scheduled automation** — cron jobs with retry, missed-run catch-up, failure alerts
440
+ - **Multi-machine swarm** — agents on laptop + server collaborate via mesh
441
+ - **Webhook automation** — Sentry error -> DevOps investigates, Stripe payment -> billing processes
442
+ - **Personal wiki** — Every conversation compounds into searchable knowledge
237
443
 
238
- ## Legal
444
+ ## Contributing
445
+
446
+ ```bash
447
+ git clone https://github.com/anis-marrouchi/agentx.git
448
+ cd agentx
449
+ npm install
450
+ npm run build # ESM build via tsup
451
+ npm test # Run tests
452
+ agentx init --force # Create local config
453
+ agentx daemon start # Start dev daemon
454
+ agentx daemon watch # See what's happening
455
+ ```
239
456
 
240
- Self-hosted, bring-your-own-key. No credentials stored or proxied. Built on official public packages (Claude API, Claude Agent SDK, Claude Code CLI). Same model as LangChain, CrewAI, AutoGen.
457
+ Areas we're looking for help:
458
+ - **Testing** — Unit and integration tests
459
+ - **Channels** — Slack, Microsoft Teams adapters
460
+ - **Dashboard** — Web UI for agent management
461
+ - **Security** — Audit of token handling and permission boundaries
241
462
 
242
463
  ## License
243
464
 
@@ -0,0 +1,2 @@
1
+ import{s as a,t as b,u as c}from"./chunk-SBUX74OU.js";import"./chunk-SFQUP3BP.js";import"./chunk-CJ45Y2IR.js";import"./chunk-X32247GM.js";import"./chunk-KXZMYLHQ.js";import"./chunk-M7HKBG3V.js";import"./chunk-FRFR27IN.js";import"./chunk-DSYKYZMT.js";import"./chunk-4YCH6IZV.js";export{a as createAgentContext,b as generate,c as generateStream};
2
+ //# sourceMappingURL=agent-QYQVGKLM.js.map
@@ -0,0 +1,10 @@
1
+ import{readFileSync as R,writeFileSync as D,appendFileSync as S,existsSync as C,mkdirSync as _}from"fs";import{resolve as T}from"path";var y={"claude-opus":{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},"claude-sonnet":{input:3,output:15,cacheRead:.3,cacheCreate:3.75},"claude-haiku":{input:.25,output:1.25,cacheRead:.025,cacheCreate:.3125}};function v(O){let t=O.toLowerCase();return t.includes("opus")?"claude-opus":t.includes("haiku")?"claude-haiku":"claude-sonnet"}var b=class{dir;cache=null;constructor(t=T(process.cwd(),".agentx/usage")){this.dir=t,_(this.dir,{recursive:!0})}static calculateCost(t){let o=v(t.model||"claude-sonnet"),n=y[o]||y["claude-sonnet"];return(t.inputTokens||0)/1e6*n.input+(t.outputTokens||0)/1e6*n.output+(t.cacheReadTokens||0)/1e6*n.cacheRead+(t.cacheCreateTokens||0)/1e6*n.cacheCreate}record(t,o,n,c,s,i,l){let e=this.today(),a=e.agents[t]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,totalDuration:0,errors:0};a.tasks++,a.totalDuration+=o,l&&(a.model=l),n?(a.inputTokens+=n.inputTokens,a.outputTokens+=n.outputTokens,a.cacheReadTokens+=n.cacheReadTokens,a.cacheCreateTokens+=n.cacheCreateTokens):c!==void 0&&s!==void 0&&(a.inputTokens+=Math.ceil(c/4),a.outputTokens+=Math.ceil(s/4)),i&&a.errors++,e.agents[t]=a,this.save(e)}today(){let t=new Date().toISOString().slice(0,10);if(this.cache?.date===t)return this.cache;let o=this.filePath(t);if(C(o))try{return this.cache=JSON.parse(R(o,"utf-8")),this.cache}catch{}return this.cache={date:t,agents:{}},this.cache}getDate(t){let o=this.filePath(t);if(!C(o))return null;try{return JSON.parse(R(o,"utf-8"))}catch{return null}}generateDailyReport(t){let o=t||new Date().toISOString().slice(0,10),n=this.getDate(o);if(!n)return null;let c=0,s=0,i=0,l=0,e=0,a=0,g="",m=0,p={};for(let[k,h]of Object.entries(n.agents)){let d=b.calculateCost(h);p[k]=d,c+=h.tasks,s+=h.inputTokens||0,i+=h.outputTokens||0,l+=h.cacheReadTokens||0,e+=h.cacheCreateTokens||0,a+=d,d>m&&(m=d,g=k)}return{date:o,totalTasks:c,totalInput:s,totalOutput:i,totalCacheRead:l,totalCacheCreate:e,totalCost:a,topAgent:g,topCost:m,agentCosts:p}}appendToTokenCosts(t,o){let n=o||T(process.cwd(),".agentx/TOKEN_COSTS.md"),c=e=>e<.01?e.toFixed(4):e.toFixed(2),s=e=>e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(0)}K`:String(e),i=Object.entries(t.agentCosts).sort(([,e],[,a])=>a-e).map(([e,a])=>`${e}: $${c(a)}`).join(", "),l=`| ${t.date} | ${t.totalTasks} | ${s(t.totalInput)} | ${s(t.totalOutput)} | ${s(t.totalCacheRead)} | ${s(t.totalCacheCreate)} | $${c(t.totalCost)} | ${i} |`;C(n)?S(n,l+`
2
+ `):D(n,`# Token Costs
3
+
4
+ Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).
5
+
6
+ | Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent |
7
+ |------|-------|-------|--------|---------|---------|------|-----------|
8
+ `+l+`
9
+ `)}summary(t=7){let o=0,n=0,c=0,s=0,i=0,l=0,e=0,a={};for(let p=0;p<t;p++){let k=new Date(Date.now()-p*864e5).toISOString().slice(0,10),h=this.getDate(k);if(h)for(let[d,u]of Object.entries(h.agents)){let f=b.calculateCost(u);o+=u.tasks,n+=u.inputTokens||0,c+=u.outputTokens||0,s+=u.cacheReadTokens||0,i+=u.cacheCreateTokens||0,l+=u.errors,e+=f;let r=a[d]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0};r.tasks+=u.tasks,r.input+=u.inputTokens||0,r.output+=u.outputTokens||0,r.cacheRead+=u.cacheReadTokens||0,r.cacheCreate+=u.cacheCreateTokens||0,r.total=r.input+r.output+r.cacheRead+r.cacheCreate,r.totalDuration+=u.totalDuration,r.avgDuration=r.totalDuration/r.tasks,r.cost+=f,u.model&&(r.model=u.model),a[d]=r}}let g=n+c+s+i,m=s+i>0?s/(s+i):0;return{totalTasks:o,totalTokens:g,totalInput:n,totalOutput:c,totalCacheRead:s,totalCacheCreate:i,cacheHitRatio:m,totalErrors:l,totalCost:e,byAgent:a}}filePath(t){return T(this.dir,`${t}.json`)}save(t){try{D(this.filePath(t.date),JSON.stringify(t,null,2))}catch{}}};export{b as a};
10
+ //# sourceMappingURL=chunk-34UAFPK4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/token-tracker.ts"],"sourcesContent":["import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks token usage per agent per day with cache-aware cost calculation.\n// Persisted to .agentx/usage/{YYYY-MM-DD}.json\n\n// Anthropic pricing per million tokens (as of April 2026)\nexport const CACHE_AWARE_PRICING: Record<string, { input: number; output: number; cacheRead: number; cacheCreate: number }> = {\n \"claude-opus\": { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },\n \"claude-sonnet\": { input: 3, output: 15, cacheRead: 0.3, cacheCreate: 3.75 },\n \"claude-haiku\": { input: 0.25, output: 1.25, cacheRead: 0.025, cacheCreate: 0.3125 },\n}\n\nexport function getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) return \"claude-opus\"\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\"\n}\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\nexport interface AgentUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n totalDuration: number\n errors: number\n model?: string\n}\n\nexport interface DailyReport {\n date: string\n totalTasks: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n totalCost: number\n topAgent: string\n topCost: number\n agentCosts: Record<string, number>\n}\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n }\n\n /**\n * Calculate cost for an agent's usage with cache-aware pricing.\n */\n static calculateCost(usage: AgentUsage): number {\n const family = getModelFamily(usage.model || \"claude-sonnet\")\n const pricing = CACHE_AWARE_PRICING[family] || CACHE_AWARE_PRICING[\"claude-sonnet\"]\n\n return (\n ((usage.inputTokens || 0) / 1_000_000) * pricing.input +\n ((usage.outputTokens || 0) / 1_000_000) * pricing.output +\n ((usage.cacheReadTokens || 0) / 1_000_000) * pricing.cacheRead +\n ((usage.cacheCreateTokens || 0) / 1_000_000) * pricing.cacheCreate\n )\n }\n\n /**\n * Record a task execution with real or estimated token counts.\n */\n record(\n agentId: string,\n duration: number,\n realUsage?: { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheCreateTokens: number },\n messageLength?: number,\n responseLength?: number,\n error?: boolean,\n model?: string,\n ): void {\n const daily = this.today()\n const agent = daily.agents[agentId] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0, totalDuration: 0, errors: 0,\n }\n\n agent.tasks++\n agent.totalDuration += duration\n if (model) agent.model = model\n\n if (realUsage) {\n agent.inputTokens += realUsage.inputTokens\n agent.outputTokens += realUsage.outputTokens\n agent.cacheReadTokens += realUsage.cacheReadTokens\n agent.cacheCreateTokens += realUsage.cacheCreateTokens\n } else if (messageLength !== undefined && responseLength !== undefined) {\n agent.inputTokens += Math.ceil(messageLength / 4)\n agent.outputTokens += Math.ceil(responseLength / 4)\n }\n\n if (error) agent.errors++\n\n daily.agents[agentId] = agent\n this.save(daily)\n }\n\n /**\n * Get today's usage summary.\n */\n today(): DailyUsage {\n const date = new Date().toISOString().slice(0, 10)\n\n if (this.cache?.date === date) return this.cache\n\n const path = this.filePath(date)\n if (existsSync(path)) {\n try {\n this.cache = JSON.parse(readFileSync(path, \"utf-8\"))\n return this.cache!\n } catch {}\n }\n\n this.cache = { date, agents: {} }\n return this.cache\n }\n\n /**\n * Get usage for a specific date.\n */\n getDate(date: string): DailyUsage | null {\n const path = this.filePath(date)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"))\n } catch {\n return null\n }\n }\n\n /**\n * Generate a daily cost report for a given date.\n */\n generateDailyReport(date?: string): DailyReport | null {\n const targetDate = date || new Date().toISOString().slice(0, 10)\n const usage = this.getDate(targetDate)\n if (!usage) return null\n\n let totalTasks = 0, totalInput = 0, totalOutput = 0\n let totalCacheRead = 0, totalCacheCreate = 0, totalCost = 0\n let topAgent = \"\", topCost = 0\n const agentCosts: Record<string, number> = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = TokenTracker.calculateCost(agent)\n agentCosts[id] = cost\n totalTasks += agent.tasks\n totalInput += agent.inputTokens || 0\n totalOutput += agent.outputTokens || 0\n totalCacheRead += agent.cacheReadTokens || 0\n totalCacheCreate += agent.cacheCreateTokens || 0\n totalCost += cost\n if (cost > topCost) { topCost = cost; topAgent = id }\n }\n\n return {\n date: targetDate, totalTasks, totalInput, totalOutput,\n totalCacheRead, totalCacheCreate, totalCost, topAgent, topCost, agentCosts,\n }\n }\n\n /**\n * Append a daily report row to TOKEN_COSTS.md.\n */\n appendToTokenCosts(report: DailyReport, filePath?: string): void {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n const fmt = (n: number) => n < 0.01 ? n.toFixed(4) : n.toFixed(2)\n const fmtTok = (n: number) =>\n n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` :\n n >= 1_000 ? `${(n / 1_000).toFixed(0)}K` :\n String(n)\n\n const agentBreakdown = Object.entries(report.agentCosts)\n .sort(([, a], [, b]) => b - a)\n .map(([id, cost]) => `${id}: $${fmt(cost)}`)\n .join(\", \")\n\n const row = `| ${report.date} | ${report.totalTasks} | ${fmtTok(report.totalInput)} | ${fmtTok(report.totalOutput)} | ${fmtTok(report.totalCacheRead)} | ${fmtTok(report.totalCacheCreate)} | $${fmt(report.totalCost)} | ${agentBreakdown} |`\n\n if (!existsSync(file)) {\n const header =\n \"# Token Costs\\n\\n\" +\n \"Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).\\n\\n\" +\n \"| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent |\\n\" +\n \"|------|-------|-------|--------|---------|---------|------|-----------|\\n\"\n writeFileSync(file, header + row + \"\\n\")\n } else {\n appendFileSync(file, row + \"\\n\")\n }\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n cacheHitRatio: number\n totalErrors: number\n totalCost: number\n byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; cost: number }>\n } {\n let totalTasks = 0\n let totalInput = 0\n let totalOutput = 0\n let totalCacheRead = 0\n let totalCacheCreate = 0\n let totalErrors = 0\n let totalCost = 0\n const byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; totalDuration: number; cost: number; model?: string }> = {}\n\n for (let i = 0; i < days; i++) {\n const date = new Date(Date.now() - i * 86400_000).toISOString().slice(0, 10)\n const usage = this.getDate(date)\n if (!usage) continue\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = TokenTracker.calculateCost(agent)\n totalTasks += agent.tasks\n totalInput += agent.inputTokens || 0\n totalOutput += agent.outputTokens || 0\n totalCacheRead += agent.cacheReadTokens || 0\n totalCacheCreate += agent.cacheCreateTokens || 0\n totalErrors += agent.errors\n totalCost += cost\n\n const existing = byAgent[id] || { tasks: 0, input: 0, output: 0, cacheRead: 0, cacheCreate: 0, total: 0, avgDuration: 0, totalDuration: 0, cost: 0 }\n existing.tasks += agent.tasks\n existing.input += agent.inputTokens || 0\n existing.output += agent.outputTokens || 0\n existing.cacheRead += agent.cacheReadTokens || 0\n existing.cacheCreate += agent.cacheCreateTokens || 0\n existing.total = existing.input + existing.output + existing.cacheRead + existing.cacheCreate\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n existing.cost += cost\n if (agent.model) existing.model = agent.model\n byAgent[id] = existing\n }\n }\n\n const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheCreate\n const cacheHitRatio = (totalCacheRead + totalCacheCreate) > 0\n ? totalCacheRead / (totalCacheRead + totalCacheCreate)\n : 0\n\n return { totalTasks, totalTokens, totalInput, totalOutput, totalCacheRead, totalCacheCreate, cacheHitRatio, totalErrors, totalCost, byAgent }\n }\n\n private filePath(date: string): string {\n return resolve(this.dir, `${date}.json`)\n }\n\n private save(usage: DailyUsage): void {\n try {\n writeFileSync(this.filePath(usage.date), JSON.stringify(usage, null, 2))\n } catch {}\n }\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","resolve","CACHE_AWARE_PRICING","getModelFamily","model","lower","TokenTracker","baseDir","usage","family","pricing","agentId","duration","realUsage","messageLength","responseLength","error","daily","agent","date","path","targetDate","totalTasks","totalInput","totalOutput","totalCacheRead","totalCacheCreate","totalCost","topAgent","topCost","agentCosts","id","cost","report","filePath","file","fmt","n","fmtTok","agentBreakdown","a","b","row","days","totalErrors","byAgent","i","existing","totalTokens","cacheHitRatio"]}
@@ -1,4 +1,4 @@
1
- import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{g}from"./chunk-MGMZNJCE.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
1
+ import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{g}from"./chunk-KXZMYLHQ.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
2
2
  ${t}
3
3
  ${n}
4
4
  ${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
@@ -29,4 +29,4 @@ Fix the error. Use create_files with the corrected file content.`}],f=await n.ge
29
29
  `)+`
30
30
  ... (truncated)`:e.join(`
31
31
  `)}export{v as a};
32
- //# sourceMappingURL=chunk-X7UN6JAA.js.map
32
+ //# sourceMappingURL=chunk-6MKAXEE5.js.map