@nicofains1/agentwatch 0.1.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 +127 -0
- package/dist/alerts.d.ts +11 -0
- package/dist/alerts.d.ts.map +1 -0
- package/dist/alerts.js +67 -0
- package/dist/alerts.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +121 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/dashboard.d.ts +15 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +40 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +49 -0
- package/dist/db.js.map +1 -0
- package/dist/heartbeat.d.ts +6 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +66 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/trace.d.ts +24 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +99 -0
- package/dist/trace.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @nicofains1/agentwatch
|
|
2
|
+
|
|
3
|
+
Observability for multi-agent systems. Track heartbeats, trace cross-agent actions, detect cascade failures, and replay what went wrong.
|
|
4
|
+
|
|
5
|
+
Built for teams running fleets of AI agents (CrewAI, AutoGen, LangGraph, custom) who need to understand why Agent B failed after Agent A timed out.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @nicofains1/agentwatch
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { AgentWatch } from '@nicofains1/agentwatch';
|
|
17
|
+
|
|
18
|
+
const aw = new AgentWatch(); // uses agentwatch.db by default
|
|
19
|
+
|
|
20
|
+
// Register heartbeats from your agents
|
|
21
|
+
aw.report('agent-a', 'healthy');
|
|
22
|
+
aw.report('agent-b', 'healthy');
|
|
23
|
+
|
|
24
|
+
// Trace cross-agent actions
|
|
25
|
+
const traceId = aw.createTraceId();
|
|
26
|
+
|
|
27
|
+
const e1 = aw.trace(traceId, 'agent-a', 'fetch-data', 'url=https://api.example.com', 'rows=150');
|
|
28
|
+
const e2 = aw.trace(traceId, 'agent-b', 'process', JSON.stringify({ rows: 150 }), '', {
|
|
29
|
+
parentEventId: e1.id,
|
|
30
|
+
status: 'error',
|
|
31
|
+
durationMs: 4200,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Find the root cause
|
|
35
|
+
const chain = aw.correlate(e2.id);
|
|
36
|
+
console.log(chain?.root_cause); // -> agent-a / fetch-data
|
|
37
|
+
|
|
38
|
+
// Fleet dashboard
|
|
39
|
+
console.log(aw.dashboardText());
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
**Heartbeat registration** - Track agent health status over time. Detect stale or offline agents based on configurable thresholds.
|
|
45
|
+
|
|
46
|
+
**Cross-agent tracing** - Link actions across agents with trace IDs and parent event references. When agent-c fails because agent-b sent bad data that it got from agent-a, the trace shows the full chain.
|
|
47
|
+
|
|
48
|
+
**Cascade failure detection** - Walk backward from any failure to find the root cause across your agent fleet. `correlate(failureEventId)` returns the full chain from root cause to final failure.
|
|
49
|
+
|
|
50
|
+
**Alert de-duplication** - Same alert type from the same agent within a time window gets collapsed into one alert with an incrementing count. Severity auto-escalates: info (1x) -> warning (3x) -> critical (10x).
|
|
51
|
+
|
|
52
|
+
**Fleet dashboard** - One-line summary of your entire fleet: which agents are healthy, degraded, erroring, or offline. Uptime percentages and active alert counts per agent.
|
|
53
|
+
|
|
54
|
+
**Forensic replay** - Given a trace ID, replay all cascade chains to understand the full failure sequence.
|
|
55
|
+
|
|
56
|
+
## CLI
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx agentwatch dashboard # Fleet health overview
|
|
60
|
+
npx agentwatch cascade <event-id> # Trace cascade from a failure
|
|
61
|
+
npx agentwatch failures [agent] # List recent failures
|
|
62
|
+
npx agentwatch alerts [agent] # List active alerts
|
|
63
|
+
npx agentwatch replay <trace-id> # Replay all cascades in a trace
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Set `AGENTWATCH_DB` to point to your database file (default: `agentwatch.db`).
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `new AgentWatch(config?)`
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const aw = new AgentWatch({
|
|
74
|
+
db_path: 'agentwatch.db', // SQLite file path (default: agentwatch.db)
|
|
75
|
+
alert_window_minutes: 30, // De-dup window for alerts (default: 30)
|
|
76
|
+
heartbeat_stale_minutes: 30, // When to mark agents as offline (default: 30)
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Heartbeats
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
aw.report(agent: string, status: 'healthy' | 'degraded' | 'error' | 'offline', context?: string)
|
|
84
|
+
aw.getLatestHeartbeat(agent: string): Heartbeat | undefined
|
|
85
|
+
aw.getFleetHealth(): AgentHealth[]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Tracing
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
aw.createTraceId(): string
|
|
92
|
+
aw.trace(traceId, agent, action, input, output, opts?): TraceEvent
|
|
93
|
+
aw.getTraceEvents(traceId: string): TraceEvent[]
|
|
94
|
+
aw.getRecentFailures(agent?: string, limit?: number): TraceEvent[]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Cascade Detection
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
aw.correlate(failureEventId: number): CascadeChain | null
|
|
101
|
+
aw.replay(traceId: string): CascadeChain[]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Alerts
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
aw.alert(agent, alertType, message): Alert
|
|
108
|
+
aw.resolveAlert(alertId: number): void
|
|
109
|
+
aw.activeAlerts(agent?: string): Alert[]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Dashboard
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
aw.dashboard(): DashboardOutput
|
|
116
|
+
aw.dashboardText(): string
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Storage
|
|
120
|
+
|
|
121
|
+
Uses SQLite via `better-sqlite3`. The database file is created automatically on first use. WAL mode is enabled for concurrent reads.
|
|
122
|
+
|
|
123
|
+
Tables: `heartbeats`, `trace_events`, `alerts` - all with proper indexes for fast lookups.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/alerts.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { Alert } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Record an alert with de-duplication.
|
|
5
|
+
* If the same (agent, alert_type) exists within the window, increment count.
|
|
6
|
+
* Escalate severity based on count: 1 = info, 3+ = warning, 10+ = critical.
|
|
7
|
+
*/
|
|
8
|
+
export declare function recordAlert(db: Database.Database, agent: string, alertType: string, message: string, windowMinutes?: number): Alert;
|
|
9
|
+
export declare function resolveAlert(db: Database.Database, alertId: number): void;
|
|
10
|
+
export declare function getActiveAlerts(db: Database.Database, agent?: string): Alert[];
|
|
11
|
+
//# sourceMappingURL=alerts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.d.ts","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,YAAY,CAAC;AAOvD;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,aAAa,GAAE,MAAW,GACzB,KAAK,CAuCP;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,OAAO,EAAE,MAAM,GACd,IAAI,CAEN;AAED,wBAAgB,eAAe,CAC7B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,KAAK,EAAE,CAWT"}
|
package/dist/alerts.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
function sqliteNow(offsetMs = 0) {
|
|
2
|
+
const d = new Date(Date.now() + offsetMs);
|
|
3
|
+
return d.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, '');
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Record an alert with de-duplication.
|
|
7
|
+
* If the same (agent, alert_type) exists within the window, increment count.
|
|
8
|
+
* Escalate severity based on count: 1 = info, 3+ = warning, 10+ = critical.
|
|
9
|
+
*/
|
|
10
|
+
export function recordAlert(db, agent, alertType, message, windowMinutes = 30) {
|
|
11
|
+
const windowStart = sqliteNow(-windowMinutes * 60 * 1000);
|
|
12
|
+
const existing = db.prepare(`
|
|
13
|
+
SELECT * FROM alerts
|
|
14
|
+
WHERE agent = ? AND alert_type = ? AND resolved = 0 AND last_seen > ?
|
|
15
|
+
ORDER BY last_seen DESC
|
|
16
|
+
LIMIT 1
|
|
17
|
+
`).get(agent, alertType, windowStart);
|
|
18
|
+
if (existing) {
|
|
19
|
+
const newCount = existing.count + 1;
|
|
20
|
+
const severity = escalateSeverity(newCount);
|
|
21
|
+
db.prepare(`
|
|
22
|
+
UPDATE alerts
|
|
23
|
+
SET count = ?, severity = ?, message = ?, last_seen = datetime('now')
|
|
24
|
+
WHERE id = ?
|
|
25
|
+
`).run(newCount, severity, message, existing.id);
|
|
26
|
+
return { ...existing, count: newCount, severity, message, last_seen: new Date().toISOString() };
|
|
27
|
+
}
|
|
28
|
+
const stmt = db.prepare(`
|
|
29
|
+
INSERT INTO alerts (agent, alert_type, severity, message)
|
|
30
|
+
VALUES (?, ?, 'info', ?)
|
|
31
|
+
`);
|
|
32
|
+
const result = stmt.run(agent, alertType, message);
|
|
33
|
+
return {
|
|
34
|
+
id: Number(result.lastInsertRowid),
|
|
35
|
+
agent,
|
|
36
|
+
alert_type: alertType,
|
|
37
|
+
severity: 'info',
|
|
38
|
+
message,
|
|
39
|
+
count: 1,
|
|
40
|
+
first_seen: new Date().toISOString(),
|
|
41
|
+
last_seen: new Date().toISOString(),
|
|
42
|
+
resolved: false,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function resolveAlert(db, alertId) {
|
|
46
|
+
db.prepare(`UPDATE alerts SET resolved = 1 WHERE id = ?`).run(alertId);
|
|
47
|
+
}
|
|
48
|
+
export function getActiveAlerts(db, agent) {
|
|
49
|
+
if (agent) {
|
|
50
|
+
return db.prepare(`
|
|
51
|
+
SELECT * FROM alerts WHERE agent = ? AND resolved = 0
|
|
52
|
+
ORDER BY severity DESC, last_seen DESC
|
|
53
|
+
`).all(agent);
|
|
54
|
+
}
|
|
55
|
+
return db.prepare(`
|
|
56
|
+
SELECT * FROM alerts WHERE resolved = 0
|
|
57
|
+
ORDER BY severity DESC, last_seen DESC
|
|
58
|
+
`).all();
|
|
59
|
+
}
|
|
60
|
+
function escalateSeverity(count) {
|
|
61
|
+
if (count >= 10)
|
|
62
|
+
return 'critical';
|
|
63
|
+
if (count >= 3)
|
|
64
|
+
return 'warning';
|
|
65
|
+
return 'info';
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=alerts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerts.js","sourceRoot":"","sources":["../src/alerts.ts"],"names":[],"mappings":"AAGA,SAAS,SAAS,CAAC,QAAQ,GAAG,CAAC;IAC7B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC;IAC1C,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACpE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,EAAqB,EACrB,KAAa,EACb,SAAiB,EACjB,OAAe,EACf,gBAAwB,EAAE;IAE1B,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CAAC;;;;;GAK3B,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAsB,CAAC;IAE3D,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC5C,EAAE,CAAC,OAAO,CAAC;;;;KAIV,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAEjD,OAAO,EAAE,GAAG,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IAClG,CAAC;IAED,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC;;;GAGvB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAEnD,OAAO;QACL,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC;QAClC,KAAK;QACL,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,MAAM;QAChB,OAAO;QACP,KAAK,EAAE,CAAC;QACR,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,QAAQ,EAAE,KAAK;KAChB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,EAAqB,EACrB,OAAe;IAEf,EAAE,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,EAAqB,EACrB,KAAc;IAEd,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,CAAC,OAAO,CAAC;;;KAGjB,CAAC,CAAC,GAAG,CAAC,KAAK,CAAY,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,CAAC,OAAO,CAAC;;;GAGjB,CAAC,CAAC,GAAG,EAAa,CAAC;AACtB,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,IAAI,KAAK,IAAI,EAAE;QAAE,OAAO,UAAU,CAAC;IACnC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { AgentWatch } from '../index.js';
|
|
3
|
+
const args = process.argv.slice(2);
|
|
4
|
+
const command = args[0];
|
|
5
|
+
const dbPath = process.env.AGENTWATCH_DB ?? 'agentwatch.db';
|
|
6
|
+
const aw = new AgentWatch({ db_path: dbPath });
|
|
7
|
+
try {
|
|
8
|
+
switch (command) {
|
|
9
|
+
case 'dashboard': {
|
|
10
|
+
console.log(aw.dashboardText());
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
case 'cascade': {
|
|
14
|
+
const failureId = parseInt(args[1], 10);
|
|
15
|
+
if (isNaN(failureId)) {
|
|
16
|
+
console.error('Usage: agentwatch cascade <failure-event-id>');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const chain = aw.correlate(failureId);
|
|
20
|
+
if (!chain) {
|
|
21
|
+
console.error(`No trace event found with id ${failureId}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
console.log(`Cascade for failure #${failureId}`);
|
|
25
|
+
console.log('='.repeat(60));
|
|
26
|
+
console.log(`Root cause: ${chain.root_cause.agent} / ${chain.root_cause.action}`);
|
|
27
|
+
console.log(`Chain length: ${chain.chain.length} steps`);
|
|
28
|
+
console.log('');
|
|
29
|
+
for (let i = 0; i < chain.chain.length; i++) {
|
|
30
|
+
const step = chain.chain[i];
|
|
31
|
+
const prefix = i === 0 ? 'ROOT' : i === chain.chain.length - 1 ? 'FAIL' : ` ${i} `;
|
|
32
|
+
console.log(`[${prefix}] ${step.agent} / ${step.action} (${step.status}, ${step.duration_ms}ms)`);
|
|
33
|
+
console.log(` Input: ${truncate(step.input, 120)}`);
|
|
34
|
+
console.log(` Output: ${truncate(step.output, 120)}`);
|
|
35
|
+
console.log(` Time: ${step.timestamp}`);
|
|
36
|
+
if (i < chain.chain.length - 1)
|
|
37
|
+
console.log(' |');
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case 'failures': {
|
|
42
|
+
const agent = args[1];
|
|
43
|
+
const limit = parseInt(args[2] ?? '20', 10);
|
|
44
|
+
const failures = aw.getRecentFailures(agent, limit);
|
|
45
|
+
if (failures.length === 0) {
|
|
46
|
+
console.log('No recent failures.');
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
console.log(`Recent failures${agent ? ` for ${agent}` : ''} (${failures.length})`);
|
|
50
|
+
console.log('='.repeat(60));
|
|
51
|
+
for (const f of failures) {
|
|
52
|
+
console.log(`#${f.id} | ${f.agent} / ${f.action} | trace=${f.trace_id.slice(0, 8)} | ${f.created_at}`);
|
|
53
|
+
console.log(` Output: ${truncate(f.output, 100)}`);
|
|
54
|
+
}
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case 'alerts': {
|
|
58
|
+
const alertAgent = args[1];
|
|
59
|
+
const alerts = aw.activeAlerts(alertAgent);
|
|
60
|
+
if (alerts.length === 0) {
|
|
61
|
+
console.log('No active alerts.');
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
console.log(`Active alerts (${alerts.length})`);
|
|
65
|
+
console.log('='.repeat(60));
|
|
66
|
+
for (const a of alerts) {
|
|
67
|
+
console.log(`[${a.severity.toUpperCase()}] ${a.agent} / ${a.alert_type} (x${a.count})`);
|
|
68
|
+
console.log(` ${a.message}`);
|
|
69
|
+
console.log(` First: ${a.first_seen} | Last: ${a.last_seen}`);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case 'replay': {
|
|
74
|
+
const traceId = args[1];
|
|
75
|
+
if (!traceId) {
|
|
76
|
+
console.error('Usage: agentwatch replay <trace-id>');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const chains = aw.replay(traceId);
|
|
80
|
+
if (chains.length === 0) {
|
|
81
|
+
console.log(`No failures found in trace ${traceId}`);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
console.log(`Trace ${traceId} - ${chains.length} cascade(s) found`);
|
|
85
|
+
console.log('='.repeat(60));
|
|
86
|
+
for (const chain of chains) {
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log(`Cascade -> failure #${chain.failure_id} (${chain.chain.length} steps)`);
|
|
89
|
+
for (let i = 0; i < chain.chain.length; i++) {
|
|
90
|
+
const step = chain.chain[i];
|
|
91
|
+
const arrow = i < chain.chain.length - 1 ? ' ->' : ' X';
|
|
92
|
+
console.log(` ${step.agent}/${step.action} [${step.status}]${arrow}`);
|
|
93
|
+
console.log(` in: ${truncate(step.input, 80)}`);
|
|
94
|
+
console.log(` out: ${truncate(step.output, 80)}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
default: {
|
|
100
|
+
console.log('AgentWatch - Multi-agent observability');
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log('Usage:');
|
|
103
|
+
console.log(' agentwatch dashboard Fleet health overview');
|
|
104
|
+
console.log(' agentwatch cascade <event-id> Trace cascade from failure');
|
|
105
|
+
console.log(' agentwatch failures [agent] List recent failures');
|
|
106
|
+
console.log(' agentwatch alerts [agent] List active alerts');
|
|
107
|
+
console.log(' agentwatch replay <trace-id> Replay all cascades in a trace');
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log('Environment:');
|
|
110
|
+
console.log(' AGENTWATCH_DB Path to SQLite database (default: agentwatch.db)');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
aw.close();
|
|
116
|
+
}
|
|
117
|
+
function truncate(s, max) {
|
|
118
|
+
const oneline = s.replace(/\n/g, ' ');
|
|
119
|
+
return oneline.length > max ? oneline.slice(0, max - 3) + '...' : oneline;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAExB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,eAAe,CAAC;AAC5D,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;AAE/C,IAAI,CAAC;IACH,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;YAChC,MAAM;QACR,CAAC;QAED,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACtC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,gCAAgC,SAAS,EAAE,CAAC,CAAC;gBAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,wBAAwB,SAAS,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,UAAU,CAAC,KAAK,MAAM,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YAClF,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAEhB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBAC5B,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC;gBACpF,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,KAAK,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,WAAW,KAAK,CAAC,CAAC;gBAClG,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC3D,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBAChD,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC1D,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5C,MAAM,QAAQ,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAEpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;gBACnC,MAAM;YACR,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,CAAC,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YACnF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;gBACvG,OAAO,CAAC,GAAG,CAAC,aAAa,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;YAE3C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;gBACjC,MAAM;YACR,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,UAAU,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;gBACxF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC9B,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,UAAU,YAAY,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;YACjE,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;gBACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,CAAC,GAAG,CAAC,8BAA8B,OAAO,EAAE,CAAC,CAAC;gBACrD,MAAM;YACR,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,MAAM,MAAM,CAAC,MAAM,mBAAmB,CAAC,CAAC;YACpE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;YAE5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,CAAC,UAAU,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,SAAS,CAAC,CAAC;gBACrF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC5C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;oBAC5B,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;oBACxD,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC,CAAC;oBACvE,OAAO,CAAC,GAAG,CAAC,YAAY,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;oBACpD,OAAO,CAAC,GAAG,CAAC,YAAY,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACtD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;YAC9E,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;YACxE,OAAO,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;YACvE,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;YAClF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;QACrF,CAAC;IACH,CAAC;AACH,CAAC;QAAS,CAAC;IACT,EAAE,CAAC,KAAK,EAAE,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,CAAS,EAAE,GAAW;IACtC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { AgentHealth } from './types.js';
|
|
3
|
+
export interface DashboardOutput {
|
|
4
|
+
timestamp: string;
|
|
5
|
+
agents: AgentHealth[];
|
|
6
|
+
total_agents: number;
|
|
7
|
+
healthy_count: number;
|
|
8
|
+
degraded_count: number;
|
|
9
|
+
error_count: number;
|
|
10
|
+
offline_count: number;
|
|
11
|
+
total_active_alerts: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function getDashboard(db: Database.Database, staleMinutes?: number): DashboardOutput;
|
|
14
|
+
export declare function formatDashboard(dashboard: DashboardOutput): string;
|
|
15
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,GAAE,MAAW,GACxB,eAAe,CAcjB;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG,MAAM,CA0BlE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getFleetHealth } from './heartbeat.js';
|
|
2
|
+
import { getActiveAlerts } from './alerts.js';
|
|
3
|
+
export function getDashboard(db, staleMinutes = 30) {
|
|
4
|
+
const agents = getFleetHealth(db, staleMinutes);
|
|
5
|
+
const alerts = getActiveAlerts(db);
|
|
6
|
+
return {
|
|
7
|
+
timestamp: new Date().toISOString(),
|
|
8
|
+
agents,
|
|
9
|
+
total_agents: agents.length,
|
|
10
|
+
healthy_count: agents.filter(a => a.status === 'healthy').length,
|
|
11
|
+
degraded_count: agents.filter(a => a.status === 'degraded').length,
|
|
12
|
+
error_count: agents.filter(a => a.status === 'error').length,
|
|
13
|
+
offline_count: agents.filter(a => a.status === 'offline').length,
|
|
14
|
+
total_active_alerts: alerts.length,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function formatDashboard(dashboard) {
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push(`AgentWatch Fleet Dashboard - ${dashboard.timestamp}`);
|
|
20
|
+
lines.push('='.repeat(60));
|
|
21
|
+
lines.push('');
|
|
22
|
+
lines.push(`Agents: ${dashboard.total_agents} total | ${dashboard.healthy_count} healthy | ${dashboard.degraded_count} degraded | ${dashboard.error_count} error | ${dashboard.offline_count} offline`);
|
|
23
|
+
lines.push(`Active Alerts: ${dashboard.total_active_alerts}`);
|
|
24
|
+
lines.push('');
|
|
25
|
+
lines.push('Agent'.padEnd(15) + 'Status'.padEnd(12) + 'Uptime'.padEnd(10) + 'Alerts'.padEnd(10) + 'Last Heartbeat');
|
|
26
|
+
lines.push('-'.repeat(60));
|
|
27
|
+
for (const agent of dashboard.agents) {
|
|
28
|
+
const statusIcon = agent.status === 'healthy' ? 'OK'
|
|
29
|
+
: agent.status === 'degraded' ? 'WARN'
|
|
30
|
+
: agent.status === 'error' ? 'ERR'
|
|
31
|
+
: 'OFF';
|
|
32
|
+
lines.push(agent.agent.padEnd(15) +
|
|
33
|
+
statusIcon.padEnd(12) +
|
|
34
|
+
`${agent.uptime_pct}%`.padEnd(10) +
|
|
35
|
+
String(agent.active_alerts).padEnd(10) +
|
|
36
|
+
(agent.last_heartbeat || 'never'));
|
|
37
|
+
}
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
|
package/dist/db.d.ts
ADDED
package/dist/db.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AA2CtC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAMxD"}
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
const SCHEMA = `
|
|
3
|
+
CREATE TABLE IF NOT EXISTS heartbeats (
|
|
4
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
5
|
+
agent TEXT NOT NULL,
|
|
6
|
+
status TEXT NOT NULL CHECK(status IN ('healthy', 'degraded', 'error', 'offline')),
|
|
7
|
+
context TEXT NOT NULL DEFAULT '{}',
|
|
8
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
CREATE TABLE IF NOT EXISTS trace_events (
|
|
12
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
13
|
+
trace_id TEXT NOT NULL,
|
|
14
|
+
agent TEXT NOT NULL,
|
|
15
|
+
action TEXT NOT NULL,
|
|
16
|
+
input TEXT NOT NULL DEFAULT '',
|
|
17
|
+
output TEXT NOT NULL DEFAULT '',
|
|
18
|
+
parent_event_id INTEGER REFERENCES trace_events(id),
|
|
19
|
+
status TEXT NOT NULL DEFAULT 'ok' CHECK(status IN ('ok', 'error')),
|
|
20
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS alerts (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
agent TEXT NOT NULL,
|
|
27
|
+
alert_type TEXT NOT NULL,
|
|
28
|
+
severity TEXT NOT NULL DEFAULT 'info' CHECK(severity IN ('info', 'warning', 'critical')),
|
|
29
|
+
message TEXT NOT NULL,
|
|
30
|
+
count INTEGER NOT NULL DEFAULT 1,
|
|
31
|
+
first_seen TEXT NOT NULL DEFAULT (datetime('now')),
|
|
32
|
+
last_seen TEXT NOT NULL DEFAULT (datetime('now')),
|
|
33
|
+
resolved INTEGER NOT NULL DEFAULT 0
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_heartbeats_agent ON heartbeats(agent, created_at);
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_trace ON trace_events(trace_id);
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_agent ON trace_events(agent, created_at);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_trace_events_parent ON trace_events(parent_event_id);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_alerts_agent ON alerts(agent, alert_type, resolved);
|
|
41
|
+
`;
|
|
42
|
+
export function createDb(path) {
|
|
43
|
+
const db = new Database(path);
|
|
44
|
+
db.pragma('journal_mode = WAL');
|
|
45
|
+
db.pragma('foreign_keys = ON');
|
|
46
|
+
db.exec(SCHEMA);
|
|
47
|
+
return db;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCd,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC9B,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC/B,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { AgentStatus, Heartbeat, AgentHealth } from './types.js';
|
|
3
|
+
export declare function report(db: Database.Database, agent: string, status: AgentStatus, context?: string): Heartbeat;
|
|
4
|
+
export declare function getLatestHeartbeat(db: Database.Database, agent: string): Heartbeat | undefined;
|
|
5
|
+
export declare function getFleetHealth(db: Database.Database, staleMinutes?: number): AgentHealth[];
|
|
6
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAQtE,wBAAgB,MAAM,CACpB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,MAAa,GACrB,SAAS,CAaX;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,EAAE,MAAM,GACZ,SAAS,GAAG,SAAS,CAOvB;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,YAAY,GAAE,MAAW,GACxB,WAAW,EAAE,CA4Cf"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Format a date as SQLite-compatible `YYYY-MM-DD HH:MM:SS` */
|
|
2
|
+
function sqliteNow(offsetMs = 0) {
|
|
3
|
+
const d = new Date(Date.now() + offsetMs);
|
|
4
|
+
return d.toISOString().replace('T', ' ').replace(/\.\d{3}Z$/, '');
|
|
5
|
+
}
|
|
6
|
+
export function report(db, agent, status, context = '{}') {
|
|
7
|
+
const stmt = db.prepare(`
|
|
8
|
+
INSERT INTO heartbeats (agent, status, context)
|
|
9
|
+
VALUES (?, ?, ?)
|
|
10
|
+
`);
|
|
11
|
+
const result = stmt.run(agent, status, context);
|
|
12
|
+
return {
|
|
13
|
+
id: Number(result.lastInsertRowid),
|
|
14
|
+
agent,
|
|
15
|
+
status,
|
|
16
|
+
context,
|
|
17
|
+
created_at: new Date().toISOString(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function getLatestHeartbeat(db, agent) {
|
|
21
|
+
return db.prepare(`
|
|
22
|
+
SELECT * FROM heartbeats
|
|
23
|
+
WHERE agent = ?
|
|
24
|
+
ORDER BY created_at DESC
|
|
25
|
+
LIMIT 1
|
|
26
|
+
`).get(agent);
|
|
27
|
+
}
|
|
28
|
+
export function getFleetHealth(db, staleMinutes = 30) {
|
|
29
|
+
const agents = db.prepare(`
|
|
30
|
+
SELECT DISTINCT agent FROM heartbeats
|
|
31
|
+
`).all();
|
|
32
|
+
return agents.map(({ agent }) => {
|
|
33
|
+
const latest = getLatestHeartbeat(db, agent);
|
|
34
|
+
if (!latest) {
|
|
35
|
+
return { agent, status: 'offline', last_heartbeat: '', uptime_pct: 0, active_alerts: 0 };
|
|
36
|
+
}
|
|
37
|
+
const staleThreshold = sqliteNow(-staleMinutes * 60 * 1000);
|
|
38
|
+
const isStale = latest.created_at < staleThreshold;
|
|
39
|
+
const status = isStale ? 'offline' : latest.status;
|
|
40
|
+
// Uptime: % of heartbeats in last 24h that were 'healthy'
|
|
41
|
+
const dayAgo = sqliteNow(-24 * 60 * 60 * 1000);
|
|
42
|
+
const totalCount = db.prepare(`
|
|
43
|
+
SELECT COUNT(*) as cnt FROM heartbeats
|
|
44
|
+
WHERE agent = ? AND created_at > ?
|
|
45
|
+
`).get(agent, dayAgo);
|
|
46
|
+
const healthyCount = db.prepare(`
|
|
47
|
+
SELECT COUNT(*) as cnt FROM heartbeats
|
|
48
|
+
WHERE agent = ? AND created_at > ? AND status = 'healthy'
|
|
49
|
+
`).get(agent, dayAgo);
|
|
50
|
+
const uptime = totalCount.cnt > 0
|
|
51
|
+
? Math.round((healthyCount.cnt / totalCount.cnt) * 100)
|
|
52
|
+
: 0;
|
|
53
|
+
const alertCount = db.prepare(`
|
|
54
|
+
SELECT COUNT(*) as cnt FROM alerts
|
|
55
|
+
WHERE agent = ? AND resolved = 0
|
|
56
|
+
`).get(agent);
|
|
57
|
+
return {
|
|
58
|
+
agent,
|
|
59
|
+
status,
|
|
60
|
+
last_heartbeat: latest.created_at,
|
|
61
|
+
uptime_pct: uptime,
|
|
62
|
+
active_alerts: alertCount.cnt,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AgentWatchConfig, AgentStatus, TraceEvent, CascadeChain } from './types.js';
|
|
2
|
+
export declare class AgentWatch {
|
|
3
|
+
private db;
|
|
4
|
+
private alertWindowMinutes;
|
|
5
|
+
private heartbeatStaleMinutes;
|
|
6
|
+
constructor(config?: AgentWatchConfig);
|
|
7
|
+
report(agent: string, status: AgentStatus, context?: string): import("./types.js").Heartbeat;
|
|
8
|
+
trace(traceId: string, agent: string, action: string, input: string, output: string, opts?: {
|
|
9
|
+
parentEventId?: number;
|
|
10
|
+
status?: 'ok' | 'error';
|
|
11
|
+
durationMs?: number;
|
|
12
|
+
}): TraceEvent;
|
|
13
|
+
correlate(failureEventId: number): CascadeChain | null;
|
|
14
|
+
dashboard(): import("./dashboard.js").DashboardOutput;
|
|
15
|
+
dashboardText(): string;
|
|
16
|
+
alert(agent: string, alertType: string, message: string): import("./types.js").Alert;
|
|
17
|
+
resolveAlert(alertId: number): void;
|
|
18
|
+
activeAlerts(agent?: string): import("./types.js").Alert[];
|
|
19
|
+
replay(traceId: string): CascadeChain[];
|
|
20
|
+
createTraceId(): string;
|
|
21
|
+
getTraceEvents(traceId: string): TraceEvent[];
|
|
22
|
+
getRecentFailures(agent?: string, limit?: number): TraceEvent[];
|
|
23
|
+
getLatestHeartbeat(agent: string): import("./types.js").Heartbeat | undefined;
|
|
24
|
+
getFleetHealth(): import("./types.js").AgentHealth[];
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
export type { AgentStatus, AlertSeverity, Heartbeat, TraceEvent, Alert, AgentHealth, CascadeStep, CascadeChain, AgentWatchConfig, } from './types.js';
|
|
28
|
+
export { createTraceId } from './trace.js';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1F,qBAAa,UAAU;IACrB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,qBAAqB,CAAS;gBAE1B,MAAM,GAAE,gBAAqB;IAQzC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,MAAa;IAKjE,KAAK,CACH,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9E,UAAU;IAWb,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAKtD,SAAS;IAIT,aAAa,IAAI,MAAM;IAKvB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAIvD,YAAY,CAAC,OAAO,EAAE,MAAM;IAI5B,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM;IAK3B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE;IAKvC,aAAa,IAAI,MAAM;IAIvB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE;IAI7C,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAI/D,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAIhC,cAAc;IAId,KAAK;CAGN;AAGD,YAAY,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,UAAU,EACV,KAAK,EACL,WAAW,EACX,WAAW,EACX,YAAY,EACZ,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createDb } from './db.js';
|
|
2
|
+
import { report, getLatestHeartbeat, getFleetHealth } from './heartbeat.js';
|
|
3
|
+
import { createTraceId, trace, correlate, getRecentFailures, getTraceEvents, replayTrace } from './trace.js';
|
|
4
|
+
import { recordAlert, resolveAlert, getActiveAlerts } from './alerts.js';
|
|
5
|
+
import { getDashboard, formatDashboard } from './dashboard.js';
|
|
6
|
+
export class AgentWatch {
|
|
7
|
+
db;
|
|
8
|
+
alertWindowMinutes;
|
|
9
|
+
heartbeatStaleMinutes;
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
const dbPath = config.db_path ?? 'agentwatch.db';
|
|
12
|
+
this.db = createDb(dbPath);
|
|
13
|
+
this.alertWindowMinutes = config.alert_window_minutes ?? 30;
|
|
14
|
+
this.heartbeatStaleMinutes = config.heartbeat_stale_minutes ?? 30;
|
|
15
|
+
}
|
|
16
|
+
// Feature 1: Heartbeat registration
|
|
17
|
+
report(agent, status, context = '{}') {
|
|
18
|
+
return report(this.db, agent, status, context);
|
|
19
|
+
}
|
|
20
|
+
// Feature 2: Cross-agent event correlation
|
|
21
|
+
trace(traceId, agent, action, input, output, opts) {
|
|
22
|
+
const event = trace(this.db, traceId, agent, action, input, output, opts);
|
|
23
|
+
// Auto-alert on errors
|
|
24
|
+
if (opts?.status === 'error') {
|
|
25
|
+
recordAlert(this.db, agent, `trace_error:${action}`, output, this.alertWindowMinutes);
|
|
26
|
+
}
|
|
27
|
+
return event;
|
|
28
|
+
}
|
|
29
|
+
correlate(failureEventId) {
|
|
30
|
+
return correlate(this.db, failureEventId);
|
|
31
|
+
}
|
|
32
|
+
// Feature 3: Fleet health dashboard
|
|
33
|
+
dashboard() {
|
|
34
|
+
return getDashboard(this.db, this.heartbeatStaleMinutes);
|
|
35
|
+
}
|
|
36
|
+
dashboardText() {
|
|
37
|
+
return formatDashboard(this.dashboard());
|
|
38
|
+
}
|
|
39
|
+
// Feature 4: Alert de-duplication
|
|
40
|
+
alert(agent, alertType, message) {
|
|
41
|
+
return recordAlert(this.db, agent, alertType, message, this.alertWindowMinutes);
|
|
42
|
+
}
|
|
43
|
+
resolveAlert(alertId) {
|
|
44
|
+
return resolveAlert(this.db, alertId);
|
|
45
|
+
}
|
|
46
|
+
activeAlerts(agent) {
|
|
47
|
+
return getActiveAlerts(this.db, agent);
|
|
48
|
+
}
|
|
49
|
+
// Feature 5: Cascade replay
|
|
50
|
+
replay(traceId) {
|
|
51
|
+
return replayTrace(this.db, traceId);
|
|
52
|
+
}
|
|
53
|
+
// Utilities
|
|
54
|
+
createTraceId() {
|
|
55
|
+
return createTraceId();
|
|
56
|
+
}
|
|
57
|
+
getTraceEvents(traceId) {
|
|
58
|
+
return getTraceEvents(this.db, traceId);
|
|
59
|
+
}
|
|
60
|
+
getRecentFailures(agent, limit) {
|
|
61
|
+
return getRecentFailures(this.db, agent, limit);
|
|
62
|
+
}
|
|
63
|
+
getLatestHeartbeat(agent) {
|
|
64
|
+
return getLatestHeartbeat(this.db, agent);
|
|
65
|
+
}
|
|
66
|
+
getFleetHealth() {
|
|
67
|
+
return getFleetHealth(this.db, this.heartbeatStaleMinutes);
|
|
68
|
+
}
|
|
69
|
+
close() {
|
|
70
|
+
this.db.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Re-export for direct use
|
|
74
|
+
export { createTraceId } from './trace.js';
|
|
75
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { TraceEvent, CascadeChain } from './types.js';
|
|
3
|
+
export declare function createTraceId(): string;
|
|
4
|
+
export declare function trace(db: Database.Database, traceId: string, agent: string, action: string, input: string, output: string, opts?: {
|
|
5
|
+
parentEventId?: number;
|
|
6
|
+
status?: 'ok' | 'error';
|
|
7
|
+
durationMs?: number;
|
|
8
|
+
}): TraceEvent;
|
|
9
|
+
export declare function getTraceEvents(db: Database.Database, traceId: string): TraceEvent[];
|
|
10
|
+
/**
|
|
11
|
+
* Walk backward from a failed event to find the root cause.
|
|
12
|
+
* Follows parent_event_id links to build the cascade chain.
|
|
13
|
+
*/
|
|
14
|
+
export declare function correlate(db: Database.Database, failureEventId: number): CascadeChain | null;
|
|
15
|
+
/**
|
|
16
|
+
* Find recent failures for an agent or across all agents.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getRecentFailures(db: Database.Database, agent?: string, limit?: number): TraceEvent[];
|
|
19
|
+
/**
|
|
20
|
+
* Find all events in a trace that are errors, then correlate each.
|
|
21
|
+
* Returns all cascade chains for a given trace.
|
|
22
|
+
*/
|
|
23
|
+
export declare function replayTrace(db: Database.Database, traceId: string): CascadeChain[];
|
|
24
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAe,YAAY,EAAE,MAAM,YAAY,CAAC;AAExE,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,KAAK,CACnB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IACJ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CAChB,GACL,UAAU,CA2BZ;AAED,wBAAgB,cAAc,CAC5B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,OAAO,EAAE,MAAM,GACd,UAAU,EAAE,CAMd;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,cAAc,EAAE,MAAM,GACrB,YAAY,GAAG,IAAI,CAkCrB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,GACjB,UAAU,EAAE,CAed;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,OAAO,EAAE,MAAM,GACd,YAAY,EAAE,CAUhB"}
|
package/dist/trace.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
export function createTraceId() {
|
|
3
|
+
return randomUUID();
|
|
4
|
+
}
|
|
5
|
+
export function trace(db, traceId, agent, action, input, output, opts = {}) {
|
|
6
|
+
const stmt = db.prepare(`
|
|
7
|
+
INSERT INTO trace_events (trace_id, agent, action, input, output, parent_event_id, status, duration_ms)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
9
|
+
`);
|
|
10
|
+
const result = stmt.run(traceId, agent, action, input, output, opts.parentEventId ?? null, opts.status ?? 'ok', opts.durationMs ?? 0);
|
|
11
|
+
return {
|
|
12
|
+
id: Number(result.lastInsertRowid),
|
|
13
|
+
trace_id: traceId,
|
|
14
|
+
agent,
|
|
15
|
+
action,
|
|
16
|
+
input,
|
|
17
|
+
output,
|
|
18
|
+
parent_event_id: opts.parentEventId ?? null,
|
|
19
|
+
status: opts.status ?? 'ok',
|
|
20
|
+
duration_ms: opts.durationMs ?? 0,
|
|
21
|
+
created_at: new Date().toISOString(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function getTraceEvents(db, traceId) {
|
|
25
|
+
return db.prepare(`
|
|
26
|
+
SELECT * FROM trace_events
|
|
27
|
+
WHERE trace_id = ?
|
|
28
|
+
ORDER BY created_at ASC
|
|
29
|
+
`).all(traceId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Walk backward from a failed event to find the root cause.
|
|
33
|
+
* Follows parent_event_id links to build the cascade chain.
|
|
34
|
+
*/
|
|
35
|
+
export function correlate(db, failureEventId) {
|
|
36
|
+
const failure = db.prepare(`
|
|
37
|
+
SELECT * FROM trace_events WHERE id = ?
|
|
38
|
+
`).get(failureEventId);
|
|
39
|
+
if (!failure)
|
|
40
|
+
return null;
|
|
41
|
+
const chain = [];
|
|
42
|
+
let current = failure;
|
|
43
|
+
while (current) {
|
|
44
|
+
chain.unshift({
|
|
45
|
+
event_id: current.id,
|
|
46
|
+
agent: current.agent,
|
|
47
|
+
action: current.action,
|
|
48
|
+
input: current.input,
|
|
49
|
+
output: current.output,
|
|
50
|
+
status: current.status,
|
|
51
|
+
duration_ms: current.duration_ms,
|
|
52
|
+
timestamp: current.created_at,
|
|
53
|
+
});
|
|
54
|
+
if (current.parent_event_id === null)
|
|
55
|
+
break;
|
|
56
|
+
current = db.prepare(`
|
|
57
|
+
SELECT * FROM trace_events WHERE id = ?
|
|
58
|
+
`).get(current.parent_event_id);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
failure_id: failureEventId,
|
|
62
|
+
root_cause: chain[0],
|
|
63
|
+
chain,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Find recent failures for an agent or across all agents.
|
|
68
|
+
*/
|
|
69
|
+
export function getRecentFailures(db, agent, limit = 20) {
|
|
70
|
+
if (agent) {
|
|
71
|
+
return db.prepare(`
|
|
72
|
+
SELECT * FROM trace_events
|
|
73
|
+
WHERE agent = ? AND status = 'error'
|
|
74
|
+
ORDER BY created_at DESC
|
|
75
|
+
LIMIT ?
|
|
76
|
+
`).all(agent, limit);
|
|
77
|
+
}
|
|
78
|
+
return db.prepare(`
|
|
79
|
+
SELECT * FROM trace_events
|
|
80
|
+
WHERE status = 'error'
|
|
81
|
+
ORDER BY created_at DESC
|
|
82
|
+
LIMIT ?
|
|
83
|
+
`).all(limit);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Find all events in a trace that are errors, then correlate each.
|
|
87
|
+
* Returns all cascade chains for a given trace.
|
|
88
|
+
*/
|
|
89
|
+
export function replayTrace(db, traceId) {
|
|
90
|
+
const failures = db.prepare(`
|
|
91
|
+
SELECT * FROM trace_events
|
|
92
|
+
WHERE trace_id = ? AND status = 'error'
|
|
93
|
+
ORDER BY created_at ASC
|
|
94
|
+
`).all(traceId);
|
|
95
|
+
return failures
|
|
96
|
+
.map(f => correlate(db, f.id))
|
|
97
|
+
.filter((c) => c !== null);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type AgentStatus = 'healthy' | 'degraded' | 'error' | 'offline';
|
|
2
|
+
export type AlertSeverity = 'info' | 'warning' | 'critical';
|
|
3
|
+
export interface Heartbeat {
|
|
4
|
+
id: number;
|
|
5
|
+
agent: string;
|
|
6
|
+
status: AgentStatus;
|
|
7
|
+
context: string;
|
|
8
|
+
created_at: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TraceEvent {
|
|
11
|
+
id: number;
|
|
12
|
+
trace_id: string;
|
|
13
|
+
agent: string;
|
|
14
|
+
action: string;
|
|
15
|
+
input: string;
|
|
16
|
+
output: string;
|
|
17
|
+
parent_event_id: number | null;
|
|
18
|
+
status: 'ok' | 'error';
|
|
19
|
+
duration_ms: number;
|
|
20
|
+
created_at: string;
|
|
21
|
+
}
|
|
22
|
+
export interface Alert {
|
|
23
|
+
id: number;
|
|
24
|
+
agent: string;
|
|
25
|
+
alert_type: string;
|
|
26
|
+
severity: AlertSeverity;
|
|
27
|
+
message: string;
|
|
28
|
+
count: number;
|
|
29
|
+
first_seen: string;
|
|
30
|
+
last_seen: string;
|
|
31
|
+
resolved: boolean;
|
|
32
|
+
}
|
|
33
|
+
export interface AgentHealth {
|
|
34
|
+
agent: string;
|
|
35
|
+
status: AgentStatus;
|
|
36
|
+
last_heartbeat: string;
|
|
37
|
+
uptime_pct: number;
|
|
38
|
+
active_alerts: number;
|
|
39
|
+
}
|
|
40
|
+
export interface CascadeStep {
|
|
41
|
+
event_id: number;
|
|
42
|
+
agent: string;
|
|
43
|
+
action: string;
|
|
44
|
+
input: string;
|
|
45
|
+
output: string;
|
|
46
|
+
status: 'ok' | 'error';
|
|
47
|
+
duration_ms: number;
|
|
48
|
+
timestamp: string;
|
|
49
|
+
}
|
|
50
|
+
export interface CascadeChain {
|
|
51
|
+
failure_id: number;
|
|
52
|
+
root_cause: CascadeStep;
|
|
53
|
+
chain: CascadeStep[];
|
|
54
|
+
}
|
|
55
|
+
export interface AgentWatchConfig {
|
|
56
|
+
db_path?: string;
|
|
57
|
+
alert_window_minutes?: number;
|
|
58
|
+
heartbeat_stale_minutes?: number;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AACvE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAE5D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,WAAW,CAAC;IACxB,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nicofains1/agentwatch",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Multi-agent observability: cascade failure detection, heartbeats, and forensic replay",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"agentwatch": "dist/cli/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"type-check": "tsc --noEmit",
|
|
16
|
+
"clean": "rm -rf dist"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"better-sqlite3": "^11.8.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/better-sqlite3": "^7.6.8"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"agents",
|
|
29
|
+
"observability",
|
|
30
|
+
"cascade-failure",
|
|
31
|
+
"multi-agent",
|
|
32
|
+
"monitoring",
|
|
33
|
+
"heartbeat",
|
|
34
|
+
"tracing",
|
|
35
|
+
"fleet-management",
|
|
36
|
+
"ai-agents",
|
|
37
|
+
"debugging"
|
|
38
|
+
],
|
|
39
|
+
"author": "nicofains1",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/nicofains1/agentwatch.git"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/nicofains1/agentwatch",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
}
|
|
52
|
+
}
|