@johpaz/hive-core 1.0.7 → 1.0.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 (53) hide show
  1. package/package.json +10 -9
  2. package/src/agent/ethics.ts +70 -68
  3. package/src/agent/index.ts +48 -17
  4. package/src/agent/providers/index.ts +11 -5
  5. package/src/agent/soul.ts +19 -15
  6. package/src/agent/user.ts +19 -15
  7. package/src/agent/workspace.ts +6 -6
  8. package/src/agents/index.ts +4 -0
  9. package/src/agents/inter-agent-bus.test.ts +264 -0
  10. package/src/agents/inter-agent-bus.ts +279 -0
  11. package/src/agents/registry.test.ts +275 -0
  12. package/src/agents/registry.ts +273 -0
  13. package/src/agents/router.test.ts +229 -0
  14. package/src/agents/router.ts +251 -0
  15. package/src/agents/team-coordinator.test.ts +401 -0
  16. package/src/agents/team-coordinator.ts +480 -0
  17. package/src/canvas/canvas-manager.test.ts +159 -0
  18. package/src/canvas/canvas-manager.ts +219 -0
  19. package/src/canvas/canvas-tools.ts +189 -0
  20. package/src/canvas/index.ts +2 -0
  21. package/src/channels/whatsapp.ts +12 -12
  22. package/src/config/loader.ts +12 -9
  23. package/src/events/event-bus.test.ts +98 -0
  24. package/src/events/event-bus.ts +171 -0
  25. package/src/gateway/server.ts +131 -35
  26. package/src/index.ts +9 -1
  27. package/src/multi-agent/manager.ts +12 -12
  28. package/src/plugins/api.ts +129 -0
  29. package/src/plugins/index.ts +2 -0
  30. package/src/plugins/loader.test.ts +285 -0
  31. package/src/plugins/loader.ts +363 -0
  32. package/src/resilience/circuit-breaker.test.ts +129 -0
  33. package/src/resilience/circuit-breaker.ts +223 -0
  34. package/src/security/google-chat.test.ts +219 -0
  35. package/src/security/google-chat.ts +269 -0
  36. package/src/security/index.ts +5 -0
  37. package/src/security/pairing.test.ts +302 -0
  38. package/src/security/pairing.ts +250 -0
  39. package/src/security/rate-limit.test.ts +239 -0
  40. package/src/security/rate-limit.ts +270 -0
  41. package/src/security/signal.test.ts +92 -0
  42. package/src/security/signal.ts +321 -0
  43. package/src/state/store.test.ts +190 -0
  44. package/src/state/store.ts +310 -0
  45. package/src/storage/sqlite.ts +3 -3
  46. package/src/tools/cron.ts +42 -2
  47. package/src/tools/dynamic-registry.test.ts +226 -0
  48. package/src/tools/dynamic-registry.ts +258 -0
  49. package/src/tools/fs.test.ts +127 -0
  50. package/src/tools/fs.ts +364 -0
  51. package/src/tools/index.ts +1 -0
  52. package/src/tools/read.ts +23 -19
  53. package/src/utils/logger.ts +112 -33
@@ -1,4 +1,4 @@
1
- import * as fs from "node:fs";
1
+ import { mkdirSync, unlinkSync, renameSync, existsSync } from "node:fs";
2
2
  import * as path from "node:path";
3
3
 
4
4
  export type LogLevel = "debug" | "info" | "warn" | "error";
@@ -12,6 +12,18 @@ export interface LoggerConfig {
12
12
  console: boolean;
13
13
  }
14
14
 
15
+ export interface LogMeta extends Record<string, unknown> {
16
+ correlationId?: string;
17
+ sessionId?: string;
18
+ userId?: string;
19
+ agentId?: string;
20
+ channel?: string;
21
+ toolName?: string;
22
+ duration?: number;
23
+ error?: string;
24
+ stack?: string;
25
+ }
26
+
15
27
  const LOG_LEVELS: Record<LogLevel, number> = {
16
28
  debug: 0,
17
29
  info: 1,
@@ -78,16 +90,18 @@ function formatTimestamp(): string {
78
90
  return new Date().toISOString();
79
91
  }
80
92
 
81
- function formatMessage(level: LogLevel, message: string, meta?: unknown): string {
93
+ function formatMessage(level: LogLevel, message: string, meta?: unknown, correlationId?: string): string {
82
94
  const timestamp = formatTimestamp();
95
+ const corrStr = correlationId ? ` [${correlationId.slice(0, 8)}]` : "";
83
96
  const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
84
- return `[${timestamp}] [${level.toUpperCase()}] ${message}${metaStr}`;
97
+ return `[${timestamp}]${corrStr} [${level.toUpperCase()}] ${message}${metaStr}`;
85
98
  }
86
99
 
87
100
  export class Logger {
88
101
  private config: LoggerConfig;
89
102
  private logFile: string | null = null;
90
103
  private currentSize = 0;
104
+ private correlationContext: LogMeta = {};
91
105
 
92
106
  constructor(config: Partial<LoggerConfig> = {}) {
93
107
  this.config = {
@@ -102,20 +116,35 @@ export class Logger {
102
116
  this.initLogFile();
103
117
  }
104
118
 
119
+ setCorrelationContext(context: Partial<LogMeta>): void {
120
+ this.correlationContext = { ...this.correlationContext, ...context };
121
+ }
122
+
123
+ clearCorrelationContext(): void {
124
+ this.correlationContext = {};
125
+ }
126
+
127
+ getCorrelationId(): string | undefined {
128
+ return this.correlationContext.correlationId;
129
+ }
130
+
131
+ withCorrelationId(id: string): this {
132
+ this.correlationContext.correlationId = id;
133
+ return this;
134
+ }
135
+
105
136
  private initLogFile(): void {
106
137
  const logDir = expandPath(this.config.dir);
107
-
138
+
108
139
  try {
109
- if (!fs.existsSync(logDir)) {
110
- fs.mkdirSync(logDir, { recursive: true });
140
+ if (!existsSync(logDir)) {
141
+ mkdirSync(logDir, { recursive: true });
111
142
  }
112
143
 
113
144
  this.logFile = path.join(logDir, `hive-${new Date().toISOString().split("T")[0]}.log`);
114
-
115
- if (fs.existsSync(this.logFile)) {
116
- const stats = fs.statSync(this.logFile);
117
- this.currentSize = stats.size;
118
- }
145
+
146
+ const file = Bun.file(this.logFile);
147
+ this.currentSize = file.size ?? 0;
119
148
  } catch {
120
149
  this.logFile = null;
121
150
  }
@@ -129,13 +158,31 @@ export class Logger {
129
158
  if (!this.config.console) return;
130
159
 
131
160
  const color = COLORS[level];
132
- const displayMeta = this.config.redactSensitive && meta ? redact(meta) : meta;
133
- const metaStr = displayMeta ? ` ${JSON.stringify(displayMeta)}` : "";
161
+ const mergedMeta = this.mergeMeta(meta);
162
+ const displayMeta = this.config.redactSensitive && mergedMeta ? redact(mergedMeta) : mergedMeta;
163
+ const metaStr = displayMeta && Object.keys(displayMeta as object).length > 0
164
+ ? ` ${JSON.stringify(displayMeta)}`
165
+ : "";
134
166
 
135
167
  const prefix = `${COLORS.dim}${formatTimestamp()}${COLORS.reset}`;
168
+ const corrStr = this.correlationContext.correlationId
169
+ ? ` ${COLORS.dim}[${this.correlationContext.correlationId.slice(0, 8)}]${COLORS.reset}`
170
+ : "";
136
171
  const levelStr = `${color}${COLORS.bright}[${level.toUpperCase().padEnd(5)}]${COLORS.reset}`;
137
-
138
- console.log(`${prefix} ${levelStr} ${message}${metaStr}`);
172
+
173
+ console.log(`${prefix}${corrStr} ${levelStr} ${message}${metaStr}`);
174
+ }
175
+
176
+ private mergeMeta(meta?: unknown): LogMeta | undefined {
177
+ if (!meta && Object.keys(this.correlationContext).length === 0) return undefined;
178
+
179
+ const contextWithoutCorrId = { ...this.correlationContext };
180
+ delete contextWithoutCorrId.correlationId;
181
+
182
+ if (!meta) return contextWithoutCorrId;
183
+ if (typeof meta !== "object") return meta as LogMeta;
184
+
185
+ return { ...contextWithoutCorrId, ...(meta as LogMeta) };
139
186
  }
140
187
 
141
188
  private writeToFile(message: string): void {
@@ -144,12 +191,15 @@ export class Logger {
144
191
  try {
145
192
  const line = message + "\n";
146
193
  const bytes = Buffer.byteLength(line);
147
-
194
+
148
195
  if (this.currentSize + bytes > this.config.maxSizeMB * 1024 * 1024) {
149
196
  this.rotateLogs();
150
197
  }
151
198
 
152
- fs.appendFileSync(this.logFile, line);
199
+ // Use sync append for logging reliability
200
+ const encoder = new TextEncoder();
201
+ const data = encoder.encode(line);
202
+ Bun.write(this.logFile, data).catch(() => { });
153
203
  this.currentSize += bytes;
154
204
  } catch {
155
205
  // Silently fail if we can't write to log file
@@ -165,13 +215,13 @@ export class Logger {
165
215
  for (let i = this.config.maxFiles - 1; i >= 1; i--) {
166
216
  const oldFile = path.join(logDir, `${baseName}.${i}.log`);
167
217
  const newFile = path.join(logDir, `${baseName}.${i + 1}.log`);
168
-
218
+
169
219
  try {
170
- if (fs.existsSync(oldFile)) {
220
+ if (existsSync(oldFile)) {
171
221
  if (i === this.config.maxFiles - 1) {
172
- fs.unlinkSync(oldFile);
222
+ unlinkSync(oldFile);
173
223
  } else {
174
- fs.renameSync(oldFile, newFile);
224
+ renameSync(oldFile, newFile);
175
225
  }
176
226
  }
177
227
  } catch {
@@ -180,7 +230,7 @@ export class Logger {
180
230
  }
181
231
 
182
232
  try {
183
- fs.renameSync(this.logFile, path.join(logDir, `${baseName}.1.log`));
233
+ renameSync(this.logFile, path.join(logDir, `${baseName}.1.log`));
184
234
  this.currentSize = 0;
185
235
  } catch {
186
236
  // Continue even if rotation fails
@@ -189,34 +239,38 @@ export class Logger {
189
239
 
190
240
  debug(message: string, meta?: unknown): void {
191
241
  if (!this.shouldLog("debug")) return;
192
- const formatted = formatMessage("debug", message, meta);
242
+ const mergedMeta = this.mergeMeta(meta);
243
+ const formatted = formatMessage("debug", message, mergedMeta, this.correlationContext.correlationId);
193
244
  this.writeToConsole("debug", message, meta);
194
245
  this.writeToFile(formatted);
195
246
  }
196
247
 
197
248
  info(message: string, meta?: unknown): void {
198
249
  if (!this.shouldLog("info")) return;
199
- const formatted = formatMessage("info", message, meta);
250
+ const mergedMeta = this.mergeMeta(meta);
251
+ const formatted = formatMessage("info", message, mergedMeta, this.correlationContext.correlationId);
200
252
  this.writeToConsole("info", message, meta);
201
253
  this.writeToFile(formatted);
202
254
  }
203
255
 
204
256
  warn(message: string, meta?: unknown): void {
205
257
  if (!this.shouldLog("warn")) return;
206
- const formatted = formatMessage("warn", message, meta);
258
+ const mergedMeta = this.mergeMeta(meta);
259
+ const formatted = formatMessage("warn", message, mergedMeta, this.correlationContext.correlationId);
207
260
  this.writeToConsole("warn", message, meta);
208
261
  this.writeToFile(formatted);
209
262
  }
210
263
 
211
264
  error(message: string, meta?: unknown): void {
212
265
  if (!this.shouldLog("error")) return;
213
- const formatted = formatMessage("error", message, meta);
266
+ const mergedMeta = this.mergeMeta(meta);
267
+ const formatted = formatMessage("error", message, mergedMeta, this.correlationContext.correlationId);
214
268
  this.writeToConsole("error", message, meta);
215
269
  this.writeToFile(formatted);
216
270
  }
217
271
 
218
272
  child(context: string): ChildLogger {
219
- return new ChildLogger(this, context);
273
+ return new ChildLogger(this, context, this.correlationContext);
220
274
  }
221
275
 
222
276
  setLevel(level: LogLevel): void {
@@ -227,27 +281,52 @@ export class Logger {
227
281
  export class ChildLogger {
228
282
  constructor(
229
283
  private parent: Logger,
230
- private context: string
231
- ) {}
284
+ private context: string,
285
+ private correlationContext: LogMeta = {}
286
+ ) { }
232
287
 
233
288
  private prefix(message: string): string {
234
289
  return `[${this.context}] ${message}`;
235
290
  }
236
291
 
292
+ withCorrelationId(id: string): this {
293
+ this.correlationContext.correlationId = id;
294
+ return this;
295
+ }
296
+
297
+ setContext(context: Partial<LogMeta>): void {
298
+ this.correlationContext = { ...this.correlationContext, ...context };
299
+ }
300
+
237
301
  debug(message: string, meta?: unknown): void {
238
- this.parent.debug(this.prefix(message), meta);
302
+ this.parent.debug(this.prefix(message), this.mergeMeta(meta));
239
303
  }
240
304
 
241
305
  info(message: string, meta?: unknown): void {
242
- this.parent.info(this.prefix(message), meta);
306
+ this.parent.info(this.prefix(message), this.mergeMeta(meta));
243
307
  }
244
308
 
245
309
  warn(message: string, meta?: unknown): void {
246
- this.parent.warn(this.prefix(message), meta);
310
+ this.parent.warn(this.prefix(message), this.mergeMeta(meta));
247
311
  }
248
312
 
249
313
  error(message: string, meta?: unknown): void {
250
- this.parent.error(this.prefix(message), meta);
314
+ this.parent.error(this.prefix(message), this.mergeMeta(meta));
315
+ }
316
+
317
+ child(subContext: string): ChildLogger {
318
+ return new ChildLogger(
319
+ this.parent,
320
+ `${this.context}:${subContext}`,
321
+ this.correlationContext
322
+ );
323
+ }
324
+
325
+ private mergeMeta(meta?: unknown): LogMeta | undefined {
326
+ if (!meta && Object.keys(this.correlationContext).length === 0) return undefined;
327
+ if (!meta) return { ...this.correlationContext };
328
+ if (typeof meta !== "object") return meta as LogMeta;
329
+ return { ...this.correlationContext, ...(meta as LogMeta) };
251
330
  }
252
331
  }
253
332