@adonis-agora/telescope 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/configure.d.ts.map +1 -1
  3. package/dist/configure.js +6 -0
  4. package/dist/configure.js.map +1 -1
  5. package/dist/providers/telescope_ai_provider.d.ts +9 -1
  6. package/dist/providers/telescope_ai_provider.d.ts.map +1 -1
  7. package/dist/providers/telescope_ai_provider.js +22 -2
  8. package/dist/providers/telescope_ai_provider.js.map +1 -1
  9. package/dist/providers/telescope_alerts_provider.d.ts +1 -0
  10. package/dist/providers/telescope_alerts_provider.d.ts.map +1 -1
  11. package/dist/providers/telescope_alerts_provider.js +37 -1
  12. package/dist/providers/telescope_alerts_provider.js.map +1 -1
  13. package/dist/providers/telescope_mcp_provider.d.ts +26 -0
  14. package/dist/providers/telescope_mcp_provider.d.ts.map +1 -0
  15. package/dist/providers/telescope_mcp_provider.js +114 -0
  16. package/dist/providers/telescope_mcp_provider.js.map +1 -0
  17. package/dist/providers/telescope_provider.d.ts +8 -0
  18. package/dist/providers/telescope_provider.d.ts.map +1 -1
  19. package/dist/providers/telescope_provider.js +56 -1
  20. package/dist/providers/telescope_provider.js.map +1 -1
  21. package/dist/providers/telescope_ui_provider.d.ts +2 -0
  22. package/dist/providers/telescope_ui_provider.d.ts.map +1 -1
  23. package/dist/providers/telescope_ui_provider.js +106 -22
  24. package/dist/providers/telescope_ui_provider.js.map +1 -1
  25. package/dist/providers/telescope_watchers_provider.d.ts +13 -2
  26. package/dist/providers/telescope_watchers_provider.d.ts.map +1 -1
  27. package/dist/providers/telescope_watchers_provider.js +57 -7
  28. package/dist/providers/telescope_watchers_provider.js.map +1 -1
  29. package/dist/src/ai/define_config.d.ts +39 -7
  30. package/dist/src/ai/define_config.d.ts.map +1 -1
  31. package/dist/src/ai/define_config.js +14 -4
  32. package/dist/src/ai/define_config.js.map +1 -1
  33. package/dist/src/ai/diagnosis_coordinator.d.ts +106 -0
  34. package/dist/src/ai/diagnosis_coordinator.d.ts.map +1 -0
  35. package/dist/src/ai/diagnosis_coordinator.js +143 -0
  36. package/dist/src/ai/diagnosis_coordinator.js.map +1 -0
  37. package/dist/src/ai/factory.d.ts +8 -5
  38. package/dist/src/ai/factory.d.ts.map +1 -1
  39. package/dist/src/ai/factory.js +20 -8
  40. package/dist/src/ai/factory.js.map +1 -1
  41. package/dist/src/ai/index.d.ts +3 -1
  42. package/dist/src/ai/index.d.ts.map +1 -1
  43. package/dist/src/ai/index.js +3 -1
  44. package/dist/src/ai/index.js.map +1 -1
  45. package/dist/src/alerts/alert_rule.d.ts +79 -11
  46. package/dist/src/alerts/alert_rule.d.ts.map +1 -1
  47. package/dist/src/alerts/alerter.d.ts +11 -1
  48. package/dist/src/alerts/alerter.d.ts.map +1 -1
  49. package/dist/src/alerts/alerter.js +21 -1
  50. package/dist/src/alerts/alerter.js.map +1 -1
  51. package/dist/src/alerts/alerter_service.d.ts +103 -0
  52. package/dist/src/alerts/alerter_service.d.ts.map +1 -0
  53. package/dist/src/alerts/alerter_service.js +215 -0
  54. package/dist/src/alerts/alerter_service.js.map +1 -0
  55. package/dist/src/alerts/index.d.ts +4 -2
  56. package/dist/src/alerts/index.d.ts.map +1 -1
  57. package/dist/src/alerts/index.js +4 -2
  58. package/dist/src/alerts/index.js.map +1 -1
  59. package/dist/src/alerts/slack_format.d.ts.map +1 -1
  60. package/dist/src/alerts/slack_format.js +35 -2
  61. package/dist/src/alerts/slack_format.js.map +1 -1
  62. package/dist/src/client_errors/config.d.ts +66 -0
  63. package/dist/src/client_errors/config.d.ts.map +1 -0
  64. package/dist/src/client_errors/config.js +17 -0
  65. package/dist/src/client_errors/config.js.map +1 -0
  66. package/dist/src/client_errors/index.d.ts +5 -0
  67. package/dist/src/client_errors/index.d.ts.map +1 -0
  68. package/dist/src/client_errors/index.js +5 -0
  69. package/dist/src/client_errors/index.js.map +1 -0
  70. package/dist/src/client_errors/ingestor.d.ts +87 -0
  71. package/dist/src/client_errors/ingestor.d.ts.map +1 -0
  72. package/dist/src/client_errors/ingestor.js +135 -0
  73. package/dist/src/client_errors/ingestor.js.map +1 -0
  74. package/dist/src/client_errors/rate_limiter.d.ts +53 -0
  75. package/dist/src/client_errors/rate_limiter.d.ts.map +1 -0
  76. package/dist/src/client_errors/rate_limiter.js +83 -0
  77. package/dist/src/client_errors/rate_limiter.js.map +1 -0
  78. package/dist/src/client_errors/validation.d.ts +62 -0
  79. package/dist/src/client_errors/validation.d.ts.map +1 -0
  80. package/dist/src/client_errors/validation.js +109 -0
  81. package/dist/src/client_errors/validation.js.map +1 -0
  82. package/dist/src/define_config.d.ts +120 -0
  83. package/dist/src/define_config.d.ts.map +1 -1
  84. package/dist/src/define_config.js +41 -0
  85. package/dist/src/define_config.js.map +1 -1
  86. package/dist/src/entry.d.ts +20 -0
  87. package/dist/src/entry.d.ts.map +1 -1
  88. package/dist/src/entry.js +20 -0
  89. package/dist/src/entry.js.map +1 -1
  90. package/dist/src/index.d.ts +16 -3
  91. package/dist/src/index.d.ts.map +1 -1
  92. package/dist/src/index.js +15 -2
  93. package/dist/src/index.js.map +1 -1
  94. package/dist/src/mcp/define_config.d.ts +57 -0
  95. package/dist/src/mcp/define_config.d.ts.map +1 -0
  96. package/dist/src/mcp/define_config.js +35 -0
  97. package/dist/src/mcp/define_config.js.map +1 -0
  98. package/dist/src/mcp/index.d.ts +7 -0
  99. package/dist/src/mcp/index.d.ts.map +1 -0
  100. package/dist/src/mcp/index.js +7 -0
  101. package/dist/src/mcp/index.js.map +1 -0
  102. package/dist/src/mcp/server.d.ts +89 -0
  103. package/dist/src/mcp/server.d.ts.map +1 -0
  104. package/dist/src/mcp/server.js +279 -0
  105. package/dist/src/mcp/server.js.map +1 -0
  106. package/dist/src/metrics/pulse.d.ts +158 -0
  107. package/dist/src/metrics/pulse.d.ts.map +1 -0
  108. package/dist/src/metrics/pulse.js +414 -0
  109. package/dist/src/metrics/pulse.js.map +1 -0
  110. package/dist/src/overload_guard.d.ts +77 -0
  111. package/dist/src/overload_guard.d.ts.map +1 -0
  112. package/dist/src/overload_guard.js +134 -0
  113. package/dist/src/overload_guard.js.map +1 -0
  114. package/dist/src/pruner.d.ts +93 -0
  115. package/dist/src/pruner.d.ts.map +1 -0
  116. package/dist/src/pruner.js +120 -0
  117. package/dist/src/pruner.js.map +1 -0
  118. package/dist/src/registry.d.ts +26 -0
  119. package/dist/src/registry.d.ts.map +1 -1
  120. package/dist/src/registry.js +19 -0
  121. package/dist/src/registry.js.map +1 -1
  122. package/dist/src/service.d.ts +16 -1
  123. package/dist/src/service.d.ts.map +1 -1
  124. package/dist/src/service.js +19 -1
  125. package/dist/src/service.js.map +1 -1
  126. package/dist/src/telescope_middleware.d.ts.map +1 -1
  127. package/dist/src/telescope_middleware.js +3 -1
  128. package/dist/src/telescope_middleware.js.map +1 -1
  129. package/dist/src/ui/api.d.ts +9 -1
  130. package/dist/src/ui/api.d.ts.map +1 -1
  131. package/dist/src/ui/api.js +23 -1
  132. package/dist/src/ui/api.js.map +1 -1
  133. package/dist/src/ui/auth.d.ts +112 -0
  134. package/dist/src/ui/auth.d.ts.map +1 -0
  135. package/dist/src/ui/auth.js +130 -0
  136. package/dist/src/ui/auth.js.map +1 -0
  137. package/dist/src/ui/dashboard_auth.d.ts +34 -0
  138. package/dist/src/ui/dashboard_auth.d.ts.map +1 -0
  139. package/dist/src/ui/dashboard_auth.js +57 -0
  140. package/dist/src/ui/dashboard_auth.js.map +1 -0
  141. package/dist/src/ui/define_config.d.ts +15 -0
  142. package/dist/src/ui/define_config.d.ts.map +1 -1
  143. package/dist/src/ui/define_config.js +4 -0
  144. package/dist/src/ui/define_config.js.map +1 -1
  145. package/dist/src/ui/index.d.ts +7 -1
  146. package/dist/src/ui/index.d.ts.map +1 -1
  147. package/dist/src/ui/index.js +6 -1
  148. package/dist/src/ui/index.js.map +1 -1
  149. package/dist/src/ui/login_page.d.ts +23 -0
  150. package/dist/src/ui/login_page.d.ts.map +1 -0
  151. package/dist/src/ui/login_page.js +170 -0
  152. package/dist/src/ui/login_page.js.map +1 -0
  153. package/dist/src/ui/session_cookie.d.ts +42 -0
  154. package/dist/src/ui/session_cookie.d.ts.map +1 -0
  155. package/dist/src/ui/session_cookie.js +97 -0
  156. package/dist/src/ui/session_cookie.js.map +1 -0
  157. package/dist/src/watchers/define_config.d.ts +115 -12
  158. package/dist/src/watchers/define_config.d.ts.map +1 -1
  159. package/dist/src/watchers/define_config.js +48 -11
  160. package/dist/src/watchers/define_config.js.map +1 -1
  161. package/dist/src/watchers/http_client_watcher.d.ts +91 -31
  162. package/dist/src/watchers/http_client_watcher.d.ts.map +1 -1
  163. package/dist/src/watchers/http_client_watcher.js +161 -53
  164. package/dist/src/watchers/http_client_watcher.js.map +1 -1
  165. package/dist/src/watchers/index.d.ts +9 -5
  166. package/dist/src/watchers/index.d.ts.map +1 -1
  167. package/dist/src/watchers/index.js +6 -2
  168. package/dist/src/watchers/index.js.map +1 -1
  169. package/dist/src/watchers/lucid_query_watcher.d.ts +20 -2
  170. package/dist/src/watchers/lucid_query_watcher.d.ts.map +1 -1
  171. package/dist/src/watchers/lucid_query_watcher.js +44 -10
  172. package/dist/src/watchers/lucid_query_watcher.js.map +1 -1
  173. package/dist/src/watchers/profiling_watcher.d.ts +144 -0
  174. package/dist/src/watchers/profiling_watcher.d.ts.map +1 -0
  175. package/dist/src/watchers/profiling_watcher.js +231 -0
  176. package/dist/src/watchers/profiling_watcher.js.map +1 -0
  177. package/dist/src/watchers/query_family_hash.d.ts +7 -2
  178. package/dist/src/watchers/query_family_hash.d.ts.map +1 -1
  179. package/dist/src/watchers/query_family_hash.js +8 -3
  180. package/dist/src/watchers/query_family_hash.js.map +1 -1
  181. package/dist/src/watchers/record.d.ts.map +1 -1
  182. package/dist/src/watchers/record.js +4 -0
  183. package/dist/src/watchers/record.js.map +1 -1
  184. package/dist/src/watchers/schedule_watcher.d.ts +143 -0
  185. package/dist/src/watchers/schedule_watcher.d.ts.map +1 -0
  186. package/dist/src/watchers/schedule_watcher.js +195 -0
  187. package/dist/src/watchers/schedule_watcher.js.map +1 -0
  188. package/dist/stubs/config/telescope.stub +0 -65
  189. package/dist/stubs/config/telescope_ai.stub +0 -36
  190. package/dist/stubs/config/telescope_alerts.stub +0 -47
  191. package/dist/stubs/config/telescope_mcp.stub +0 -0
  192. package/dist/stubs/config/telescope_ui.stub +0 -53
  193. package/dist/stubs/config/telescope_watchers.stub +0 -41
  194. package/dist/stubs/database/migrations/create_telescope_entries_table.stub +2 -2
  195. package/package.json +12 -2
@@ -0,0 +1,106 @@
1
+ import type { Entry } from '../entry.js';
2
+ import type { Diagnosis, ExceptionEntryContent } from './diagnoser.js';
3
+ import type { DiagnoseOptions } from './telescope_ai_diagnoser.js';
4
+ /**
5
+ * Default per-diagnosis wall-clock budget the coordinator enforces (ms). The
6
+ * on-demand MCP call and the alert path both wait AT MOST this long for a model
7
+ * response; a slower diagnosis loses the race and the caller proceeds without it
8
+ * (the underlying call keeps running and its result still lands in the cache for
9
+ * the next reader).
10
+ */
11
+ declare const DEFAULT_TIMEOUT_MS = 8000;
12
+ /**
13
+ * The minimal diagnoser surface the coordinator drives. {@link TelescopeAiDiagnoser}
14
+ * satisfies it; a test injects a fake. Declared STRUCTURALLY so the coordinator —
15
+ * and everything that imports it, including the core `index.ts` — has NO dependency
16
+ * on the Anthropic SDK. The host supplies the concrete provider; when it supplies
17
+ * none, `diagnoser` is `null` and the coordinator is an inert no-op.
18
+ */
19
+ export interface DiagnoserLike {
20
+ diagnose(entry: Entry<ExceptionEntryContent>, options?: DiagnoseOptions): Promise<Diagnosis | null>;
21
+ }
22
+ /**
23
+ * A compact, transport-friendly projection of a {@link Diagnosis} (drops the
24
+ * `cached` flag). This is what the alerter attaches to an alert payload, so a
25
+ * Slack/webhook consumer sees a stable, minimal shape.
26
+ */
27
+ export interface DiagnosisSummary {
28
+ cause: string;
29
+ fix: string;
30
+ confidence: string;
31
+ model: string;
32
+ }
33
+ /** Construction deps for {@link DiagnosisCoordinator}. */
34
+ export interface DiagnosisCoordinatorOptions {
35
+ /**
36
+ * The resolved diagnoser, or `null` when AI is unconfigured. When `null`,
37
+ * {@link DiagnosisCoordinator.isConfigured} is `false` and every diagnose call
38
+ * resolves to `null` — the feature is entirely inert with no configured provider.
39
+ */
40
+ diagnoser: DiagnoserLike | null;
41
+ /** Per-diagnosis timeout in ms. `<= 0` disables the timeout. Default 8000. */
42
+ timeoutMs?: number;
43
+ /** Failure log sink. Defaults to `console.warn`. */
44
+ logger?: (message: string) => void;
45
+ }
46
+ /**
47
+ * Central AI-diagnosis coordinator — the single seam the MCP `diagnose_exception`
48
+ * tool and the alerter's `new-exception` path both call. It owns the resolved
49
+ * diagnoser and adds three safety properties on top of it:
50
+ *
51
+ * - **Optional / lazy**: constructed with `diagnoser: null` when the host has not
52
+ * configured AI, in which case it is a pure no-op ({@link isConfigured} is
53
+ * `false` and diagnose calls return `null`). Nothing imports the LLM SDK through
54
+ * it — the provider supplies the concrete diagnoser.
55
+ * - **De-duped**: concurrent diagnose calls for the SAME exception family share one
56
+ * in-flight promise, so two simultaneous callers (an MCP request and an alert)
57
+ * never trigger two model calls. Completed diagnoses are additionally cached by
58
+ * the underlying diagnoser (by family hash).
59
+ * - **Fail-safe**: a per-call timeout bounds the wait, and ANY diagnoser failure
60
+ * (rejection or timeout) resolves to `null` and is logged — it NEVER throws into
61
+ * the host's MCP transport or alert flush path.
62
+ */
63
+ export declare class DiagnosisCoordinator {
64
+ private readonly diagnoser;
65
+ private readonly timeoutMs;
66
+ private readonly logger;
67
+ /** In-flight diagnoses keyed by family hash, so concurrent callers coalesce. */
68
+ private readonly inFlight;
69
+ constructor(options: DiagnosisCoordinatorOptions);
70
+ /** Whether a real diagnoser is wired. When `false`, all diagnose calls no-op to `null`. */
71
+ isConfigured(): boolean;
72
+ /**
73
+ * Diagnose one exception entry, timeout-bounded and fail-safe. Resolves to `null`
74
+ * when unconfigured or on any failure/timeout — NEVER throws. Concurrent calls for
75
+ * the same family hash share a single in-flight diagnosis.
76
+ */
77
+ diagnose(entry: Entry<ExceptionEntryContent>, options?: DiagnoseOptions): Promise<Diagnosis | null>;
78
+ /**
79
+ * Diagnose and render the result as markdown for the MCP `diagnose_exception`
80
+ * tool. Returns `null` when unconfigured or on failure, so the tool degrades to
81
+ * its "not configured / no result" message.
82
+ */
83
+ diagnoseMarkdown(entry: Entry<ExceptionEntryContent>, options?: DiagnoseOptions): Promise<string | null>;
84
+ /**
85
+ * Diagnose and project to a compact {@link DiagnosisSummary} for the alerter to
86
+ * attach to an alert payload. Returns `null` when unconfigured or on failure, so
87
+ * the alert simply omits the "Probable cause (AI)" section.
88
+ */
89
+ diagnoseSummary(entry: Entry<ExceptionEntryContent>, options?: DiagnoseOptions): Promise<DiagnosisSummary | null>;
90
+ /** Run one diagnosis under the timeout, swallowing every failure to `null`. */
91
+ private run;
92
+ /**
93
+ * Race `promise` against the configured timeout. On timeout resolves `null`; a
94
+ * late rejection of the underlying promise is swallowed so it never surfaces as
95
+ * an unhandled rejection after the timer has already won.
96
+ */
97
+ private withTimeout;
98
+ }
99
+ /**
100
+ * Render a {@link Diagnosis} as compact markdown for the MCP tool. Kept small and
101
+ * agent-facing: a heading, a confidence/model line, the probable cause, and the
102
+ * suggested fix.
103
+ */
104
+ export declare function formatDiagnosisMarkdown(diagnosis: Diagnosis): string;
105
+ export { DEFAULT_TIMEOUT_MS };
106
+ //# sourceMappingURL=diagnosis_coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosis_coordinator.d.ts","sourceRoot":"","sources":["../../../src/ai/diagnosis_coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE;;;;;;GAMG;AACH,QAAA,MAAM,kBAAkB,OAAQ,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CACN,KAAK,EAAE,KAAK,CAAC,qBAAqB,CAAC,EACnC,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,0DAA0D;AAC1D,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuB;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgD;gBAE7D,OAAO,EAAE,2BAA2B;IAMhD,2FAA2F;IAC3F,YAAY,IAAI,OAAO;IAIvB;;;;OAIG;IACG,QAAQ,CACZ,KAAK,EAAE,KAAK,CAAC,qBAAqB,CAAC,EACnC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAoB5B;;;;OAIG;IACG,gBAAgB,CACpB,KAAK,EAAE,KAAK,CAAC,qBAAqB,CAAC,EACnC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKzB;;;;OAIG;IACG,eAAe,CACnB,KAAK,EAAE,KAAK,CAAC,qBAAqB,CAAC,EACnC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAWnC,+EAA+E;YACjE,GAAG;IAajB;;;;OAIG;IACH,OAAO,CAAC,WAAW;CAcpB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAcpE;AAMD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Default per-diagnosis wall-clock budget the coordinator enforces (ms). The
3
+ * on-demand MCP call and the alert path both wait AT MOST this long for a model
4
+ * response; a slower diagnosis loses the race and the caller proceeds without it
5
+ * (the underlying call keeps running and its result still lands in the cache for
6
+ * the next reader).
7
+ */
8
+ const DEFAULT_TIMEOUT_MS = 8_000;
9
+ /**
10
+ * Central AI-diagnosis coordinator — the single seam the MCP `diagnose_exception`
11
+ * tool and the alerter's `new-exception` path both call. It owns the resolved
12
+ * diagnoser and adds three safety properties on top of it:
13
+ *
14
+ * - **Optional / lazy**: constructed with `diagnoser: null` when the host has not
15
+ * configured AI, in which case it is a pure no-op ({@link isConfigured} is
16
+ * `false` and diagnose calls return `null`). Nothing imports the LLM SDK through
17
+ * it — the provider supplies the concrete diagnoser.
18
+ * - **De-duped**: concurrent diagnose calls for the SAME exception family share one
19
+ * in-flight promise, so two simultaneous callers (an MCP request and an alert)
20
+ * never trigger two model calls. Completed diagnoses are additionally cached by
21
+ * the underlying diagnoser (by family hash).
22
+ * - **Fail-safe**: a per-call timeout bounds the wait, and ANY diagnoser failure
23
+ * (rejection or timeout) resolves to `null` and is logged — it NEVER throws into
24
+ * the host's MCP transport or alert flush path.
25
+ */
26
+ export class DiagnosisCoordinator {
27
+ diagnoser;
28
+ timeoutMs;
29
+ logger;
30
+ /** In-flight diagnoses keyed by family hash, so concurrent callers coalesce. */
31
+ inFlight = new Map();
32
+ constructor(options) {
33
+ this.diagnoser = options.diagnoser;
34
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
35
+ this.logger = options.logger ?? ((message) => console.warn(message));
36
+ }
37
+ /** Whether a real diagnoser is wired. When `false`, all diagnose calls no-op to `null`. */
38
+ isConfigured() {
39
+ return this.diagnoser !== null;
40
+ }
41
+ /**
42
+ * Diagnose one exception entry, timeout-bounded and fail-safe. Resolves to `null`
43
+ * when unconfigured or on any failure/timeout — NEVER throws. Concurrent calls for
44
+ * the same family hash share a single in-flight diagnosis.
45
+ */
46
+ async diagnose(entry, options = {}) {
47
+ const diagnoser = this.diagnoser;
48
+ if (diagnoser === null)
49
+ return null;
50
+ const familyHash = entry.familyHash;
51
+ if (familyHash !== null) {
52
+ const existing = this.inFlight.get(familyHash);
53
+ if (existing !== undefined)
54
+ return existing;
55
+ }
56
+ const task = this.run(diagnoser, entry, options);
57
+ if (familyHash !== null) {
58
+ this.inFlight.set(familyHash, task);
59
+ void task.finally(() => {
60
+ this.inFlight.delete(familyHash);
61
+ });
62
+ }
63
+ return task;
64
+ }
65
+ /**
66
+ * Diagnose and render the result as markdown for the MCP `diagnose_exception`
67
+ * tool. Returns `null` when unconfigured or on failure, so the tool degrades to
68
+ * its "not configured / no result" message.
69
+ */
70
+ async diagnoseMarkdown(entry, options = {}) {
71
+ const diagnosis = await this.diagnose(entry, options);
72
+ return diagnosis === null ? null : formatDiagnosisMarkdown(diagnosis);
73
+ }
74
+ /**
75
+ * Diagnose and project to a compact {@link DiagnosisSummary} for the alerter to
76
+ * attach to an alert payload. Returns `null` when unconfigured or on failure, so
77
+ * the alert simply omits the "Probable cause (AI)" section.
78
+ */
79
+ async diagnoseSummary(entry, options = {}) {
80
+ const diagnosis = await this.diagnose(entry, options);
81
+ if (diagnosis === null)
82
+ return null;
83
+ return {
84
+ cause: diagnosis.cause,
85
+ fix: diagnosis.fix,
86
+ confidence: diagnosis.confidence,
87
+ model: diagnosis.model,
88
+ };
89
+ }
90
+ /** Run one diagnosis under the timeout, swallowing every failure to `null`. */
91
+ async run(diagnoser, entry, options) {
92
+ try {
93
+ return await this.withTimeout(diagnoser.diagnose(entry, options));
94
+ }
95
+ catch (error) {
96
+ this.logger(`Telescope AI: diagnosis coordinator swallowed a failure: ${asMessage(error)}`);
97
+ return null;
98
+ }
99
+ }
100
+ /**
101
+ * Race `promise` against the configured timeout. On timeout resolves `null`; a
102
+ * late rejection of the underlying promise is swallowed so it never surfaces as
103
+ * an unhandled rejection after the timer has already won.
104
+ */
105
+ withTimeout(promise) {
106
+ if (!(this.timeoutMs > 0))
107
+ return promise;
108
+ // Attach a no-op catch so a rejection that lands AFTER the timeout wins the
109
+ // race can't become an unhandled rejection. The race still observes the
110
+ // original promise, so a rejection BEFORE the timeout still propagates to `run`.
111
+ promise.catch(() => undefined);
112
+ return Promise.race([
113
+ promise,
114
+ new Promise((resolve) => {
115
+ const timer = setTimeout(() => resolve(null), this.timeoutMs);
116
+ timer.unref?.();
117
+ }),
118
+ ]);
119
+ }
120
+ }
121
+ /**
122
+ * Render a {@link Diagnosis} as compact markdown for the MCP tool. Kept small and
123
+ * agent-facing: a heading, a confidence/model line, the probable cause, and the
124
+ * suggested fix.
125
+ */
126
+ export function formatDiagnosisMarkdown(diagnosis) {
127
+ return [
128
+ '## Probable cause (AI diagnosis)',
129
+ '',
130
+ `**Confidence:** ${diagnosis.confidence} · **Model:** ${diagnosis.model}${diagnosis.cached ? ' · cached' : ''}`,
131
+ '',
132
+ diagnosis.cause.trim() !== '' ? diagnosis.cause : '(no cause returned)',
133
+ '',
134
+ '### Suggested fix',
135
+ '',
136
+ diagnosis.fix.trim() !== '' ? diagnosis.fix : '(no fix returned)',
137
+ ].join('\n');
138
+ }
139
+ function asMessage(error) {
140
+ return error instanceof Error ? error.message : String(error);
141
+ }
142
+ export { DEFAULT_TIMEOUT_MS };
143
+ //# sourceMappingURL=diagnosis_coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosis_coordinator.js","sourceRoot":"","sources":["../../../src/ai/diagnosis_coordinator.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,15 @@
1
1
  import type { ResolvedTelescopeAiConfig } from './define_config.js';
2
- import type { DiagnosisStore } from './diagnosis_cache.js';
2
+ import { type DiagnosisStore } from './diagnosis_cache.js';
3
3
  import { TelescopeAiDiagnoser } from './telescope_ai_diagnoser.js';
4
4
  /**
5
- * Build a {@link TelescopeAiDiagnoser} from a resolved config, constructing the
6
- * real Anthropic SDK client from `config.apiKey`. Returns `null` when AI is
7
- * disabled or no key resolved — so the provider can wire a no-op without callers
5
+ * Build a {@link TelescopeAiDiagnoser} from a resolved config. Uses the
6
+ * host-supplied `config.client` verbatim when present (so a host can wire its own
7
+ * provider); otherwise constructs the real Anthropic SDK client from
8
+ * `config.apiKey`. Returns `null` when AI is disabled or neither a client nor a key
9
+ * resolved — so the provider can wire a no-op coordinator without callers
8
10
  * branching. A custom `cache` (e.g. Redis-backed) can be supplied for
9
- * cross-process diagnosis sharing.
11
+ * cross-process diagnosis sharing; otherwise an in-memory cache honouring
12
+ * `config.cacheTtlMs` is used.
10
13
  */
11
14
  export declare function createDiagnoser(config: ResolvedTelescopeAiConfig, options?: {
12
15
  cache?: DiagnosisStore;
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/ai/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAgC,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjG;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,yBAAyB,EACjC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC3E,oBAAoB,GAAG,IAAI,CAc7B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/ai/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAgC,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjG;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,yBAAyB,EACjC,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC3E,oBAAoB,GAAG,IAAI,CAuB7B"}
@@ -1,23 +1,35 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
+ import { DiagnosisCache } from './diagnosis_cache.js';
2
3
  import { TelescopeAiDiagnoser } from './telescope_ai_diagnoser.js';
3
4
  /**
4
- * Build a {@link TelescopeAiDiagnoser} from a resolved config, constructing the
5
- * real Anthropic SDK client from `config.apiKey`. Returns `null` when AI is
6
- * disabled or no key resolved — so the provider can wire a no-op without callers
5
+ * Build a {@link TelescopeAiDiagnoser} from a resolved config. Uses the
6
+ * host-supplied `config.client` verbatim when present (so a host can wire its own
7
+ * provider); otherwise constructs the real Anthropic SDK client from
8
+ * `config.apiKey`. Returns `null` when AI is disabled or neither a client nor a key
9
+ * resolved — so the provider can wire a no-op coordinator without callers
7
10
  * branching. A custom `cache` (e.g. Redis-backed) can be supplied for
8
- * cross-process diagnosis sharing.
11
+ * cross-process diagnosis sharing; otherwise an in-memory cache honouring
12
+ * `config.cacheTtlMs` is used.
9
13
  */
10
14
  export function createDiagnoser(config, options = {}) {
11
- if (!config.enabled || config.apiKey === null)
15
+ if (!config.enabled)
12
16
  return null;
13
- // The SDK's `Anthropic` instance satisfies the structural client contract.
14
- const client = new Anthropic({ apiKey: config.apiKey });
17
+ // Prefer the host-supplied provider; else build the SDK client from the key.
18
+ let client = config.client;
19
+ if (client === null) {
20
+ if (config.apiKey === null)
21
+ return null;
22
+ // The SDK's `Anthropic` instance satisfies the structural client contract.
23
+ client = new Anthropic({ apiKey: config.apiKey });
24
+ }
25
+ const cache = options.cache ??
26
+ (config.cacheTtlMs !== null ? new DiagnosisCache({ ttlMs: config.cacheTtlMs }) : undefined);
15
27
  return new TelescopeAiDiagnoser({
16
28
  client,
17
29
  model: config.model,
18
30
  maxTokens: config.maxTokens,
19
31
  enabled: true,
20
- ...(options.cache !== undefined ? { cache: options.cache } : {}),
32
+ ...(cache !== undefined ? { cache } : {}),
21
33
  ...(options.logger !== undefined ? { logger: options.logger } : {}),
22
34
  });
23
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/ai/factory.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAG1C,OAAO,EAAgC,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjG;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAiC,EACjC,UAA0E,EAAE;IAE5E,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE3D,2EAA2E;IAC3E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAuC,CAAC;IAE9F,OAAO,IAAI,oBAAoB,CAAC;QAC9B,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,IAAI;QACb,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/ai/factory.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,cAAc,EAAuB,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAgC,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEjG;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAiC,EACjC,UAA0E,EAAE;IAE5E,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAEjC,6EAA6E;IAC7E,IAAI,MAAM,GAAmC,MAAM,CAAC,MAAM,CAAC;IAC3D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACxC,2EAA2E;QAC3E,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAuC,CAAC;IAC1F,CAAC;IAED,MAAM,KAAK,GACT,OAAO,CAAC,KAAK;QACb,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAE9F,OAAO,IAAI,oBAAoB,CAAC;QAC9B,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,IAAI;QACb,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC,CAAC;AACL,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export declare const VERSION = "0.4.0";
2
+ export declare const VERSION = "0.5.0";
3
3
  export { TelescopeAiDiagnoser } from './telescope_ai_diagnoser.js';
4
4
  export type { AnthropicMessagesClient, DiagnoseOptions, TelescopeAiDiagnoserOptions, } from './telescope_ai_diagnoser.js';
5
5
  export { normalizeConfidence, parseDiagnosis } from './diagnoser.js';
@@ -9,6 +9,8 @@ export type { DiagnosisStore } from './diagnosis_cache.js';
9
9
  export { buildUserPrompt, STACK_FRAME_LIMIT, SYSTEM_PROMPT } from './prompt.js';
10
10
  export type { RelatedEntrySummary } from './prompt.js';
11
11
  export { createDiagnoser } from './factory.js';
12
+ export { DEFAULT_TIMEOUT_MS, DiagnosisCoordinator, formatDiagnosisMarkdown, } from './diagnosis_coordinator.js';
13
+ export type { DiagnoserLike, DiagnosisCoordinatorOptions, DiagnosisSummary, } from './diagnosis_coordinator.js';
12
14
  export { DEFAULT_MAX_TOKENS, DEFAULT_MODEL, defineConfig, resolveConfig, } from './define_config.js';
13
15
  export type { ResolvedTelescopeAiConfig, TelescopeAiConfig, } from './define_config.js';
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ai/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,eAAe,EACf,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAG5F,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChF,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ai/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EACV,uBAAuB,EACvB,eAAe,EACf,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrE,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAG5F,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChF,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,aAAa,EACb,2BAA2B,EAC3B,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /** Keep in sync with this package's `version` in package.json. */
2
- export const VERSION = '0.4.0';
2
+ export const VERSION = '0.5.0';
3
3
  // — diagnoser —
4
4
  export { TelescopeAiDiagnoser } from './telescope_ai_diagnoser.js';
5
5
  // — diagnosis model + parsing —
@@ -10,6 +10,8 @@ export { DEFAULT_DIAGNOSIS_CACHE_MAX, DEFAULT_DIAGNOSIS_TTL_MS, DiagnosisCache,
10
10
  export { buildUserPrompt, STACK_FRAME_LIMIT, SYSTEM_PROMPT } from './prompt.js';
11
11
  // — factory (constructs the real Anthropic client) —
12
12
  export { createDiagnoser } from './factory.js';
13
+ // — diagnosis coordinator (wires MCP + alerter) —
14
+ export { DEFAULT_TIMEOUT_MS, DiagnosisCoordinator, formatDiagnosisMarkdown, } from './diagnosis_coordinator.js';
13
15
  // — config —
14
16
  export { DEFAULT_MAX_TOKENS, DEFAULT_MODEL, defineConfig, resolveConfig, } from './define_config.js';
15
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ai/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,gBAAgB;AAChB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAOnE,gCAAgC;AAChC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrE,YAAY;AACZ,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAG9B,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGhF,qDAAqD;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,aAAa;AACb,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ai/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAE/B,gBAAgB;AAChB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAOnE,gCAAgC;AAChC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrE,YAAY;AACZ,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAG9B,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGhF,qDAAqD;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,kDAAkD;AAClD,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAOpC,aAAa;AACb,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,oBAAoB,CAAC"}
@@ -1,16 +1,29 @@
1
1
  import type { AlertChannel } from './alert_channel.js';
2
2
  /**
3
- * A single alerting rule. Ported (trimmed) from the aviary telescope core. This
4
- * Adonis port focuses on the two rules the headless `@adonis-agora/telescope` store can
5
- * back today:
3
+ * A single alerting rule. Ported (trimmed) from the aviary telescope core.
6
4
  *
7
- * - `new-exception` — fires the FIRST time an exception's `familyHash` is seen
8
- * within `window` (a genuinely NEW error family), and again
9
- * if the family re-appears after the window has elapsed (the
10
- * "re-occurring after being resolved" signal). Deduplication
11
- * is per-process via {@link NewExceptionTracker}.
12
- * - `exception-rate` — fires when `>= threshold` exception entries were recorded
13
- * in the trailing `window`.
5
+ * Two families of rules, evaluated by two complementary services:
6
+ *
7
+ * - `new-exception` / `exception-rate` — the exception-driven rules the
8
+ * {@link Alerter} evaluates over a freshly-polled batch of exception entries
9
+ * (fed by the {@link ExceptionPoller}). They fire-and-cooldown; they do NOT
10
+ * auto-resolve.
11
+ * - `new-exception` fires the FIRST time an exception's `familyHash` is seen
12
+ * within `window` (a genuinely NEW error family), and
13
+ * again if the family re-appears after the window elapses.
14
+ * Dedup is per-process via {@link NewExceptionTracker}.
15
+ * - `exception-rate` fires when `>= threshold` exception entries were recorded
16
+ * in the trailing `window`.
17
+ *
18
+ * - `metric-threshold` — the stateful rule the {@link AlerterService} evaluates on
19
+ * an interval over a trailing `window`, reusing the SAME windowed aggregation
20
+ * the dashboard shows (`summarizeStats`/`percentile` via `MetricsService`), so
21
+ * an alert means exactly what the metrics view shows. It RAISES when the
22
+ * computed metric first crosses `threshold` in the `comparator` direction and
23
+ * AUTO-RESOLVES when it clears — with dedup (no duplicate raise while active)
24
+ * and a post-resolve cooldown for flap control. Use it to page on
25
+ * "request p99 > 800ms" (a slow endpoint), "cache hit-rate < 0.8", or an
26
+ * exception spike (`exception-count >= N`).
14
27
  */
15
28
  export type AlertRule = {
16
29
  type: 'new-exception';
@@ -19,7 +32,31 @@ export type AlertRule = {
19
32
  type: 'exception-rate';
20
33
  window: string;
21
34
  threshold: number;
35
+ } | {
36
+ type: 'metric-threshold';
37
+ /** The computed metric to evaluate (see {@link AlertMetric}). */
38
+ metric: AlertMetric;
39
+ /** Trailing window to aggregate over (e.g. `'5m'`). */
40
+ window: string;
41
+ /** `gte` fires when value >= threshold; `lte` when value <= threshold. */
42
+ comparator: 'gte' | 'lte';
43
+ /** The threshold the metric is compared against. */
44
+ threshold: number;
45
+ /**
46
+ * Minimum samples in the window before a percentile/ratio rule can fire —
47
+ * guards a p99 page against a single slow request on a quiet host. Default
48
+ * 1. Ignored for `exception-count` (a count of 0 is a meaningful value that
49
+ * must be able to auto-resolve a firing spike).
50
+ */
51
+ minSamples?: number;
22
52
  };
53
+ /**
54
+ * The metrics a `metric-threshold` rule can evaluate. Each is derived from the
55
+ * SAME windowed aggregation the stats view uses (`summarizeStats`), so an alert
56
+ * means exactly what the dashboard shows. Latency metrics are in ms;
57
+ * `cache-hit-rate` is a ratio in [0, 1]; `exception-count` is a raw count.
58
+ */
59
+ export type AlertMetric = 'request-p95-ms' | 'request-p99-ms' | 'query-p95-ms' | 'query-p99-ms' | 'cache-hit-rate' | 'exception-count';
23
60
  /**
24
61
  * Rich exception context attached to a `new-exception` alert. Pulled from the
25
62
  * exception {@link Entry} that fired the rule. Absent on rate-rule alerts.
@@ -46,6 +83,22 @@ export interface ExceptionAlertContext {
46
83
  /** Exception entry id (for the dashboard deep link). */
47
84
  entryId: string;
48
85
  }
86
+ /**
87
+ * A compact AI probable-cause summary optionally attached to a `new-exception`
88
+ * alert by the {@link Alerter} when a diagnosis coordinator is configured. Kept
89
+ * structural (no dependency on the `ai` subpath) so alerting stays independent of
90
+ * whether AI is installed. Absent when AI is off or a diagnosis wasn't ready.
91
+ */
92
+ export interface AlertDiagnosis {
93
+ /** Likely root cause — what failed and why. */
94
+ cause: string;
95
+ /** A concrete, actionable suggested fix. */
96
+ fix: string;
97
+ /** The model's confidence (`high` / `medium` / `low`). */
98
+ confidence: string;
99
+ /** The model id that produced the diagnosis. */
100
+ model: string;
101
+ }
49
102
  /**
50
103
  * The shape delivered to every {@link AlertChannel} when a rule fires.
51
104
  */
@@ -59,8 +112,23 @@ export interface AlertPayload {
59
112
  firedAt: string;
60
113
  /** The reporting instance identifier. */
61
114
  instanceId: string;
62
- /** Rich context for `new-exception` alerts; absent for rate rules. */
115
+ /**
116
+ * Whether this notification RAISES an alert or RESOLVES a previously-raised one.
117
+ * Present on stateful `metric-threshold` alerts; absent (⇒ `'firing'`) on the
118
+ * fire-and-cooldown exception rules, which carry no resolve signal.
119
+ */
120
+ status?: 'firing' | 'resolved';
121
+ /** The evaluated metric — present on `metric-threshold` alerts. */
122
+ metric?: AlertMetric;
123
+ /** Rich context for `new-exception` alerts; absent for rate/metric rules. */
63
124
  exception?: ExceptionAlertContext;
125
+ /**
126
+ * AI probable-cause analysis, attached to a `new-exception` alert when a
127
+ * diagnosis coordinator is configured and produced a result within the grace
128
+ * window. Absent when AI is off or no diagnosis was ready. Channels render it as
129
+ * a "Probable cause (AI)" section; the raw webhook payload carries it verbatim.
130
+ */
131
+ diagnosis?: AlertDiagnosis;
64
132
  /** External dashboard URL when configured (lets channels build deep links). */
65
133
  dashboardUrl?: string;
66
134
  }
@@ -1 +1 @@
1
- {"version":3,"file":"alert_rule.d.ts","sourceRoot":"","sources":["../../../src/alerts/alert_rule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,kEAAkE;IAClE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iCAAiC;IACjC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uCAAuC;IACvC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,mEAAmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB"}
1
+ {"version":3,"file":"alert_rule.d.ts","sourceRoot":"","sources":["../../../src/alerts/alert_rule.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { Entry } from '../entry.js';
2
- import type { ResolvedAlerts } from './alert_rule.js';
2
+ import type { AlertDiagnosis, ResolvedAlerts } from './alert_rule.js';
3
3
  export interface AlerterDeps {
4
4
  alerts: ResolvedAlerts;
5
5
  /** Wall-clock seam (ms). Defaults to `Date.now`. */
@@ -8,6 +8,14 @@ export interface AlerterDeps {
8
8
  maxFamilies?: number;
9
9
  /** Failure log sink. Defaults to `console.warn`. */
10
10
  logger?: (message: string) => void;
11
+ /**
12
+ * Optional AI probable-cause hook. When present, a `new-exception` alert awaits
13
+ * it (fail-safe / time-bounded by the caller — usually the diagnosis
14
+ * coordinator) and attaches the result to the payload's `diagnosis` field. Omit
15
+ * (the default) and alerts behave exactly as before, with no AI section. Must
16
+ * never throw; the alerter additionally guards it.
17
+ */
18
+ diagnose?: (entry: Entry) => Promise<AlertDiagnosis | null>;
11
19
  }
12
20
  /**
13
21
  * Wires a source of exception {@link Entry} objects → rule evaluation → channels.
@@ -54,6 +62,8 @@ export declare class Alerter {
54
62
  /** Number of error families currently tracked (test/observability seam). */
55
63
  get trackedFamilies(): number;
56
64
  private evaluateNewException;
65
+ /** Run the optional diagnose hook, swallowing any failure to `null`. */
66
+ private safeDiagnose;
57
67
  private evaluateExceptionRate;
58
68
  /** Find a configured rule of `type` with its array index, or `null`. */
59
69
  private findRule;
@@ -1 +1 @@
1
- {"version":3,"file":"alerter.d.ts","sourceRoot":"","sources":["../../../src/alerts/alerter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAIV,cAAc,EACf,MAAM,iBAAiB,CAAC;AAOzB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,OAAO;IAaN,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgB;gBAEpB,IAAI,EAAE,WAAW;IAM9C;;;;OAIG;IACG,QAAQ,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBxD;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKvC,4EAA4E;IAC5E,IAAI,eAAe,IAAI,MAAM,CAE5B;YAEa,oBAAoB;YAyBpB,qBAAqB;IAiBnC,wEAAwE;IACxE,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,wBAAwB;IAmBhC;;;;OAIG;YACW,QAAQ;IAWtB,OAAO,CAAC,kBAAkB;CAM3B"}
1
+ {"version":3,"file":"alerter.d.ts","sourceRoot":"","sources":["../../../src/alerts/alerter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EACV,cAAc,EAId,cAAc,EACf,MAAM,iBAAiB,CAAC;AAOzB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAC;IACvB,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CAC7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,OAAO;IAaN,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,wEAAwE;IACxE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAgB;gBAEpB,IAAI,EAAE,WAAW;IAM9C;;;;OAIG;IACG,QAAQ,CAAC,gBAAgB,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBxD;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKvC,4EAA4E;IAC5E,IAAI,eAAe,IAAI,MAAM,CAE5B;YAEa,oBAAoB;IA+BlC,wEAAwE;YAC1D,YAAY;YAWZ,qBAAqB;IAiBnC,wEAAwE;IACxE,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,wBAAwB;IAmBhC;;;;OAIG;YACW,QAAQ;IAWtB,OAAO,CAAC,kBAAkB;CAM3B"}
@@ -97,7 +97,27 @@ export class Alerter {
97
97
  continue;
98
98
  this.lastFiredFamily.set(entry.familyHash, nowMs);
99
99
  const occurrences = occurrencesByFamily.get(entry.familyHash) ?? 1;
100
- await this.dispatch(this.buildNewExceptionPayload(result.rule, entry, occurrences, nowMs));
100
+ const payload = this.buildNewExceptionPayload(result.rule, entry, occurrences, nowMs);
101
+ // Optionally enrich with an AI probable-cause section when a diagnose hook is
102
+ // wired. The hook is expected to be fail-safe/time-bounded (the coordinator
103
+ // is); we guard it anyway so a diagnosis failure never blocks the alert.
104
+ const diagnosis = await this.safeDiagnose(entry);
105
+ if (diagnosis !== null)
106
+ payload.diagnosis = diagnosis;
107
+ await this.dispatch(payload);
108
+ }
109
+ }
110
+ /** Run the optional diagnose hook, swallowing any failure to `null`. */
111
+ async safeDiagnose(entry) {
112
+ const diagnose = this.deps.diagnose;
113
+ if (diagnose === undefined)
114
+ return null;
115
+ try {
116
+ return await diagnose(entry);
117
+ }
118
+ catch (error) {
119
+ this.logger(`Telescope alert diagnosis failed: ${asMessage(error)}`);
120
+ return null;
101
121
  }
102
122
  }
103
123
  async evaluateExceptionRate(nowMs) {