agentix-cli 0.6.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.
- package/README.md +337 -116
- package/dist/agent-QYQVGKLM.js +2 -0
- package/dist/chunk-34UAFPK4.js +10 -0
- package/dist/chunk-34UAFPK4.js.map +1 -0
- package/dist/{chunk-X7UN6JAA.js → chunk-6MKAXEE5.js} +2 -2
- package/dist/chunk-BAIQJHQF.js +64 -0
- package/dist/chunk-BAIQJHQF.js.map +1 -0
- package/dist/chunk-BHDLKX3G.js +6 -0
- package/dist/chunk-BHDLKX3G.js.map +1 -0
- package/dist/chunk-CJ45Y2IR.js +12 -0
- package/dist/chunk-CJ45Y2IR.js.map +1 -0
- package/dist/chunk-DSYKYZMT.js +2 -0
- package/dist/chunk-DSYKYZMT.js.map +1 -0
- package/dist/chunk-IVVVYXPH.js +136 -0
- package/dist/chunk-IVVVYXPH.js.map +1 -0
- package/dist/{chunk-MGMZNJCE.js → chunk-KXZMYLHQ.js} +27 -27
- package/dist/chunk-KXZMYLHQ.js.map +1 -0
- package/dist/chunk-SBUX74OU.js +108 -0
- package/dist/chunk-SBUX74OU.js.map +1 -0
- package/dist/chunk-X32247GM.js +2 -0
- package/dist/chunk-X32247GM.js.map +1 -0
- package/dist/chunk-ZRYBSI5G.js +23 -0
- package/dist/chunk-ZRYBSI5G.js.map +1 -0
- package/dist/cli.js +289 -9
- package/dist/cli.js.map +1 -1
- package/dist/compaction-E3MQRLTL.js +28 -0
- package/dist/compaction-E3MQRLTL.js.map +1 -0
- package/dist/config-MSWKC466.js +2 -0
- package/dist/debug-N3B5NVJU.js +2 -0
- package/dist/heal-OTGT5HHJ.js +2 -0
- package/dist/heal-OTGT5HHJ.js.map +1 -0
- package/dist/index.d.ts +707 -164
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/memory-extract-RGPUPMKU.js +2 -0
- package/dist/memory-extract-RGPUPMKU.js.map +1 -0
- package/dist/{providers-AVYG63KK.js → providers-G3WZ4RVJ.js} +2 -2
- package/dist/providers-G3WZ4RVJ.js.map +1 -0
- package/dist/registry-FP6FUOXF.js +2 -0
- package/dist/registry-FP6FUOXF.js.map +1 -0
- package/dist/subagent-WV7QKQ3L.js +3 -0
- package/dist/subagent-WV7QKQ3L.js.map +1 -0
- package/dist/usage-dashboard-2TJQBFSH.js +404 -0
- package/dist/usage-dashboard-2TJQBFSH.js.map +1 -0
- package/package.json +4 -1
- package/dist/agent-K2YOEOJ5.js +0 -2
- package/dist/chunk-F73GPYCO.js +0 -106
- package/dist/chunk-F73GPYCO.js.map +0 -1
- package/dist/chunk-MGMZNJCE.js.map +0 -1
- package/dist/chunk-WT2UJMBC.js +0 -68
- package/dist/chunk-WT2UJMBC.js.map +0 -1
- package/dist/chunk-Z4GC5D6D.js +0 -12
- package/dist/chunk-Z4GC5D6D.js.map +0 -1
- package/dist/heal-UV5A6B5T.js +0 -2
- /package/dist/{agent-K2YOEOJ5.js.map → agent-QYQVGKLM.js.map} +0 -0
- /package/dist/{chunk-X7UN6JAA.js.map → chunk-6MKAXEE5.js.map} +0 -0
- /package/dist/{heal-UV5A6B5T.js.map → config-MSWKC466.js.map} +0 -0
- /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
|
|
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
|
-
|
|
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
|
|
23
|
+
## Quick Start
|
|
14
24
|
|
|
15
25
|
```bash
|
|
16
|
-
agentx init
|
|
17
|
-
agentx agent add
|
|
18
|
-
agentx channel add
|
|
19
|
-
agentx daemon 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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- **
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
###
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
agentx
|
|
83
|
-
agentx
|
|
84
|
-
agentx
|
|
85
|
-
agentx
|
|
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
|
|
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": "
|
|
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
|
-
"
|
|
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": "
|
|
339
|
+
{ "project": "*", "agent": "default-agent" }
|
|
142
340
|
],
|
|
143
341
|
"agentMappings": [
|
|
144
|
-
{
|
|
145
|
-
|
|
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.
|
|
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,
|
|
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
|
-
| `/
|
|
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
|
|
198
|
-
| `/v1/chat/completions` | POST | OpenAI-compatible endpoint
|
|
199
|
-
| `/
|
|
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
|
-
##
|
|
395
|
+
## Context Engine
|
|
203
396
|
|
|
204
|
-
|
|
397
|
+
Every agent prompt is built from structured layers, each with a token budget:
|
|
205
398
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
413
|
+
Total budget: 6000 tokens. Context always injected, even on resumed sessions.
|
|
213
414
|
|
|
214
|
-
## Three
|
|
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
|
-
##
|
|
423
|
+
## Daemon Management
|
|
424
|
+
|
|
425
|
+
Single-instance guard via PID file — prevents orphan processes on restart:
|
|
223
426
|
|
|
224
427
|
```bash
|
|
225
|
-
agentx
|
|
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
|
|
433
|
+
## Use Cases
|
|
229
434
|
|
|
230
435
|
- **Team of Telegram bots** — each project gets its own bot + agent
|
|
231
|
-
- **GitLab
|
|
232
|
-
- **
|
|
233
|
-
- **
|
|
234
|
-
- **
|
|
235
|
-
- **
|
|
236
|
-
- **
|
|
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
|
-
##
|
|
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
|
-
|
|
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":"AAAA,OAAS,gBAAAA,EAAc,iBAAAC,EAAe,kBAAAC,EAAgB,cAAAC,EAAY,aAAAC,MAAiB,KACnF,OAAS,WAAAC,MAAe,OAOjB,IAAMC,EAAiH,CAC5H,cAAiB,CAAE,MAAO,GAAM,OAAQ,GAAM,UAAW,IAAQ,YAAa,KAAM,EACpF,gBAAiB,CAAE,MAAO,EAAM,OAAQ,GAAM,UAAW,GAAQ,YAAa,IAAK,EACnF,eAAiB,CAAE,MAAO,IAAM,OAAQ,KAAM,UAAW,KAAQ,YAAa,KAAO,CACvF,EAEO,SAASC,EAAeC,EAAuB,CACpD,IAAMC,EAAQD,EAAM,YAAY,EAChC,OAAIC,EAAM,SAAS,MAAM,EAAU,cAC/BA,EAAM,SAAS,OAAO,EAAU,eAC7B,eACT,CA+BO,IAAMC,EAAN,KAAmB,CAChB,IACA,MAA2B,KAEnC,YAAYC,EAAkBN,EAAQ,QAAQ,IAAI,EAAG,eAAe,EAAG,CACrE,KAAK,IAAMM,EACXP,EAAU,KAAK,IAAK,CAAE,UAAW,EAAK,CAAC,CACzC,CAKA,OAAO,cAAcQ,EAA2B,CAC9C,IAAMC,EAASN,EAAeK,EAAM,OAAS,eAAe,EACtDE,EAAUR,EAAoBO,CAAM,GAAKP,EAAoB,eAAe,EAElF,OACIM,EAAM,aAAe,GAAK,IAAaE,EAAQ,OAC/CF,EAAM,cAAgB,GAAK,IAAaE,EAAQ,QAChDF,EAAM,iBAAmB,GAAK,IAAaE,EAAQ,WACnDF,EAAM,mBAAqB,GAAK,IAAaE,EAAQ,WAE3D,CAKA,OACEC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAZ,EACM,CACN,IAAMa,EAAQ,KAAK,MAAM,EACnBC,EAAQD,EAAM,OAAON,CAAO,GAAK,CACrC,MAAO,EAAG,YAAa,EAAG,aAAc,EACxC,gBAAiB,EAAG,kBAAmB,EAAG,cAAe,EAAG,OAAQ,CACtE,EAEAO,EAAM,QACNA,EAAM,eAAiBN,EACnBR,IAAOc,EAAM,MAAQd,GAErBS,GACFK,EAAM,aAAeL,EAAU,YAC/BK,EAAM,cAAgBL,EAAU,aAChCK,EAAM,iBAAmBL,EAAU,gBACnCK,EAAM,mBAAqBL,EAAU,mBAC5BC,IAAkB,QAAaC,IAAmB,SAC3DG,EAAM,aAAe,KAAK,KAAKJ,EAAgB,CAAC,EAChDI,EAAM,cAAgB,KAAK,KAAKH,EAAiB,CAAC,GAGhDC,GAAOE,EAAM,SAEjBD,EAAM,OAAON,CAAO,EAAIO,EACxB,KAAK,KAAKD,CAAK,CACjB,CAKA,OAAoB,CAClB,IAAME,EAAO,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAEjD,GAAI,KAAK,OAAO,OAASA,EAAM,OAAO,KAAK,MAE3C,IAAMC,EAAO,KAAK,SAASD,CAAI,EAC/B,GAAIpB,EAAWqB,CAAI,EACjB,GAAI,CACF,YAAK,MAAQ,KAAK,MAAMxB,EAAawB,EAAM,OAAO,CAAC,EAC5C,KAAK,KACd,MAAE,CAAO,CAGX,YAAK,MAAQ,CAAE,KAAAD,EAAM,OAAQ,CAAC,CAAE,EACzB,KAAK,KACd,CAKA,QAAQA,EAAiC,CACvC,IAAMC,EAAO,KAAK,SAASD,CAAI,EAC/B,GAAI,CAACpB,EAAWqB,CAAI,EAAG,OAAO,KAC9B,GAAI,CACF,OAAO,KAAK,MAAMxB,EAAawB,EAAM,OAAO,CAAC,CAC/C,MAAE,CACA,OAAO,IACT,CACF,CAKA,oBAAoBD,EAAmC,CACrD,IAAME,EAAaF,GAAQ,IAAI,KAAK,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EACzDX,EAAQ,KAAK,QAAQa,CAAU,EACrC,GAAI,CAACb,EAAO,OAAO,KAEnB,IAAIc,EAAa,EAAGC,EAAa,EAAGC,EAAc,EAC9CC,EAAiB,EAAGC,EAAmB,EAAGC,EAAY,EACtDC,EAAW,GAAIC,EAAU,EACvBC,EAAqC,CAAC,EAE5C,OAAW,CAACC,EAAIb,CAAK,IAAK,OAAO,QAAQV,EAAM,MAAM,EAAG,CACtD,IAAMwB,EAAO1B,EAAa,cAAcY,CAAK,EAC7CY,EAAWC,CAAE,EAAIC,EACjBV,GAAcJ,EAAM,MACpBK,GAAcL,EAAM,aAAe,EACnCM,GAAeN,EAAM,cAAgB,EACrCO,GAAkBP,EAAM,iBAAmB,EAC3CQ,GAAoBR,EAAM,mBAAqB,EAC/CS,GAAaK,EACTA,EAAOH,IAAWA,EAAUG,EAAMJ,EAAWG,GAGnD,MAAO,CACL,KAAMV,EAAY,WAAAC,EAAY,WAAAC,EAAY,YAAAC,EAC1C,eAAAC,EAAgB,iBAAAC,EAAkB,UAAAC,EAAW,SAAAC,EAAU,QAAAC,EAAS,WAAAC,CAClE,CACF,CAKA,mBAAmBG,EAAqBC,EAAyB,CAC/D,IAAMC,EAAOD,GAAYjC,EAAQ,QAAQ,IAAI,EAAG,wBAAwB,EAClEmC,EAAOC,GAAcA,EAAI,IAAOA,EAAE,QAAQ,CAAC,EAAIA,EAAE,QAAQ,CAAC,EAC1DC,EAAUD,GACdA,GAAK,IAAY,IAAIA,EAAI,KAAW,QAAQ,CAAC,KAC7CA,GAAK,IAAQ,IAAIA,EAAI,KAAO,QAAQ,CAAC,KACrC,OAAOA,CAAC,EAEJE,EAAiB,OAAO,QAAQN,EAAO,UAAU,EACpD,KAAK,CAAC,CAAC,CAAEO,CAAC,EAAG,CAAC,CAAEC,CAAC,IAAMA,EAAID,CAAC,EAC5B,IAAI,CAAC,CAACT,EAAIC,CAAI,IAAM,GAAGD,OAAQK,EAAIJ,CAAI,GAAG,EAC1C,KAAK,IAAI,EAENU,EAAM,KAAKT,EAAO,UAAUA,EAAO,gBAAgBK,EAAOL,EAAO,UAAU,OAAOK,EAAOL,EAAO,WAAW,OAAOK,EAAOL,EAAO,cAAc,OAAOK,EAAOL,EAAO,gBAAgB,QAAQG,EAAIH,EAAO,SAAS,OAAOM,MAEvNxC,EAAWoC,CAAI,EAQlBrC,EAAeqC,EAAMO,EAAM;AAAA,CAAI,EAF/B7C,EAAcsC,EAJZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAI2BO,EAAM;AAAA,CAAI,CAI3C,CAKA,QAAQC,EAAe,EAWrB,CACA,IAAIrB,EAAa,EACbC,EAAa,EACbC,EAAc,EACdC,EAAiB,EACjBC,EAAmB,EACnBkB,EAAc,EACdjB,EAAY,EACVkB,EAA6M,CAAC,EAEpN,QAASC,EAAI,EAAGA,EAAIH,EAAMG,IAAK,CAC7B,IAAM3B,EAAO,IAAI,KAAK,KAAK,IAAI,EAAI2B,EAAI,KAAS,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EACrEtC,EAAQ,KAAK,QAAQW,CAAI,EAC/B,GAAKX,EAEL,OAAW,CAACuB,EAAIb,CAAK,IAAK,OAAO,QAAQV,EAAM,MAAM,EAAG,CACtD,IAAMwB,EAAO1B,EAAa,cAAcY,CAAK,EAC7CI,GAAcJ,EAAM,MACpBK,GAAcL,EAAM,aAAe,EACnCM,GAAeN,EAAM,cAAgB,EACrCO,GAAkBP,EAAM,iBAAmB,EAC3CQ,GAAoBR,EAAM,mBAAqB,EAC/C0B,GAAe1B,EAAM,OACrBS,GAAaK,EAEb,IAAMe,EAAWF,EAAQd,CAAE,GAAK,CAAE,MAAO,EAAG,MAAO,EAAG,OAAQ,EAAG,UAAW,EAAG,YAAa,EAAG,MAAO,EAAG,YAAa,EAAG,cAAe,EAAG,KAAM,CAAE,EACnJgB,EAAS,OAAS7B,EAAM,MACxB6B,EAAS,OAAS7B,EAAM,aAAe,EACvC6B,EAAS,QAAU7B,EAAM,cAAgB,EACzC6B,EAAS,WAAa7B,EAAM,iBAAmB,EAC/C6B,EAAS,aAAe7B,EAAM,mBAAqB,EACnD6B,EAAS,MAAQA,EAAS,MAAQA,EAAS,OAASA,EAAS,UAAYA,EAAS,YAClFA,EAAS,eAAiB7B,EAAM,cAChC6B,EAAS,YAAcA,EAAS,cAAgBA,EAAS,MACzDA,EAAS,MAAQf,EACbd,EAAM,QAAO6B,EAAS,MAAQ7B,EAAM,OACxC2B,EAAQd,CAAE,EAAIgB,GAIlB,IAAMC,EAAczB,EAAaC,EAAcC,EAAiBC,EAC1DuB,EAAiBxB,EAAiBC,EAAoB,EACxDD,GAAkBA,EAAiBC,GACnC,EAEJ,MAAO,CAAE,WAAAJ,EAAY,YAAA0B,EAAa,WAAAzB,EAAY,YAAAC,EAAa,eAAAC,EAAgB,iBAAAC,EAAkB,cAAAuB,EAAe,YAAAL,EAAa,UAAAjB,EAAW,QAAAkB,CAAQ,CAC9I,CAEQ,SAAS1B,EAAsB,CACrC,OAAOlB,EAAQ,KAAK,IAAK,GAAGkB,QAAW,CACzC,CAEQ,KAAKX,EAAyB,CACpC,GAAI,CACFX,EAAc,KAAK,SAASW,EAAM,IAAI,EAAG,KAAK,UAAUA,EAAO,KAAM,CAAC,CAAC,CACzE,MAAE,CAAO,CACX,CACF","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-
|
|
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-
|
|
32
|
+
//# sourceMappingURL=chunk-6MKAXEE5.js.map
|