@poolzin/pool-bot 2026.3.7 → 2026.3.10

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 (150) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +147 -69
  3. package/dist/.buildstamp +1 -1
  4. package/dist/agents/error-classifier.js +251 -0
  5. package/dist/agents/skills/security.js +211 -0
  6. package/dist/build-info.json +3 -3
  7. package/dist/cli/cron-cli/register.cron-dashboard.js +339 -0
  8. package/dist/cli/cron-cli/register.js +2 -0
  9. package/dist/cli/errors.js +187 -0
  10. package/dist/cli/lazy-commands.example.js +113 -0
  11. package/dist/cli/lazy-commands.js +329 -0
  12. package/dist/cli/program/command-registry.js +26 -0
  13. package/dist/cli/program/register.maintenance.js +21 -0
  14. package/dist/cli/program/register.skills.js +4 -0
  15. package/dist/cli/program/register.subclis.js +9 -0
  16. package/dist/cli/swarm-cli/register.js +8 -0
  17. package/dist/cli/swarm-cli/register.swarm-status.js +488 -0
  18. package/dist/cli/telemetry-cli/register.js +10 -0
  19. package/dist/cli/telemetry-cli/register.telemetry-alerts.js +176 -0
  20. package/dist/cli/telemetry-cli/register.telemetry-metrics.js +323 -0
  21. package/dist/cli/telemetry-cli/register.telemetry-status.js +179 -0
  22. package/dist/commands/doctor-checks.js +498 -0
  23. package/dist/config/config.js +1 -0
  24. package/dist/config/secrets-integration.js +88 -0
  25. package/dist/context-engine/index.js +33 -0
  26. package/dist/context-engine/legacy.js +179 -0
  27. package/dist/context-engine/registry.js +86 -0
  28. package/dist/context-engine/summarizing.js +290 -0
  29. package/dist/context-engine/types.js +7 -0
  30. package/dist/cron/service/timer.js +18 -0
  31. package/dist/gateway/protocol/index.js +5 -2
  32. package/dist/gateway/protocol/schema/error-codes.js +1 -0
  33. package/dist/gateway/protocol/schema/swarm.js +80 -0
  34. package/dist/gateway/protocol/schema.js +1 -0
  35. package/dist/gateway/server-close.js +4 -0
  36. package/dist/gateway/server-constants.js +1 -0
  37. package/dist/gateway/server-cron.js +29 -0
  38. package/dist/gateway/server-maintenance.js +35 -2
  39. package/dist/gateway/server-methods/swarm.js +58 -0
  40. package/dist/gateway/server-methods/telemetry.js +71 -0
  41. package/dist/gateway/server-methods-list.js +8 -0
  42. package/dist/gateway/server-methods.js +9 -2
  43. package/dist/gateway/server.impl.js +33 -16
  44. package/dist/infra/abort-pattern.js +106 -0
  45. package/dist/infra/retry.js +96 -0
  46. package/dist/secrets/index.js +28 -0
  47. package/dist/secrets/resolver.js +185 -0
  48. package/dist/secrets/runtime.js +142 -0
  49. package/dist/secrets/types.js +11 -0
  50. package/dist/security/dangerous-tools.js +80 -0
  51. package/dist/security/types.js +12 -0
  52. package/dist/skills/commands.js +333 -0
  53. package/dist/skills/index.js +164 -0
  54. package/dist/skills/loader.js +282 -0
  55. package/dist/skills/parser.js +446 -0
  56. package/dist/skills/registry.js +394 -0
  57. package/dist/skills/security.js +312 -0
  58. package/dist/skills/types.js +21 -0
  59. package/dist/swarm/service.js +247 -0
  60. package/dist/telemetry/alert-engine.js +258 -0
  61. package/dist/telemetry/cron-instrumentation.js +49 -0
  62. package/dist/telemetry/gateway-instrumentation.js +80 -0
  63. package/dist/telemetry/instrumentation.js +66 -0
  64. package/dist/telemetry/service.js +345 -0
  65. package/dist/test-utils/index.js +219 -0
  66. package/dist/tui/components/assistant-message.js +6 -2
  67. package/dist/tui/components/hyperlink-markdown.js +32 -0
  68. package/dist/tui/components/searchable-select-list.js +12 -1
  69. package/dist/tui/components/user-message.js +6 -2
  70. package/dist/tui/index.js +611 -0
  71. package/dist/tui/theme/theme-detection.js +226 -0
  72. package/dist/tui/tui-command-handlers.js +20 -0
  73. package/dist/tui/tui-formatters.js +4 -3
  74. package/dist/tui/utils/ctrl-c-handler.js +67 -0
  75. package/dist/tui/utils/osc8-hyperlinks.js +208 -0
  76. package/dist/tui/utils/safe-stop.js +180 -0
  77. package/dist/tui/utils/session-key-utils.js +81 -0
  78. package/dist/tui/utils/text-sanitization.js +284 -0
  79. package/dist/utils/lru-cache.js +116 -0
  80. package/dist/utils/performance.js +199 -0
  81. package/dist/utils/retry.js +240 -0
  82. package/docs/INTEGRATION_PLAN.md +475 -0
  83. package/docs/INTEGRATION_SUMMARY.md +215 -0
  84. package/docs/MELHORIAS_IMPLEMENTADAS.md +228 -0
  85. package/docs/MELHORIAS_PROFISSIONAIS.md +282 -0
  86. package/docs/PLANO_ACAO_TUI.md +357 -0
  87. package/docs/PROGRESSO_TUI.md +66 -0
  88. package/docs/RELATORIO_FINAL.md +217 -0
  89. package/docs/diagnostico-shell-completion.md +265 -0
  90. package/docs/features/advanced-memory.md +585 -0
  91. package/docs/features/discord-components-v2.md +277 -0
  92. package/docs/features/swarm.md +100 -0
  93. package/docs/features/telemetry.md +284 -0
  94. package/docs/integrations/HEXSTRIKE_PLAN.md +796 -0
  95. package/docs/integrations/INTEGRATION_PLAN.md +744 -0
  96. package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
  97. package/docs/integrations/XYOPS_PLAN.md +978 -0
  98. package/docs/models/provider-infrastructure.md +400 -0
  99. package/docs/security/exec-approvals.md +294 -0
  100. package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
  101. package/docs/skills/SKILL.md +524 -0
  102. package/docs/skills.md +405 -0
  103. package/extensions/bluebubbles/package.json +1 -1
  104. package/extensions/copilot-proxy/package.json +1 -1
  105. package/extensions/diagnostics-otel/package.json +1 -1
  106. package/extensions/discord/package.json +1 -1
  107. package/extensions/feishu/package.json +1 -1
  108. package/extensions/google-antigravity-auth/package.json +1 -1
  109. package/extensions/google-gemini-cli-auth/package.json +1 -1
  110. package/extensions/googlechat/package.json +1 -1
  111. package/extensions/hexstrike-bridge/README.md +119 -0
  112. package/extensions/hexstrike-bridge/index.test.ts +247 -0
  113. package/extensions/hexstrike-bridge/index.ts +487 -0
  114. package/extensions/hexstrike-bridge/package.json +17 -0
  115. package/extensions/imessage/package.json +1 -1
  116. package/extensions/irc/package.json +1 -1
  117. package/extensions/line/package.json +1 -1
  118. package/extensions/llm-task/package.json +1 -1
  119. package/extensions/lobster/package.json +1 -1
  120. package/extensions/matrix/CHANGELOG.md +5 -0
  121. package/extensions/matrix/package.json +1 -1
  122. package/extensions/mattermost/package.json +1 -1
  123. package/extensions/mcp-server/index.ts +14 -0
  124. package/extensions/mcp-server/package.json +11 -0
  125. package/extensions/mcp-server/src/service.ts +540 -0
  126. package/extensions/memory-core/package.json +1 -1
  127. package/extensions/memory-lancedb/package.json +1 -1
  128. package/extensions/minimax-portal-auth/package.json +1 -1
  129. package/extensions/msteams/CHANGELOG.md +5 -0
  130. package/extensions/msteams/package.json +1 -1
  131. package/extensions/nextcloud-talk/package.json +1 -1
  132. package/extensions/nostr/CHANGELOG.md +5 -0
  133. package/extensions/nostr/package.json +1 -1
  134. package/extensions/open-prose/package.json +1 -1
  135. package/extensions/openai-codex-auth/package.json +1 -1
  136. package/extensions/signal/package.json +1 -1
  137. package/extensions/slack/package.json +1 -1
  138. package/extensions/telegram/package.json +1 -1
  139. package/extensions/tlon/package.json +1 -1
  140. package/extensions/twitch/CHANGELOG.md +5 -0
  141. package/extensions/twitch/package.json +1 -1
  142. package/extensions/voice-call/CHANGELOG.md +5 -0
  143. package/extensions/voice-call/package.json +1 -1
  144. package/extensions/whatsapp/package.json +1 -1
  145. package/extensions/zalo/CHANGELOG.md +5 -0
  146. package/extensions/zalo/package.json +1 -1
  147. package/extensions/zalouser/CHANGELOG.md +5 -0
  148. package/extensions/zalouser/package.json +1 -1
  149. package/package.json +8 -1
  150. package/skills/example-skill/SKILL.md +195 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Skill types and interfaces for PoolBot Skills System
3
+ * Compatible with agentskills.io ecosystem
4
+ *
5
+ * @module skills/types
6
+ */
7
+ /**
8
+ * Skill system error
9
+ */
10
+ export class SkillError extends Error {
11
+ code;
12
+ skillId;
13
+ cause;
14
+ constructor(code, message, skillId, cause) {
15
+ super(message);
16
+ this.code = code;
17
+ this.skillId = skillId;
18
+ this.cause = cause;
19
+ this.name = "SkillError";
20
+ }
21
+ }
@@ -0,0 +1,247 @@
1
+ // Agent Swarm Service - Multi-agent coordination and task distribution
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { createSubsystemLogger } from "../logging/subsystem.js";
5
+ const log = createSubsystemLogger("swarm");
6
+ const POOLBOT_DIR = join(process.env.HOME ?? "/tmp", ".poolbot");
7
+ const SWARM_STATE_FILE = join(POOLBOT_DIR, "swarm-state.json");
8
+ export class SwarmService {
9
+ swarms;
10
+ persistEnabled;
11
+ constructor(persistEnabled = true) {
12
+ this.swarms = new Map();
13
+ this.persistEnabled = persistEnabled;
14
+ this.loadFromDisk();
15
+ }
16
+ ensureDir() {
17
+ if (!existsSync(POOLBOT_DIR)) {
18
+ mkdirSync(POOLBOT_DIR, { recursive: true });
19
+ }
20
+ }
21
+ loadFromDisk() {
22
+ if (!this.persistEnabled)
23
+ return;
24
+ try {
25
+ this.ensureDir();
26
+ if (existsSync(SWARM_STATE_FILE)) {
27
+ const data = readFileSync(SWARM_STATE_FILE, "utf-8");
28
+ const parsed = JSON.parse(data);
29
+ for (const [id, swarm] of Object.entries(parsed)) {
30
+ this.swarms.set(id, swarm);
31
+ }
32
+ log.info(`Loaded ${this.swarms.size} swarms from disk`);
33
+ }
34
+ }
35
+ catch (error) {
36
+ log.error(`Failed to load swarm state: ${String(error)}`);
37
+ }
38
+ }
39
+ saveToDisk() {
40
+ if (!this.persistEnabled)
41
+ return;
42
+ try {
43
+ this.ensureDir();
44
+ const data = {};
45
+ for (const [id, swarm] of this.swarms) {
46
+ data[id] = swarm;
47
+ }
48
+ writeFileSync(SWARM_STATE_FILE, JSON.stringify(data, null, 2));
49
+ }
50
+ catch (error) {
51
+ log.error(`Failed to save swarm state: ${String(error)}`);
52
+ }
53
+ }
54
+ async list() {
55
+ const swarmList = Array.from(this.swarms.values()).map((swarm) => ({
56
+ id: swarm.id,
57
+ name: swarm.name,
58
+ status: swarm.status,
59
+ members: swarm.members.length,
60
+ tasks: swarm.tasks.length,
61
+ }));
62
+ return { swarms: swarmList };
63
+ }
64
+ async getStatus(swarmId) {
65
+ const swarm = this.swarms.get(swarmId);
66
+ return { swarm: swarm ?? null };
67
+ }
68
+ async create(params) {
69
+ const id = `swarm-${Date.now()}`;
70
+ const now = Date.now();
71
+ const swarm = {
72
+ id,
73
+ name: params.name,
74
+ description: params.description,
75
+ createdAt: now,
76
+ orchestratorAgentId: params.orchestratorAgentId,
77
+ members: [],
78
+ tasks: [],
79
+ strategy: params.strategy,
80
+ status: "active",
81
+ };
82
+ this.swarms.set(id, swarm);
83
+ this.saveToDisk();
84
+ log.info(`Created swarm: ${id} (${params.name})`);
85
+ return {
86
+ id,
87
+ name: params.name,
88
+ description: params.description,
89
+ strategy: params.strategy,
90
+ orchestratorAgentId: params.orchestratorAgentId,
91
+ createdAt: now,
92
+ status: "active",
93
+ };
94
+ }
95
+ async delete(swarmId) {
96
+ const deleted = this.swarms.delete(swarmId);
97
+ if (deleted) {
98
+ this.saveToDisk();
99
+ log.info(`Deleted swarm: ${swarmId}`);
100
+ }
101
+ return deleted;
102
+ }
103
+ async addMember(swarmId, member) {
104
+ const swarm = this.swarms.get(swarmId);
105
+ if (!swarm)
106
+ return false;
107
+ // Check if member already exists
108
+ const existingIndex = swarm.members.findIndex((m) => m.agentId === member.agentId);
109
+ if (existingIndex >= 0) {
110
+ swarm.members[existingIndex] = member;
111
+ }
112
+ else {
113
+ swarm.members.push(member);
114
+ }
115
+ this.saveToDisk();
116
+ return true;
117
+ }
118
+ async removeMember(swarmId, agentId) {
119
+ const swarm = this.swarms.get(swarmId);
120
+ if (!swarm)
121
+ return false;
122
+ const initialLength = swarm.members.length;
123
+ swarm.members = swarm.members.filter((m) => m.agentId !== agentId);
124
+ const removed = swarm.members.length < initialLength;
125
+ if (removed) {
126
+ this.saveToDisk();
127
+ }
128
+ return removed;
129
+ }
130
+ async addTask(swarmId, task) {
131
+ const swarm = this.swarms.get(swarmId);
132
+ if (!swarm)
133
+ return false;
134
+ swarm.tasks.push(task);
135
+ this.saveToDisk();
136
+ return true;
137
+ }
138
+ async removeTask(swarmId, taskId) {
139
+ const swarm = this.swarms.get(swarmId);
140
+ if (!swarm)
141
+ return false;
142
+ const initialLength = swarm.tasks.length;
143
+ swarm.tasks = swarm.tasks.filter((t) => t.id !== taskId);
144
+ const removed = swarm.tasks.length < initialLength;
145
+ if (removed) {
146
+ this.saveToDisk();
147
+ }
148
+ return removed;
149
+ }
150
+ async updateTaskStatus(swarmId, taskId, status, assignedTo) {
151
+ const swarm = this.swarms.get(swarmId);
152
+ if (!swarm)
153
+ return false;
154
+ const task = swarm.tasks.find((t) => t.id === taskId);
155
+ if (!task)
156
+ return false;
157
+ task.status = status;
158
+ if (assignedTo)
159
+ task.assignedTo = assignedTo;
160
+ if (status === "in_progress" && !task.startedAt)
161
+ task.startedAt = Date.now();
162
+ if (status === "completed" && !task.completedAt)
163
+ task.completedAt = Date.now();
164
+ this.saveToDisk();
165
+ return true;
166
+ }
167
+ async updateMemberStatus(swarmId, agentId, status, currentTaskId) {
168
+ const swarm = this.swarms.get(swarmId);
169
+ if (!swarm)
170
+ return false;
171
+ const member = swarm.members.find((m) => m.agentId === agentId);
172
+ if (!member)
173
+ return false;
174
+ member.status = status;
175
+ member.lastHeartbeatAt = Date.now();
176
+ if (currentTaskId !== undefined)
177
+ member.currentTaskId = currentTaskId;
178
+ if (status === "working" && currentTaskId) {
179
+ member.completedTasks++;
180
+ }
181
+ this.saveToDisk();
182
+ return true;
183
+ }
184
+ async updateSwarmStatus(swarmId, status) {
185
+ const swarm = this.swarms.get(swarmId);
186
+ if (!swarm)
187
+ return false;
188
+ swarm.status = status;
189
+ this.saveToDisk();
190
+ return true;
191
+ }
192
+ // Get or create default swarm for demo purposes
193
+ async getOrCreateDefaultSwarm() {
194
+ const defaultId = "swarm-default";
195
+ let swarm = this.swarms.get(defaultId);
196
+ if (!swarm) {
197
+ const now = Date.now();
198
+ swarm = {
199
+ id: defaultId,
200
+ name: "Default Swarm",
201
+ description: "Default agent swarm for task coordination",
202
+ createdAt: now,
203
+ orchestratorAgentId: "main",
204
+ members: [
205
+ {
206
+ agentId: "agent-1",
207
+ sessionKey: "swarm/agent-1",
208
+ status: "idle",
209
+ capabilities: ["typescript", "review", "coding"],
210
+ joinedAt: now,
211
+ lastHeartbeatAt: now,
212
+ completedTasks: 0,
213
+ failedTasks: 0,
214
+ },
215
+ ],
216
+ tasks: [],
217
+ strategy: "capability_match",
218
+ status: "active",
219
+ };
220
+ this.swarms.set(defaultId, swarm);
221
+ this.saveToDisk();
222
+ }
223
+ return swarm;
224
+ }
225
+ // Get swarm statistics
226
+ async getStats(swarmId) {
227
+ const swarm = this.swarms.get(swarmId);
228
+ if (!swarm)
229
+ return null;
230
+ const activeMembers = swarm.members.filter((m) => m.status === "idle" || m.status === "working").length;
231
+ const completedTasks = swarm.tasks.filter((t) => t.status === "completed").length;
232
+ const failedTasks = swarm.tasks.filter((t) => t.status === "failed").length;
233
+ const pendingTasks = swarm.tasks.filter((t) => t.status === "pending").length;
234
+ const inProgressTasks = swarm.tasks.filter((t) => t.status === "in_progress").length;
235
+ return {
236
+ totalMembers: swarm.members.length,
237
+ activeMembers,
238
+ totalTasks: swarm.tasks.length,
239
+ completedTasks,
240
+ failedTasks,
241
+ pendingTasks,
242
+ inProgressTasks,
243
+ };
244
+ }
245
+ }
246
+ // Singleton instance
247
+ export const swarmService = new SwarmService();
@@ -0,0 +1,258 @@
1
+ import { createSubsystemLogger } from "../logging/subsystem.js";
2
+ const log = createSubsystemLogger("telemetry:alerts");
3
+ export class AlertEngine {
4
+ rules = new Map();
5
+ states = new Map();
6
+ onAlertCallback;
7
+ constructor(rules = []) {
8
+ for (const rule of rules) {
9
+ this.addRule(rule);
10
+ }
11
+ }
12
+ addRule(rule) {
13
+ this.rules.set(rule.id, rule);
14
+ this.states.set(rule.id, { consecutiveCount: 0 });
15
+ log.debug(`Added alert rule: ${rule.id} (${rule.name})`);
16
+ }
17
+ removeRule(ruleId) {
18
+ this.states.delete(ruleId);
19
+ return this.rules.delete(ruleId);
20
+ }
21
+ getRules() {
22
+ return Array.from(this.rules.values());
23
+ }
24
+ onAlert(callback) {
25
+ this.onAlertCallback = callback;
26
+ }
27
+ evaluate(snapshot) {
28
+ const alerts = [];
29
+ const now = Date.now();
30
+ for (const rule of this.rules.values()) {
31
+ if (!rule.enabled)
32
+ continue;
33
+ const alert = this.evaluateRule(rule, snapshot, now);
34
+ if (alert) {
35
+ alerts.push(alert);
36
+ this.onAlertCallback?.(alert);
37
+ }
38
+ }
39
+ return alerts;
40
+ }
41
+ evaluateRule(rule, snapshot, now) {
42
+ const state = this.states.get(rule.id);
43
+ if (!state)
44
+ return null;
45
+ // Find matching metrics
46
+ const matchingMetrics = snapshot.metrics.filter((m) => {
47
+ if (m.name !== rule.threshold.metric)
48
+ return false;
49
+ if (rule.attributes) {
50
+ for (const [key, value] of Object.entries(rule.attributes)) {
51
+ if (m.attributes?.[key] !== value)
52
+ return false;
53
+ }
54
+ }
55
+ return true;
56
+ });
57
+ if (matchingMetrics.length === 0) {
58
+ // Reset state if no matching metrics
59
+ state.consecutiveCount = 0;
60
+ state.triggeredAt = undefined;
61
+ return null;
62
+ }
63
+ // Check if any metric violates threshold
64
+ let violated = false;
65
+ let violatingMetric = null;
66
+ for (const metric of matchingMetrics) {
67
+ if (this.checkThreshold(metric.value, rule.threshold)) {
68
+ violated = true;
69
+ violatingMetric = metric;
70
+ break;
71
+ }
72
+ }
73
+ if (!violated) {
74
+ // Reset state if threshold no longer violated
75
+ state.consecutiveCount = 0;
76
+ state.triggeredAt = undefined;
77
+ return null;
78
+ }
79
+ // Track consecutive violations
80
+ state.consecutiveCount++;
81
+ if (!state.triggeredAt) {
82
+ state.triggeredAt = now;
83
+ }
84
+ // Check duration requirement
85
+ const durationMs = rule.threshold.durationMs ?? 0;
86
+ if (now - state.triggeredAt < durationMs) {
87
+ return null; // Haven't met duration requirement yet
88
+ }
89
+ // Check cooldown
90
+ const cooldownMs = rule.threshold.cooldownMs ?? 60000;
91
+ if (state.lastAlertAt && now - state.lastAlertAt < cooldownMs) {
92
+ return null; // Still in cooldown
93
+ }
94
+ // Trigger alert
95
+ state.lastAlertAt = now;
96
+ return {
97
+ id: `${rule.id}-${now}`,
98
+ ruleId: rule.id,
99
+ name: rule.name,
100
+ severity: rule.severity,
101
+ metric: rule.threshold.metric,
102
+ value: violatingMetric.value,
103
+ threshold: rule.threshold.value,
104
+ operator: rule.threshold.operator,
105
+ timestamp: now,
106
+ message: this.buildAlertMessage(rule, violatingMetric.value),
107
+ attributes: violatingMetric.attributes,
108
+ };
109
+ }
110
+ checkThreshold(value, threshold) {
111
+ switch (threshold.operator) {
112
+ case "gt":
113
+ return value > threshold.value;
114
+ case "gte":
115
+ return value >= threshold.value;
116
+ case "lt":
117
+ return value < threshold.value;
118
+ case "lte":
119
+ return value <= threshold.value;
120
+ case "eq":
121
+ return value === threshold.value;
122
+ default:
123
+ return false;
124
+ }
125
+ }
126
+ buildAlertMessage(rule, value) {
127
+ const opMap = {
128
+ gt: ">",
129
+ gte: ">=",
130
+ lt: "<",
131
+ lte: "<=",
132
+ eq: "=",
133
+ };
134
+ return `${rule.name}: ${rule.threshold.metric} = ${value} ${opMap[rule.threshold.operator]} ${rule.threshold.value}`;
135
+ }
136
+ }
137
+ // Predefined alert rules for common Pool Bot scenarios
138
+ export const defaultAlertRules = [
139
+ // Cron Job Alerts
140
+ {
141
+ id: "cron-high-failure-rate",
142
+ name: "High Cron Job Failure Rate",
143
+ description: "Triggers when cron job failures exceed 3 in 5 minutes",
144
+ enabled: true,
145
+ severity: "warning",
146
+ threshold: {
147
+ metric: "poolbot.cron.job_failures",
148
+ operator: "gt",
149
+ value: 3,
150
+ durationMs: 300000, // 5 minutes
151
+ cooldownMs: 600000, // 10 minutes
152
+ },
153
+ },
154
+ {
155
+ id: "cron-slow-jobs",
156
+ name: "Slow Cron Job Execution",
157
+ description: "Triggers when cron job duration exceeds 60 seconds",
158
+ enabled: true,
159
+ severity: "info",
160
+ threshold: {
161
+ metric: "poolbot.cron.job_duration_ms",
162
+ operator: "gt",
163
+ value: 60000,
164
+ durationMs: 60000, // 1 minute
165
+ cooldownMs: 300000, // 5 minutes
166
+ },
167
+ },
168
+ {
169
+ id: "cron-job-stuck",
170
+ name: "Cron Job Possibly Stuck",
171
+ description: "Triggers when cron job duration exceeds 5 minutes",
172
+ enabled: true,
173
+ severity: "critical",
174
+ threshold: {
175
+ metric: "poolbot.cron.job_duration_ms",
176
+ operator: "gt",
177
+ value: 300000,
178
+ durationMs: 60000,
179
+ cooldownMs: 300000,
180
+ },
181
+ },
182
+ // Security Scan Alerts (HexStrike)
183
+ {
184
+ id: "security-scan-failures",
185
+ name: "Security Scan Failures",
186
+ description: "Triggers when security scans fail",
187
+ enabled: true,
188
+ severity: "warning",
189
+ threshold: {
190
+ metric: "poolbot.security.scan_failures",
191
+ operator: "gt",
192
+ value: 2,
193
+ durationMs: 300000,
194
+ cooldownMs: 600000,
195
+ },
196
+ },
197
+ {
198
+ id: "security-high-latency",
199
+ name: "HexStrike API High Latency",
200
+ description: "Triggers when HexStrike API latency exceeds 5 seconds",
201
+ enabled: true,
202
+ severity: "info",
203
+ threshold: {
204
+ metric: "poolbot.security.api_latency_ms",
205
+ operator: "gt",
206
+ value: 5000,
207
+ durationMs: 120000,
208
+ cooldownMs: 300000,
209
+ },
210
+ },
211
+ // Swarm Alerts
212
+ {
213
+ id: "swarm-high-failure-rate",
214
+ name: "Swarm Task Failure Rate",
215
+ description: "Triggers when swarm tasks fail frequently",
216
+ enabled: true,
217
+ severity: "warning",
218
+ threshold: {
219
+ metric: "poolbot.swarm.task_failures",
220
+ operator: "gt",
221
+ value: 5,
222
+ durationMs: 600000,
223
+ cooldownMs: 600000,
224
+ },
225
+ },
226
+ // Gateway Health Alerts
227
+ {
228
+ id: "gateway-high-memory",
229
+ name: "High Gateway Memory Usage",
230
+ description: "Triggers when gateway memory usage exceeds 512MB",
231
+ enabled: true,
232
+ severity: "critical",
233
+ threshold: {
234
+ metric: "poolbot.gateway.memory_usage_bytes",
235
+ operator: "gt",
236
+ value: 536870912, // 512MB
237
+ durationMs: 120000,
238
+ cooldownMs: 300000,
239
+ },
240
+ },
241
+ {
242
+ id: "gateway-high-cpu",
243
+ name: "High Gateway CPU Usage",
244
+ description: "Triggers when gateway CPU usage exceeds 80%",
245
+ enabled: true,
246
+ severity: "warning",
247
+ threshold: {
248
+ metric: "poolbot.gateway.cpu_usage_percent",
249
+ operator: "gt",
250
+ value: 80,
251
+ durationMs: 180000,
252
+ cooldownMs: 300000,
253
+ },
254
+ },
255
+ ];
256
+ export function createAlertEngine(customRules) {
257
+ return new AlertEngine([...defaultAlertRules, ...(customRules ?? [])]);
258
+ }
@@ -0,0 +1,49 @@
1
+ import { recordCounter, recordGauge, recordHistogram, withInstrumentation, } from "../telemetry/instrumentation.js";
2
+ export function instrumentCronOperation(operation, fn, attributes) {
3
+ return withInstrumentation({
4
+ operation,
5
+ component: "cron",
6
+ attributes,
7
+ }, fn);
8
+ }
9
+ export function recordCronJobExecution(metrics) {
10
+ recordCounter("poolbot.cron.jobs_executed", 1, {
11
+ job_id: metrics.jobId,
12
+ job_name: metrics.jobName,
13
+ schedule: metrics.schedule,
14
+ status: metrics.success ? "success" : "failure",
15
+ });
16
+ recordHistogram("poolbot.cron.job_duration_ms", metrics.durationMs, {
17
+ job_id: metrics.jobId,
18
+ job_name: metrics.jobName,
19
+ });
20
+ if (!metrics.success) {
21
+ recordCounter("poolbot.cron.job_failures", 1, {
22
+ job_id: metrics.jobId,
23
+ job_name: metrics.jobName,
24
+ error_type: metrics.errorType ?? "unknown",
25
+ });
26
+ }
27
+ }
28
+ export function recordCronJobScheduled(jobId, jobName, schedule) {
29
+ recordCounter("poolbot.cron.jobs_scheduled", 1, {
30
+ job_id: jobId,
31
+ job_name: jobName,
32
+ schedule,
33
+ });
34
+ }
35
+ export function recordCronJobCancelled(jobId, jobName) {
36
+ recordCounter("poolbot.cron.jobs_cancelled", 1, {
37
+ job_id: jobId,
38
+ job_name: jobName,
39
+ });
40
+ }
41
+ export function updateActiveCronJobsGauge(count) {
42
+ recordGauge("poolbot.cron.active_jobs", count);
43
+ }
44
+ export function recordCronQueueDepth(depth) {
45
+ recordGauge("poolbot.cron.queue_depth", depth);
46
+ }
47
+ export function recordCronOverdueJobs(count) {
48
+ recordGauge("poolbot.cron.overdue_jobs", count);
49
+ }
@@ -0,0 +1,80 @@
1
+ import { recordCounter, recordHistogram, withInstrumentation, } from "../telemetry/instrumentation.js";
2
+ export function instrumentGatewayRequest(operation, fn, attributes) {
3
+ return withInstrumentation({
4
+ operation,
5
+ component: "gateway",
6
+ attributes,
7
+ }, fn);
8
+ }
9
+ export function recordGatewayRequest(metrics) {
10
+ recordCounter("poolbot.gateway.requests", 1, {
11
+ method: metrics.method,
12
+ path: metrics.path,
13
+ status_code: String(metrics.statusCode),
14
+ });
15
+ recordHistogram("poolbot.gateway.request_duration_ms", metrics.durationMs, {
16
+ method: metrics.method,
17
+ path: metrics.path,
18
+ });
19
+ if (metrics.statusCode >= 500) {
20
+ recordCounter("poolbot.gateway.server_errors", 1, {
21
+ path: metrics.path,
22
+ });
23
+ }
24
+ else if (metrics.statusCode >= 400) {
25
+ recordCounter("poolbot.gateway.client_errors", 1, {
26
+ path: metrics.path,
27
+ status_code: String(metrics.statusCode),
28
+ });
29
+ }
30
+ }
31
+ export function createTelemetryMiddleware() {
32
+ return (req, res, next) => {
33
+ const startTime = Date.now();
34
+ const path = req.url ?? "/unknown";
35
+ const method = req.method ?? "UNKNOWN";
36
+ recordCounter("poolbot.gateway.active_requests", 1, {
37
+ method,
38
+ path,
39
+ });
40
+ const originalEnd = res.end.bind(res);
41
+ res.end = function (...args) {
42
+ const duration = Date.now() - startTime;
43
+ recordCounter("poolbot.gateway.active_requests", -1, {
44
+ method,
45
+ path,
46
+ });
47
+ recordGatewayRequest({
48
+ method,
49
+ path,
50
+ statusCode: res.statusCode,
51
+ durationMs: duration,
52
+ userAgent: req.headers["user-agent"],
53
+ });
54
+ return originalEnd(...args);
55
+ };
56
+ next();
57
+ };
58
+ }
59
+ export function instrumentWebSocketConnection(connectionId) {
60
+ recordCounter("poolbot.gateway.ws.connections", 1, {
61
+ connection_id: connectionId,
62
+ });
63
+ }
64
+ export function recordWebSocketMessage(connectionId, messageType, sizeBytes) {
65
+ recordCounter("poolbot.gateway.ws.messages", 1, {
66
+ connection_id: connectionId,
67
+ message_type: messageType,
68
+ });
69
+ recordHistogram("poolbot.gateway.ws.message_size_bytes", sizeBytes, {
70
+ message_type: messageType,
71
+ });
72
+ }
73
+ export function recordWebSocketDisconnection(connectionId, durationMs) {
74
+ recordCounter("poolbot.gateway.ws.disconnections", 1, {
75
+ connection_id: connectionId,
76
+ });
77
+ recordHistogram("poolbot.gateway.ws.connection_duration_ms", durationMs, {
78
+ connection_id: connectionId,
79
+ });
80
+ }