next-observer 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -2
  3. package/dist/agents/cards.d.ts +14 -0
  4. package/dist/agents/cards.js +85 -0
  5. package/dist/agents/cards.js.map +1 -0
  6. package/dist/agents/chat.d.ts +21 -0
  7. package/dist/agents/chat.js +83 -0
  8. package/dist/agents/chat.js.map +1 -0
  9. package/dist/agents/index.d.ts +6 -0
  10. package/dist/agents/index.js +8 -0
  11. package/dist/agents/index.js.map +1 -0
  12. package/dist/agents/investigator.d.ts +50 -0
  13. package/dist/agents/investigator.js +123 -0
  14. package/dist/agents/investigator.js.map +1 -0
  15. package/dist/agents/mock-llm.d.ts +7 -0
  16. package/dist/agents/mock-llm.js +46 -0
  17. package/dist/agents/mock-llm.js.map +1 -0
  18. package/dist/agents/model.d.ts +5 -0
  19. package/dist/agents/model.js +20 -0
  20. package/dist/agents/model.js.map +1 -0
  21. package/dist/agents/specialists.d.ts +20 -0
  22. package/dist/agents/specialists.js +74 -0
  23. package/dist/agents/specialists.js.map +1 -0
  24. package/dist/bin.d.ts +2 -0
  25. package/dist/bin.js +20 -0
  26. package/dist/bin.js.map +1 -0
  27. package/dist/cli.d.ts +29 -0
  28. package/dist/cli.js +231 -0
  29. package/dist/cli.js.map +1 -0
  30. package/dist/collector/chat.d.ts +93 -0
  31. package/dist/collector/chat.js +2 -0
  32. package/dist/collector/chat.js.map +1 -0
  33. package/dist/collector/decode.d.ts +52 -0
  34. package/dist/collector/decode.js +59 -0
  35. package/dist/collector/decode.js.map +1 -0
  36. package/dist/collector/framework.d.ts +7 -0
  37. package/dist/collector/framework.js +9 -0
  38. package/dist/collector/framework.js.map +1 -0
  39. package/dist/collector/index.d.ts +6 -0
  40. package/dist/collector/index.js +5 -0
  41. package/dist/collector/index.js.map +1 -0
  42. package/dist/collector/memory-storage.d.ts +21 -0
  43. package/dist/collector/memory-storage.js +185 -0
  44. package/dist/collector/memory-storage.js.map +1 -0
  45. package/dist/collector/server.d.ts +31 -0
  46. package/dist/collector/server.js +267 -0
  47. package/dist/collector/server.js.map +1 -0
  48. package/dist/collector/static.d.ts +3 -0
  49. package/dist/collector/static.js +43 -0
  50. package/dist/collector/static.js.map +1 -0
  51. package/dist/collector/types.d.ts +116 -0
  52. package/dist/collector/types.js +2 -0
  53. package/dist/collector/types.js.map +1 -0
  54. package/dist/debug/demo.d.ts +4 -0
  55. package/dist/debug/demo.js +90 -0
  56. package/dist/debug/demo.js.map +1 -0
  57. package/dist/debug/detector.d.ts +61 -0
  58. package/dist/debug/detector.js +116 -0
  59. package/dist/debug/detector.js.map +1 -0
  60. package/dist/debug/index.d.ts +3 -0
  61. package/dist/debug/index.js +5 -0
  62. package/dist/debug/index.js.map +1 -0
  63. package/dist/debug/queries.d.ts +207 -0
  64. package/dist/debug/queries.js +242 -0
  65. package/dist/debug/queries.js.map +1 -0
  66. package/dist/ui/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  67. package/dist/ui/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  68. package/dist/ui/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  69. package/dist/ui/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  70. package/dist/ui/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  71. package/dist/ui/assets/index-Y3VIawX_.css +2 -0
  72. package/dist/ui/assets/index-vwPciq9g.js +11 -0
  73. package/dist/ui/index.html +13 -0
  74. package/package.json +72 -4
@@ -0,0 +1,116 @@
1
+ export type AttributeValue = string | number | boolean | null | AttributeValue[] | {
2
+ [key: string]: AttributeValue;
3
+ };
4
+ export type Attributes = Record<string, AttributeValue>;
5
+ export type SpanKindName = 'unspecified' | 'internal' | 'server' | 'client' | 'producer' | 'consumer';
6
+ export type SpanStatusName = 'unset' | 'ok' | 'error';
7
+ export interface SpanEvent {
8
+ name: string;
9
+ timeMs: number;
10
+ attributes: Attributes;
11
+ }
12
+ /** Storage-independent span shape — every storage adapter and every agent tool speaks this. */
13
+ export interface NormalizedSpan {
14
+ traceId: string;
15
+ spanId: string;
16
+ parentSpanId: string | null;
17
+ name: string;
18
+ kind: SpanKindName;
19
+ service: string;
20
+ serviceVersion: string | null;
21
+ scope: string | null;
22
+ /** Unix epoch, milliseconds (fractional). */
23
+ startTimeMs: number;
24
+ durationMs: number;
25
+ status: SpanStatusName;
26
+ statusMessage: string | null;
27
+ attributes: Attributes;
28
+ resource: Attributes;
29
+ events: SpanEvent[];
30
+ }
31
+ export interface TraceFilter {
32
+ service?: string;
33
+ /** Case-insensitive substring match on any span name in the trace. */
34
+ operation?: string;
35
+ /** Minimum duration of the whole trace. */
36
+ minDurationMs?: number;
37
+ hasError?: boolean;
38
+ fromMs?: number;
39
+ toMs?: number;
40
+ /** Default 50. */
41
+ limit?: number;
42
+ }
43
+ export interface TraceSummary {
44
+ traceId: string;
45
+ rootName: string;
46
+ rootService: string;
47
+ services: string[];
48
+ startTimeMs: number;
49
+ durationMs: number;
50
+ spanCount: number;
51
+ errorCount: number;
52
+ }
53
+ export interface OperationFilter {
54
+ service?: string;
55
+ operation?: string;
56
+ fromMs?: number;
57
+ toMs?: number;
58
+ /** Split stats per service.version — the basis for "which deployment introduced the regression". */
59
+ byVersion?: boolean;
60
+ /** Skip Next.js internal spans (see isFrameworkSpan). */
61
+ hideFramework?: boolean;
62
+ /** Skip each route's first request in a fresh process (StorageAdapter.isColdStart); counted in `coldStarts`. */
63
+ hideColdStarts?: boolean;
64
+ }
65
+ export interface OperationStats {
66
+ service: string;
67
+ /** Set only when stats were requested with byVersion. */
68
+ serviceVersion?: string | null;
69
+ operation: string;
70
+ count: number;
71
+ errorCount: number;
72
+ errorRate: number;
73
+ avgMs: number;
74
+ p50Ms: number;
75
+ p95Ms: number;
76
+ p99Ms: number;
77
+ maxMs: number;
78
+ /** With hideColdStarts: how many cold-start requests were left out. */
79
+ coldStarts?: number;
80
+ /** With hideColdStarts: every request was a cold start, so they are kept — these numbers include compile time. */
81
+ onlyColdStarts?: boolean;
82
+ }
83
+ export interface SpanFilter {
84
+ service?: string;
85
+ /** Case-insensitive substring match on the span name. */
86
+ operation?: string;
87
+ status?: SpanStatusName;
88
+ fromMs?: number;
89
+ toMs?: number;
90
+ /** Default 100. */
91
+ limit?: number;
92
+ /** Skip Next.js internal spans (see isFrameworkSpan). */
93
+ hideFramework?: boolean;
94
+ }
95
+ export interface ServiceInfo {
96
+ name: string;
97
+ /** In order of first appearance — i.e. deploy order. */
98
+ versions: string[];
99
+ spanCount: number;
100
+ lastSeenMs: number;
101
+ }
102
+ export interface StorageAdapter {
103
+ insertSpans(spans: NormalizedSpan[]): Promise<void>;
104
+ queryTraces(filter: TraceFilter): Promise<TraceSummary[]>;
105
+ getTrace(traceId: string): Promise<NormalizedSpan[]>;
106
+ getOperationStats(filter: OperationFilter): Promise<OperationStats[]>;
107
+ /** Most recently received first. */
108
+ querySpans(filter: SpanFilter): Promise<NormalizedSpan[]>;
109
+ getServices(): Promise<ServiceInfo[]>;
110
+ count(): Promise<number>;
111
+ /**
112
+ * True for the first server span of a route in a process (resource `service.instance.id`, sent by next-observe): in
113
+ * `next dev` that request includes compiling the route, so it is not representative latency. False without an id.
114
+ */
115
+ isColdStart(span: NormalizedSpan): boolean;
116
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/collector/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { NormalizedSpan, StorageAdapter } from '../collector/types.js';
2
+ export declare function demoSpans(now?: number): NormalizedSpan[];
3
+ /** Inserts the demo scenario. Old v0 data goes first so deploy order is v0 → v1 → v2. */
4
+ export declare function seedDemo(storage: StorageAdapter, now?: number): Promise<void>;
@@ -0,0 +1,90 @@
1
+ const MIN = 60_000;
2
+ let seq = 0;
3
+ const id = (length) => (++seq).toString(16).padStart(length, '0');
4
+ function span(traceId, parent, name, startTimeMs, durationMs, extra = {}) {
5
+ return {
6
+ traceId,
7
+ spanId: id(16),
8
+ parentSpanId: parent?.spanId ?? null,
9
+ name,
10
+ kind: parent ? 'internal' : 'server',
11
+ service: 'shop',
12
+ serviceVersion: parent?.serviceVersion ?? 'v1',
13
+ scope: 'test',
14
+ startTimeMs,
15
+ durationMs,
16
+ status: 'unset',
17
+ statusMessage: null,
18
+ attributes: {},
19
+ resource: {},
20
+ events: [],
21
+ ...extra,
22
+ };
23
+ }
24
+ // Like a real Next.js app: the request span (kind server) and Next's own "executing api route" step around user code.
25
+ const nextRequest = { 'next.span_type': 'BaseServer.handleRequest' };
26
+ function nextRoute(t, root, at, durationMs, extra = {}) {
27
+ const route = String(root.attributes['http.route']);
28
+ return span(t, root, `executing api route (app) ${route}`, at, durationMs, { ...extra, attributes: { 'next.span_type': 'AppRouteRouteHandlers.runHandler', 'next.route': route } });
29
+ }
30
+ function checkout(version, at, paymentMs) {
31
+ const t = id(32);
32
+ const root = span(t, null, 'POST /api/checkout', at, paymentMs + 20, { serviceVersion: version, attributes: { ...nextRequest, 'http.route': '/api/checkout', 'http.status_code': 200 } });
33
+ const route = nextRoute(t, root, at + 5, paymentMs + 10);
34
+ return [root, route, span(t, route, 'chargePayment', at + 10, paymentMs, { attributes: { 'code.filepath': 'lib/payment.ts', 'code.function': 'chargePayment' } })];
35
+ }
36
+ function inventory(version, at, fails) {
37
+ const t = id(32);
38
+ const root = span(t, null, 'GET /api/inventory/[id]', at, 15, {
39
+ serviceVersion: version,
40
+ status: fails ? 'error' : 'unset',
41
+ attributes: { ...nextRequest, 'http.route': '/api/inventory/[id]', 'http.status_code': fails ? 500 : 200 },
42
+ });
43
+ const route = nextRoute(t, root, at + 1, 12, { status: fails ? 'error' : 'unset', statusMessage: fails ? 'Inventory service timeout: upstream not responding' : null });
44
+ const check = span(t, route, 'inventory.check', at + 2, 10, {
45
+ status: fails ? 'error' : 'unset',
46
+ statusMessage: fails ? 'Inventory service timeout: upstream not responding' : null,
47
+ attributes: { 'code.filepath': 'app/api/inventory/[id]/route.ts' },
48
+ events: fails ? [{ name: 'exception', timeMs: at + 12, attributes: { 'exception.message': 'Inventory service timeout: upstream not responding' } }] : [],
49
+ });
50
+ return [root, route, check];
51
+ }
52
+ function catalog(version, at) {
53
+ const t = id(32);
54
+ const root = span(t, null, 'GET /api/products', at, 120, { serviceVersion: version, attributes: { ...nextRequest, 'http.route': '/api/products' } });
55
+ const route = nextRoute(t, root, at + 1, 118);
56
+ const ids = span(t, route, 'getProductIds', at + 1, 8);
57
+ const queries = [0, 1, 2, 3, 4].map((i) => span(t, route, 'db.query', at + 10 + i * 20, 18, { attributes: { 'db.statement': 'SELECT * FROM products WHERE id = ?' } }));
58
+ return [root, route, ids, ...queries];
59
+ }
60
+ const PAYMENT_V1 = [160, 180, 200, 240, 260, 300];
61
+ const PAYMENT_V2 = [1400, 1450, 1600, 2100, 2380, 2490];
62
+ export function demoSpans(now = Date.now()) {
63
+ seq = 0;
64
+ const spans = [];
65
+ // v1: 12–8 minutes ago
66
+ for (let i = 0; i < 30; i++) {
67
+ const at = now - 12 * MIN + i * 8_000;
68
+ spans.push(...checkout('v1', at, PAYMENT_V1[i % PAYMENT_V1.length]));
69
+ spans.push(...inventory('v1', at + 1000, i % 10 < 3));
70
+ if (i % 5 === 0)
71
+ spans.push(...catalog('v1', at + 2000));
72
+ }
73
+ // v2: 5–1 minutes ago (fresh enough not to look like a silent service)
74
+ for (let i = 0; i < 30; i++) {
75
+ const at = now - 5 * MIN + i * 8_000;
76
+ spans.push(...checkout('v2', at, PAYMENT_V2[i % PAYMENT_V2.length]));
77
+ spans.push(...inventory('v2', at + 1000, i % 10 < 3));
78
+ if (i % 5 === 0)
79
+ spans.push(...catalog('v2', at + 2000));
80
+ }
81
+ // Old data outside the default 15-minute window (seedDemo inserts these 3 spans first).
82
+ spans.push(...checkout('v0', now - 40 * MIN, 50));
83
+ return spans;
84
+ }
85
+ /** Inserts the demo scenario. Old v0 data goes first so deploy order is v0 → v1 → v2. */
86
+ export async function seedDemo(storage, now = Date.now()) {
87
+ const spans = demoSpans(now);
88
+ await storage.insertSpans([...spans.slice(-3), ...spans.slice(0, -3)]);
89
+ }
90
+ //# sourceMappingURL=demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../src/debug/demo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { NormalizedSpan } from '../collector/types.js';
2
+ export type AnomalyType = 'high_error_rate' | 'high_latency' | 'no_traffic';
3
+ export interface Anomaly {
4
+ id: string;
5
+ type: AnomalyType;
6
+ /** 'all' = share of all server requests; 'operation' = one operation failing/slow on its own (see `subject`). */
7
+ scope: 'all' | 'operation';
8
+ subject?: {
9
+ service: string;
10
+ operation: string;
11
+ };
12
+ severity: 'warning' | 'critical';
13
+ detectedAtMs: number;
14
+ /** What was measured: error share, slow share, or seconds of silence. */
15
+ value: number;
16
+ threshold: number;
17
+ /** Server requests in the window the value is based on (0 for no_traffic). */
18
+ sampleSize: number;
19
+ windowMs: number;
20
+ /** Operations behind the anomaly, worst first. */
21
+ operations: {
22
+ service: string;
23
+ operation: string;
24
+ count: number;
25
+ errors: number;
26
+ slow: number;
27
+ }[];
28
+ }
29
+ export interface DetectorOptions {
30
+ windowMs?: number;
31
+ /** Error share of server requests in the window. */
32
+ errorRate?: number;
33
+ /** A request slower than this counts as slow. */
34
+ slowMs?: number;
35
+ /** Slow share of server requests in the window. */
36
+ slowRate?: number;
37
+ /** Silence after traffic was seen. */
38
+ noTrafficMs?: number;
39
+ /** Below this many server requests in the window, rates are not judged. */
40
+ minSamples?: number;
41
+ /** The same anomaly type is not reported again within this time. */
42
+ cooldownMs?: number;
43
+ now?: () => number;
44
+ }
45
+ export declare class AnomalyDetector {
46
+ readonly options: Required<Omit<DetectorOptions, 'now'>>;
47
+ private readonly now;
48
+ private window;
49
+ private lastSeenMs;
50
+ /** Cooldown per type (app-wide) or per type + operation. */
51
+ private readonly lastReportedMs;
52
+ private seq;
53
+ constructor(options?: DetectorOptions);
54
+ /** Feed spans as they are ingested. */
55
+ observe(spans: NormalizedSpan[]): void;
56
+ /** Evaluate the window now; returns new anomalies (respecting cooldowns). */
57
+ check(): Anomaly[];
58
+ private anomaly;
59
+ }
60
+ /** The question the agents get when an anomaly fires — carries the evidence so they know where to start. */
61
+ export declare function questionFor(anomaly: Anomaly): string;
@@ -0,0 +1,116 @@
1
+ const DEFAULTS = { windowMs: 10_000, errorRate: 0.2, slowMs: 1000, slowRate: 0.3, noTrafficMs: 120_000, minSamples: 5, cooldownMs: 300_000 };
2
+ // Only server entry spans count: Next emits ~7 internal spans per request, which would dilute a 30% error rate below
3
+ // any threshold. Browser and internal spans are ignored.
4
+ const isServerRequest = (s) => s.kind === 'server';
5
+ export class AnomalyDetector {
6
+ options;
7
+ now;
8
+ window = [];
9
+ lastSeenMs = null;
10
+ /** Cooldown per type (app-wide) or per type + operation. */
11
+ lastReportedMs = new Map();
12
+ seq = 0;
13
+ constructor(options = {}) {
14
+ const { now, ...rest } = options;
15
+ this.options = { ...DEFAULTS, ...rest };
16
+ this.now = now ?? Date.now;
17
+ }
18
+ /** Feed spans as they are ingested. */
19
+ observe(spans) {
20
+ const atMs = this.now();
21
+ if (spans.length > 0)
22
+ this.lastSeenMs = atMs;
23
+ for (const span of spans)
24
+ if (isServerRequest(span))
25
+ this.window.push({ atMs, span });
26
+ }
27
+ /** Evaluate the window now; returns new anomalies (respecting cooldowns). */
28
+ check() {
29
+ const now = this.now();
30
+ const o = this.options;
31
+ this.window = this.window.filter((w) => now - w.atMs <= o.windowMs);
32
+ const requests = this.window.map((w) => w.span);
33
+ const found = [];
34
+ const judge = (list, subject) => {
35
+ if (list.length < o.minSamples)
36
+ return;
37
+ const errorRate = list.filter((s) => s.status === 'error').length / list.length;
38
+ const slowRate = list.filter((s) => s.durationMs > o.slowMs).length / list.length;
39
+ // An operation-level anomaly would only repeat an app-wide one of the same type.
40
+ const covered = (type) => subject && found.some((a) => a.type === type && a.scope === 'all');
41
+ if (errorRate > o.errorRate && !covered('high_error_rate')) {
42
+ found.push(this.anomaly('high_error_rate', errorRate, o.errorRate, list, now, errorRate >= 2 * o.errorRate, subject));
43
+ }
44
+ if (slowRate > o.slowRate && !covered('high_latency')) {
45
+ found.push(this.anomaly('high_latency', slowRate, o.slowRate, list, now, slowRate >= 2 * o.slowRate, subject));
46
+ }
47
+ };
48
+ judge(requests);
49
+ // Per operation: on a realistic route mix one broken endpoint (30% errors) is only ~10% of all requests —
50
+ // below the app-wide threshold, yet clearly an incident.
51
+ const byOperation = new Map();
52
+ for (const s of requests) {
53
+ const key = `${s.service}\u0000${s.name}`;
54
+ byOperation.set(key, [...(byOperation.get(key) ?? []), s]);
55
+ }
56
+ for (const list of byOperation.values())
57
+ judge(list, { service: list[0].service, operation: list[0].name });
58
+ if (this.lastSeenMs !== null && now - this.lastSeenMs > o.noTrafficMs) {
59
+ found.push(this.anomaly('no_traffic', Math.round((now - this.lastSeenMs) / 1000), o.noTrafficMs / 1000, [], now, true));
60
+ }
61
+ return found.filter((a) => {
62
+ const key = a.subject ? `${a.type}\u0000${a.subject.service}\u0000${a.subject.operation}` : a.type;
63
+ const last = this.lastReportedMs.get(key);
64
+ if (last !== undefined && now - last < o.cooldownMs)
65
+ return false;
66
+ this.lastReportedMs.set(key, now);
67
+ return true;
68
+ });
69
+ }
70
+ anomaly(type, value, threshold, requests, now, critical, subject) {
71
+ const byOperation = new Map();
72
+ for (const s of requests) {
73
+ const key = `${s.service}\u0000${s.name}`;
74
+ const entry = byOperation.get(key) ?? { service: s.service, operation: s.name, count: 0, errors: 0, slow: 0 };
75
+ entry.count++;
76
+ if (s.status === 'error')
77
+ entry.errors++;
78
+ if (s.durationMs > this.options.slowMs)
79
+ entry.slow++;
80
+ byOperation.set(key, entry);
81
+ }
82
+ const culprit = (e) => (type === 'high_error_rate' ? e.errors : e.slow);
83
+ const operations = [...byOperation.values()]
84
+ .filter((e) => type === 'no_traffic' || culprit(e) > 0)
85
+ .sort((a, b) => culprit(b) - culprit(a))
86
+ .slice(0, 3);
87
+ return {
88
+ id: `${type}-${now}-${++this.seq}`,
89
+ type,
90
+ scope: subject ? 'operation' : 'all',
91
+ ...(subject ? { subject } : {}),
92
+ severity: critical ? 'critical' : 'warning',
93
+ detectedAtMs: now,
94
+ value: Math.round(value * 1000) / 1000,
95
+ threshold,
96
+ sampleSize: requests.length,
97
+ windowMs: this.options.windowMs,
98
+ operations,
99
+ };
100
+ }
101
+ }
102
+ /** The question the agents get when an anomaly fires — carries the evidence so they know where to start. */
103
+ export function questionFor(anomaly) {
104
+ const ops = anomaly.operations.map((o) => `${o.operation} (${o.service}: ${o.errors} errors, ${o.slow} slow of ${o.count})`).join('; ');
105
+ const seconds = Math.round(anomaly.windowMs / 1000);
106
+ const what = anomaly.subject ? `${anomaly.subject.operation} requests` : 'server requests';
107
+ switch (anomaly.type) {
108
+ case 'high_error_rate':
109
+ return `Anomaly detected: ${Math.round(anomaly.value * 100)}% of ${what} failed in the last ${seconds}s (${anomaly.sampleSize} requests). Most affected: ${ops}. Find the failing operation, the exact error and its source.`;
110
+ case 'high_latency':
111
+ return `Anomaly detected: ${Math.round(anomaly.value * 100)}% of ${what} were slower than threshold in the last ${seconds}s (${anomaly.sampleSize} requests). Most affected: ${ops}. Find what is slow and which deployment introduced it.`;
112
+ case 'no_traffic':
113
+ return `Anomaly detected: no spans received for ${anomaly.value}s after traffic was flowing. Check which services went silent.`;
114
+ }
115
+ }
116
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detector.js","sourceRoot":"","sources":["../../src/debug/detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { createAgentQueries, type AgentQueries, type QueryOptions, type TraceRow } from './queries.js';
2
+ export { demoSpans, seedDemo } from './demo.js';
3
+ export { AnomalyDetector, questionFor, type Anomaly, type AnomalyType, type DetectorOptions } from './detector.js';
@@ -0,0 +1,5 @@
1
+ // Agent building blocks. No LLM framework is imported here — ADK wiring comes on top.
2
+ export { createAgentQueries } from './queries.js';
3
+ export { demoSpans, seedDemo } from './demo.js';
4
+ export { AnomalyDetector, questionFor } from './detector.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/debug/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,kBAAkB,EAAuD,MAAM,cAAc,CAAA;AACtG,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAwD,MAAM,eAAe,CAAA"}
@@ -0,0 +1,207 @@
1
+ import type { StorageAdapter } from '../collector/types.js';
2
+ export interface QueryOptions {
3
+ /** Injectable clock for tests. */
4
+ now?: () => number;
5
+ }
6
+ interface Window {
7
+ /** Look back this many minutes. Default 15. */
8
+ sinceMinutes?: number;
9
+ }
10
+ export interface TraceRow {
11
+ depth: number;
12
+ name: string;
13
+ service: string;
14
+ durationMs: number;
15
+ /** Duration minus direct children — where the time was actually spent. */
16
+ selfMs: number;
17
+ /** First request of this route after a server start — in next dev its time includes compiling the route. */
18
+ coldStart?: boolean;
19
+ /** Self time of hidden Next.js internal spans under this one (routing, rendering, dev compilation) — not user code. */
20
+ nextInternalMs?: number;
21
+ error?: string;
22
+ attributes?: Record<string, unknown>;
23
+ }
24
+ export declare function createAgentQueries(storage: StorageAdapter, options?: QueryOptions): {
25
+ /** Services, their deployed versions and how fresh their data is. */
26
+ getServices(): Promise<{
27
+ services: {
28
+ name: string;
29
+ versions: string[];
30
+ spans: number;
31
+ lastSpanSecondsAgo: number;
32
+ }[];
33
+ }>;
34
+ /** Slowest operations by p95 with error rates. */
35
+ getOperationStats(args?: {
36
+ service?: string;
37
+ operation?: string;
38
+ limit?: number;
39
+ } & Window): Promise<{
40
+ note: string;
41
+ operations: {
42
+ service: string;
43
+ operation: string;
44
+ count: number;
45
+ errorRate: number;
46
+ p50Ms: number;
47
+ p95Ms: number;
48
+ p99Ms: number;
49
+ onlyColdStarts?: boolean | undefined;
50
+ }[];
51
+ } | {
52
+ note?: undefined;
53
+ operations: {
54
+ service: string;
55
+ operation: string;
56
+ count: number;
57
+ errorRate: number;
58
+ p50Ms: number;
59
+ p95Ms: number;
60
+ p99Ms: number;
61
+ onlyColdStarts?: boolean | undefined;
62
+ }[];
63
+ }>;
64
+ /**
65
+ * Latest deployed version vs the one before it, per operation (versions ordered by first appearance = deploy order).
66
+ * Biggest p95 increase first — the answer to "which deployment introduced the regression".
67
+ */
68
+ compareVersions(args?: {
69
+ service?: string;
70
+ operation?: string;
71
+ } & Window): Promise<{
72
+ note: string;
73
+ changes: {
74
+ service: string;
75
+ operation: string;
76
+ from: {
77
+ version: string;
78
+ count: number;
79
+ p50Ms: number;
80
+ p95Ms: number;
81
+ errorRate: number;
82
+ onlyColdStarts?: boolean | undefined;
83
+ };
84
+ to: {
85
+ version: string;
86
+ count: number;
87
+ p50Ms: number;
88
+ p95Ms: number;
89
+ errorRate: number;
90
+ onlyColdStarts?: boolean | undefined;
91
+ };
92
+ p95Ratio: number | null;
93
+ errorRateDelta: number;
94
+ }[];
95
+ } | {
96
+ note?: undefined;
97
+ changes: {
98
+ service: string;
99
+ operation: string;
100
+ from: {
101
+ version: string;
102
+ count: number;
103
+ p50Ms: number;
104
+ p95Ms: number;
105
+ errorRate: number;
106
+ onlyColdStarts?: boolean | undefined;
107
+ };
108
+ to: {
109
+ version: string;
110
+ count: number;
111
+ p50Ms: number;
112
+ p95Ms: number;
113
+ errorRate: number;
114
+ onlyColdStarts?: boolean | undefined;
115
+ };
116
+ p95Ratio: number | null;
117
+ errorRateDelta: number;
118
+ }[];
119
+ }>;
120
+ /** Failing operations: error rate, most common exception messages and example traces. */
121
+ getErrors(args?: {
122
+ service?: string;
123
+ operation?: string;
124
+ limit?: number;
125
+ } & Window): Promise<{
126
+ note: string;
127
+ errors: {
128
+ service: string;
129
+ operation: string;
130
+ errors: number;
131
+ errorRate: number | null;
132
+ topMessages: {
133
+ message: string;
134
+ count: number;
135
+ }[];
136
+ exampleTraceIds: string[];
137
+ }[];
138
+ } | {
139
+ note?: undefined;
140
+ errors: {
141
+ service: string;
142
+ operation: string;
143
+ errors: number;
144
+ errorRate: number | null;
145
+ topMessages: {
146
+ message: string;
147
+ count: number;
148
+ }[];
149
+ exampleTraceIds: string[];
150
+ }[];
151
+ }>;
152
+ /** Recent traces matching filters — to get example trace ids. */
153
+ searchTraces(args?: {
154
+ service?: string;
155
+ operation?: string;
156
+ minDurationMs?: number;
157
+ hasError?: boolean;
158
+ limit?: number;
159
+ } & Window): Promise<{
160
+ note: string;
161
+ traces: {
162
+ traceId: string;
163
+ root: string;
164
+ services: string[];
165
+ durationMs: number;
166
+ spans: number;
167
+ errors: number;
168
+ secondsAgo: number;
169
+ }[];
170
+ } | {
171
+ note?: undefined;
172
+ traces: {
173
+ traceId: string;
174
+ root: string;
175
+ services: string[];
176
+ durationMs: number;
177
+ spans: number;
178
+ errors: number;
179
+ secondsAgo: number;
180
+ }[];
181
+ }>;
182
+ /**
183
+ * One trace as a compact tree: depth, duration, self time (duration minus children), errors and code location.
184
+ * `repeated` flags ≥3 same-named siblings — the N+1 signature.
185
+ */
186
+ getTrace(args: {
187
+ traceId: string;
188
+ maxSpans?: number;
189
+ }): Promise<{
190
+ note: string;
191
+ spans: never[];
192
+ repeated: never[];
193
+ } | {
194
+ traceId: string;
195
+ note?: string | undefined;
196
+ nextInternalSpansHidden?: number | undefined;
197
+ spans: TraceRow[];
198
+ repeated: {
199
+ parent: string;
200
+ operation: string;
201
+ count: number;
202
+ totalMs: number;
203
+ }[];
204
+ }>;
205
+ };
206
+ export type AgentQueries = ReturnType<typeof createAgentQueries>;
207
+ export {};