@johpaz/hive-sdk 0.0.4 → 0.0.6

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.
@@ -1,25 +1,3245 @@
1
1
  // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
13
+ var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
21
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
22
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ for (let key of __getOwnPropNames(mod))
24
+ if (!__hasOwnProp.call(to, key))
25
+ __defProp(to, key, {
26
+ get: __accessProp.bind(mod, key),
27
+ enumerable: true
28
+ });
29
+ if (canCache)
30
+ cache.set(mod, to);
31
+ return to;
32
+ };
33
+ var __toCommonJS = (from) => {
34
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
35
+ if (entry)
36
+ return entry;
37
+ entry = __defProp({}, "__esModule", { value: true });
38
+ if (from && typeof from === "object" || typeof from === "function") {
39
+ for (var key of __getOwnPropNames(from))
40
+ if (!__hasOwnProp.call(entry, key))
41
+ __defProp(entry, key, {
42
+ get: __accessProp.bind(from, key),
43
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
44
+ });
45
+ }
46
+ __moduleCache.set(from, entry);
47
+ return entry;
48
+ };
49
+ var __moduleCache;
50
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
51
+ var __returnValue = (v) => v;
52
+ function __exportSetter(name, newValue) {
53
+ this[name] = __returnValue.bind(null, newValue);
54
+ }
55
+ var __export = (target, all) => {
56
+ for (var name in all)
57
+ __defProp(target, name, {
58
+ get: all[name],
59
+ enumerable: true,
60
+ configurable: true,
61
+ set: __exportSetter.bind(all, name)
62
+ });
63
+ };
64
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
65
+ var __require = import.meta.require;
66
+
67
+ // ../core/src/config/loader.ts
68
+ import * as z from "zod";
69
+ import { existsSync, readFileSync } from "fs";
70
+ import * as path from "path";
71
+ function loadEnv(hiveDir) {
72
+ const envPath = path.join(hiveDir, ".env");
73
+ if (existsSync(envPath)) {
74
+ try {
75
+ const text = readFileSync(envPath, "utf8");
76
+ const lines = text.split(`
77
+ `);
78
+ for (const line of lines) {
79
+ const trimmed = line.trim();
80
+ if (!trimmed || trimmed.startsWith("#"))
81
+ continue;
82
+ const [key, ...valueParts] = trimmed.split("=");
83
+ if (key && valueParts.length > 0) {
84
+ const value = valueParts.join("=").trim().replace(/^['"]|['"]$/g, "");
85
+ process.env[key.trim()] = value;
86
+ }
87
+ }
88
+ } catch (e) {}
89
+ }
90
+ }
91
+ function getHiveDir() {
92
+ if (process.env.HIVE_HOME) {
93
+ const hiveDir = process.env.HIVE_HOME.startsWith("~") ? path.join(process.env.HOME || "", process.env.HIVE_HOME.slice(1)) : process.env.HIVE_HOME;
94
+ loadEnv(hiveDir);
95
+ return hiveDir;
96
+ }
97
+ if (process.env.HIVE_DEV === "1" || process.env.HIVE_DEV === "true") {
98
+ const localDir = path.join(process.cwd(), ".hive-dev");
99
+ loadEnv(localDir);
100
+ return localDir;
101
+ }
102
+ const defaultDir = path.join(process.env.HOME || "", ".hive");
103
+ loadEnv(defaultDir);
104
+ return defaultDir;
105
+ }
106
+ function buildDefaultConfig() {
107
+ const hiveDir = getHiveDir();
108
+ return {
109
+ gateway: {
110
+ host: process.env.HIVE_HOST || "127.0.0.1",
111
+ port: parseInt(process.env.HIVE_PORT || "18790", 10),
112
+ pidFile: path.join(hiveDir, "gateway.pid"),
113
+ authToken: process.env.HIVE_AUTH_TOKEN || undefined,
114
+ tools: {
115
+ allow: ["*"],
116
+ deny: []
117
+ }
118
+ },
119
+ logging: {
120
+ level: process.env.HIVE_LOG_LEVEL || "info",
121
+ dir: path.join(hiveDir, "logs"),
122
+ maxSizeMB: 10,
123
+ maxFiles: 5,
124
+ redactSensitive: true,
125
+ console: true
126
+ },
127
+ agent: {
128
+ defaultAgentId: "main",
129
+ baseDir: path.join(hiveDir, "agents"),
130
+ context: {
131
+ maxTokens: 0,
132
+ compactionThreshold: 0.8,
133
+ minMessagesAfterCompaction: 4,
134
+ maxCompactionRetries: 3
135
+ }
136
+ },
137
+ models: {
138
+ defaultProvider: "openai",
139
+ defaults: {
140
+ openai: "gpt-4o",
141
+ anthropic: "claude-sonnet-4-20250514",
142
+ ollama: "llama3.2",
143
+ openrouter: "anthropic/claude-sonnet-4"
144
+ },
145
+ providers: {}
146
+ },
147
+ sessions: {
148
+ dir: path.join(hiveDir, "sessions"),
149
+ pruneAfterHours: 24,
150
+ maxTranscriptSizeMB: 50
151
+ },
152
+ agents: {
153
+ list: [
154
+ {
155
+ id: "main",
156
+ default: true,
157
+ workspace: path.join(hiveDir, "agents", "main", "workspace"),
158
+ description: "Default personal assistant"
159
+ }
160
+ ]
161
+ },
162
+ bindings: [],
163
+ channels: {
164
+ webchat: { enabled: true }
165
+ },
166
+ tools: {
167
+ allow: ["*"],
168
+ deny: [],
169
+ exec: {
170
+ enabled: true,
171
+ allowlist: [],
172
+ denylist: ["rm -rf /", "sudo", "chmod 777", "> /dev/", "mkfs"],
173
+ timeoutSeconds: 30,
174
+ workDir: path.join(process.env.HOME || "", "exec")
175
+ },
176
+ web: {
177
+ allowlist: [],
178
+ denylist: ["file://", "ftp://"],
179
+ timeoutSeconds: 30
180
+ },
181
+ browser: {
182
+ enabled: true,
183
+ cdpUrl: "ws://127.0.0.1:9222",
184
+ headless: true,
185
+ timeoutMs: 30000
186
+ },
187
+ canvas: {
188
+ enabled: true,
189
+ port: 18793
190
+ },
191
+ sandbox: {
192
+ dm: { allow: ["*"], deny: [] },
193
+ group: { allow: ["*"], deny: [] }
194
+ }
195
+ },
196
+ skills: {
197
+ allowBundled: [],
198
+ managedDir: path.join(hiveDir, "skills"),
199
+ extraDirs: [],
200
+ hotReload: true,
201
+ maxSkillSizeKB: 100
202
+ },
203
+ mcp: {
204
+ enabled: true,
205
+ servers: {},
206
+ healthCheck: {
207
+ enabled: true,
208
+ intervalSeconds: 60
209
+ }
210
+ },
211
+ memory: {
212
+ dbPath: path.join(hiveDir, "memory.db"),
213
+ notesDir: path.join(hiveDir, "agents", "main", "workspace", "memory"),
214
+ episodic: {
215
+ enabled: false,
216
+ provider: "openai",
217
+ maxEpisodesPerSession: 100
218
+ }
219
+ },
220
+ cron: {
221
+ enabled: true,
222
+ dbPath: path.join(hiveDir, "cron.db"),
223
+ maxConcurrentJobs: 5,
224
+ timezone: "UTC"
225
+ },
226
+ retry: {
227
+ maxAttempts: 3,
228
+ initialDelayMs: 1000,
229
+ backoffMultiplier: 2,
230
+ maxDelayMs: 30000
231
+ },
232
+ security: {
233
+ maxMessageLength: {
234
+ telegram: 4096,
235
+ discord: 2000,
236
+ slack: 40000,
237
+ webchat: 1e5,
238
+ whatsapp: 65536
239
+ },
240
+ skillScanning: true,
241
+ warnOnInsecureConfig: true
242
+ },
243
+ hooks: {
244
+ scripts: {}
245
+ },
246
+ captcha: {
247
+ enabled: false,
248
+ autoSolve: true,
249
+ visionProvider: "gemini",
250
+ visionModel: "gemini-2.0-flash-exp",
251
+ maxAttempts: 3,
252
+ maxRounds: 5,
253
+ enabledSites: []
254
+ }
255
+ };
256
+ }
257
+ function loadConfig() {
258
+ return buildDefaultConfig();
259
+ }
260
+ var LogLevelSchema, DMPolicySchema, TransportSchema, ProviderConfigSchema, ToolRestrictionsSchema, ExecConfigSchema, WebConfigSchema, BrowserConfigSchema, CanvasConfigSchema, SandboxConfigSchema, ToolsConfigSchema, ContextConfigSchema, AgentEntrySchema, AccountConfigSchema, ChannelConfigSchema, PeerMatchSchema, BindingMatchSchema, BindingSchema, MCPServerConfigSchema, MCPConfigSchema, EpisodicMemoryConfigSchema, MemoryConfigSchema, CronConfigSchema, RetryConfigSchema, HooksConfigSchema, LoggingConfigSchema, GatewayConfigSchema, ModelsConfigSchema, SessionsConfigSchema, SkillsConfigSchema, SecurityConfigSchema, CaptchaConfigSchema, UserConfigSchema, ConfigSchema;
261
+ var init_loader = __esm(() => {
262
+ LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
263
+ DMPolicySchema = z.enum(["open", "pairing", "allowlist"]);
264
+ TransportSchema = z.enum(["stdio", "sse", "websocket"]);
265
+ ProviderConfigSchema = z.object({
266
+ apiKey: z.string().optional(),
267
+ baseUrl: z.string().optional(),
268
+ rateLimit: z.number().optional(),
269
+ retries: z.number().optional(),
270
+ retryDelayMs: z.number().optional()
271
+ });
272
+ ToolRestrictionsSchema = z.object({
273
+ allow: z.array(z.string()).optional(),
274
+ deny: z.array(z.string()).optional()
275
+ });
276
+ ExecConfigSchema = z.object({
277
+ enabled: z.boolean().optional(),
278
+ allowlist: z.array(z.string()).optional(),
279
+ denylist: z.array(z.string()).optional(),
280
+ timeoutSeconds: z.number().optional(),
281
+ workDir: z.string().optional()
282
+ });
283
+ WebConfigSchema = z.object({
284
+ allowlist: z.array(z.string()).optional(),
285
+ denylist: z.array(z.string()).optional(),
286
+ timeoutSeconds: z.number().optional()
287
+ });
288
+ BrowserConfigSchema = z.object({
289
+ enabled: z.boolean().optional(),
290
+ cdpUrl: z.string().optional(),
291
+ headless: z.boolean().optional(),
292
+ timeoutMs: z.number().optional()
293
+ });
294
+ CanvasConfigSchema = z.object({
295
+ enabled: z.boolean().optional(),
296
+ port: z.number().optional()
297
+ });
298
+ SandboxConfigSchema = z.object({
299
+ dm: ToolRestrictionsSchema.optional(),
300
+ group: ToolRestrictionsSchema.optional()
301
+ });
302
+ ToolsConfigSchema = z.object({
303
+ allow: z.array(z.string()).optional(),
304
+ deny: z.array(z.string()).optional(),
305
+ exec: ExecConfigSchema.optional(),
306
+ web: WebConfigSchema.optional(),
307
+ browser: BrowserConfigSchema.optional(),
308
+ canvas: CanvasConfigSchema.optional(),
309
+ sandbox: SandboxConfigSchema.optional()
310
+ });
311
+ ContextConfigSchema = z.object({
312
+ maxTokens: z.number().optional(),
313
+ compactionThreshold: z.number().optional(),
314
+ minMessagesAfterCompaction: z.number().optional(),
315
+ maxCompactionRetries: z.number().optional()
316
+ });
317
+ AgentEntrySchema = z.object({
318
+ id: z.string(),
319
+ default: z.boolean().optional(),
320
+ workspace: z.string(),
321
+ description: z.string().optional()
322
+ });
323
+ AccountConfigSchema = z.object({
324
+ botToken: z.string().optional(),
325
+ applicationId: z.string().optional(),
326
+ appToken: z.string().optional(),
327
+ signingSecret: z.string().optional(),
328
+ dmPolicy: DMPolicySchema.optional(),
329
+ allowFrom: z.array(z.string()).optional()
330
+ });
331
+ ChannelConfigSchema = z.object({
332
+ enabled: z.boolean().optional(),
333
+ accounts: z.record(z.string(), AccountConfigSchema).optional(),
334
+ dmPolicy: DMPolicySchema.optional(),
335
+ allowFrom: z.array(z.string()).optional(),
336
+ groups: z.boolean().optional(),
337
+ guilds: z.record(z.string(), z.unknown()).optional(),
338
+ experimental: z.boolean().optional()
339
+ });
340
+ PeerMatchSchema = z.object({
341
+ kind: z.enum(["direct", "group"]).optional(),
342
+ id: z.string().optional()
343
+ });
344
+ BindingMatchSchema = z.object({
345
+ channel: z.string().optional(),
346
+ accountId: z.string().optional(),
347
+ peer: PeerMatchSchema.optional(),
348
+ guildId: z.string().optional(),
349
+ teamId: z.string().optional(),
350
+ roles: z.array(z.string()).optional()
351
+ });
352
+ BindingSchema = z.object({
353
+ agentId: z.string(),
354
+ match: BindingMatchSchema
355
+ });
356
+ MCPServerConfigSchema = z.object({
357
+ enabled: z.boolean().optional(),
358
+ transport: TransportSchema,
359
+ command: z.string().optional(),
360
+ args: z.array(z.string()).optional(),
361
+ env: z.record(z.string(), z.string()).optional(),
362
+ url: z.string().optional(),
363
+ headers: z.record(z.string(), z.string()).optional(),
364
+ reconnect: z.object({
365
+ enabled: z.boolean().optional(),
366
+ maxRetries: z.number().optional(),
367
+ delayMs: z.number().optional(),
368
+ backoffMultiplier: z.number().optional()
369
+ }).optional()
370
+ });
371
+ MCPConfigSchema = z.object({
372
+ enabled: z.boolean().optional(),
373
+ servers: z.record(z.string(), MCPServerConfigSchema).optional(),
374
+ healthCheck: z.object({
375
+ enabled: z.boolean().optional(),
376
+ intervalSeconds: z.number().optional()
377
+ }).optional()
378
+ });
379
+ EpisodicMemoryConfigSchema = z.object({
380
+ enabled: z.boolean().optional(),
381
+ provider: z.enum(["openai", "local"]).optional(),
382
+ maxEpisodesPerSession: z.number().optional()
383
+ });
384
+ MemoryConfigSchema = z.object({
385
+ dbPath: z.string().optional(),
386
+ notesDir: z.string().optional(),
387
+ episodic: EpisodicMemoryConfigSchema.optional()
388
+ });
389
+ CronConfigSchema = z.object({
390
+ enabled: z.boolean().optional(),
391
+ dbPath: z.string().optional(),
392
+ maxConcurrentJobs: z.number().optional(),
393
+ timezone: z.string().optional()
394
+ });
395
+ RetryConfigSchema = z.object({
396
+ maxAttempts: z.number().optional(),
397
+ initialDelayMs: z.number().optional(),
398
+ backoffMultiplier: z.number().optional(),
399
+ maxDelayMs: z.number().optional()
400
+ });
401
+ HooksConfigSchema = z.object({
402
+ scripts: z.object({
403
+ before_model_resolve: z.string().optional(),
404
+ before_prompt_build: z.string().optional(),
405
+ before_tool_call: z.string().optional(),
406
+ after_tool_call: z.string().optional(),
407
+ tool_result_persist: z.string().optional(),
408
+ before_compaction: z.string().optional(),
409
+ after_compaction: z.string().optional(),
410
+ message_received: z.string().optional(),
411
+ message_sending: z.string().optional(),
412
+ message_sent: z.string().optional(),
413
+ session_start: z.string().optional(),
414
+ session_end: z.string().optional(),
415
+ gateway_start: z.string().optional(),
416
+ gateway_stop: z.string().optional()
417
+ }).optional()
418
+ });
419
+ LoggingConfigSchema = z.object({
420
+ level: LogLevelSchema.optional(),
421
+ dir: z.string().optional(),
422
+ maxSizeMB: z.number().optional(),
423
+ maxFiles: z.number().optional(),
424
+ redactSensitive: z.boolean().optional(),
425
+ console: z.boolean().optional()
426
+ });
427
+ GatewayConfigSchema = z.object({
428
+ host: z.string().optional(),
429
+ port: z.number().optional(),
430
+ authToken: z.string().optional(),
431
+ pidFile: z.string().optional(),
432
+ tools: ToolRestrictionsSchema.optional()
433
+ });
434
+ ModelsConfigSchema = z.object({
435
+ defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek"]).optional(),
436
+ defaults: z.record(z.string(), z.string()).optional(),
437
+ providers: z.record(z.string(), ProviderConfigSchema).optional()
438
+ });
439
+ SessionsConfigSchema = z.object({
440
+ dir: z.string().optional(),
441
+ pruneAfterHours: z.number().optional(),
442
+ maxTranscriptSizeMB: z.number().optional()
443
+ });
444
+ SkillsConfigSchema = z.object({
445
+ allowBundled: z.array(z.string()).optional(),
446
+ managedDir: z.string().optional(),
447
+ extraDirs: z.array(z.string()).optional(),
448
+ hotReload: z.boolean().optional(),
449
+ maxSkillSizeKB: z.number().optional()
450
+ });
451
+ SecurityConfigSchema = z.object({
452
+ maxMessageLength: z.record(z.string(), z.number()).optional(),
453
+ skillScanning: z.boolean().optional(),
454
+ warnOnInsecureConfig: z.boolean().optional(),
455
+ allowedUsers: z.array(z.string()).optional()
456
+ });
457
+ CaptchaConfigSchema = z.object({
458
+ enabled: z.boolean().optional(),
459
+ autoSolve: z.boolean().optional(),
460
+ visionProvider: z.enum(["gemini", "openai", "anthropic"]).optional(),
461
+ visionModel: z.string().optional(),
462
+ maxAttempts: z.number().optional(),
463
+ maxRounds: z.number().optional(),
464
+ apiKey: z.string().optional(),
465
+ enabledSites: z.array(z.string()).optional()
466
+ });
467
+ UserConfigSchema = z.object({
468
+ id: z.string(),
469
+ name: z.string(),
470
+ channels: z.record(z.string(), z.string()).optional()
471
+ });
472
+ ConfigSchema = z.object({
473
+ gateway: GatewayConfigSchema.optional(),
474
+ logging: LoggingConfigSchema.optional(),
475
+ user: UserConfigSchema.optional(),
476
+ agent: z.object({
477
+ defaultAgentId: z.string().optional(),
478
+ baseDir: z.string().optional(),
479
+ context: ContextConfigSchema.optional()
480
+ }).optional(),
481
+ models: ModelsConfigSchema.optional(),
482
+ sessions: SessionsConfigSchema.optional(),
483
+ agents: z.object({
484
+ list: z.array(AgentEntrySchema).optional()
485
+ }).optional(),
486
+ bindings: z.array(BindingSchema).optional(),
487
+ channels: z.record(z.string(), ChannelConfigSchema).optional(),
488
+ tools: ToolsConfigSchema.optional(),
489
+ skills: SkillsConfigSchema.optional(),
490
+ mcp: MCPConfigSchema.optional(),
491
+ memory: MemoryConfigSchema.optional(),
492
+ cron: CronConfigSchema.optional(),
493
+ retry: RetryConfigSchema.optional(),
494
+ security: SecurityConfigSchema.optional(),
495
+ hooks: HooksConfigSchema.optional(),
496
+ captcha: CaptchaConfigSchema.optional()
497
+ });
498
+ });
499
+
500
+ // ../core/src/utils/logger.ts
501
+ import { mkdirSync as mkdirSync2, unlinkSync, renameSync, existsSync as existsSync2 } from "fs";
502
+ import * as path2 from "path";
503
+ function emitLogEntry(entry) {
504
+ for (const cb of _logListeners) {
505
+ try {
506
+ cb(entry);
507
+ } catch {}
508
+ }
509
+ }
510
+ function expandPath(p) {
511
+ if (p.startsWith("~")) {
512
+ return path2.join(process.env.HOME || "", p.slice(1));
513
+ }
514
+ return p;
515
+ }
516
+ function redact(obj, seen = new WeakSet) {
517
+ if (obj === null || typeof obj !== "object") {
518
+ return obj;
519
+ }
520
+ if (seen.has(obj)) {
521
+ return "[Circular]";
522
+ }
523
+ seen.add(obj);
524
+ if (Array.isArray(obj)) {
525
+ return obj.map((item) => redact(item, seen));
526
+ }
527
+ const result = {};
528
+ for (const [key, value] of Object.entries(obj)) {
529
+ const isSensitive = SENSITIVE_PATTERNS.some((p) => p.test(key));
530
+ if (isSensitive) {
531
+ result[key] = "[REDACTED]";
532
+ } else if (typeof value === "object" && value !== null) {
533
+ result[key] = redact(value, seen);
534
+ } else {
535
+ result[key] = value;
536
+ }
537
+ }
538
+ return result;
539
+ }
540
+ function formatTimestamp() {
541
+ return new Date().toISOString();
542
+ }
543
+ function formatMessage(level, message, meta, correlationId) {
544
+ const timestamp = formatTimestamp();
545
+ const corrStr = correlationId ? ` [${correlationId.slice(0, 8)}]` : "";
546
+ const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
547
+ return `[${timestamp}]${corrStr} [${level.toUpperCase()}] ${message}${metaStr}`;
548
+ }
549
+
550
+ class Logger {
551
+ config;
552
+ logFile = null;
553
+ currentSize = 0;
554
+ correlationContext = {};
555
+ constructor(config = {}) {
556
+ this.config = {
557
+ level: config.level ?? "info",
558
+ dir: config.dir ?? path2.join(getHiveDir(), "logs"),
559
+ maxSizeMB: config.maxSizeMB ?? 10,
560
+ maxFiles: config.maxFiles ?? 5,
561
+ redactSensitive: config.redactSensitive ?? true,
562
+ console: config.console ?? true
563
+ };
564
+ }
565
+ setCorrelationContext(context) {
566
+ this.correlationContext = { ...this.correlationContext, ...context };
567
+ }
568
+ clearCorrelationContext() {
569
+ this.correlationContext = {};
570
+ }
571
+ getCorrelationId() {
572
+ return this.correlationContext.correlationId;
573
+ }
574
+ withCorrelationId(id) {
575
+ this.correlationContext.correlationId = id;
576
+ return this;
577
+ }
578
+ initLogFile() {
579
+ const logDir = expandPath(this.config.dir);
580
+ try {
581
+ if (!existsSync2(logDir)) {
582
+ mkdirSync2(logDir, { recursive: true });
583
+ }
584
+ this.logFile = path2.join(logDir, `hive-${new Date().toISOString().split("T")[0]}.log`);
585
+ const file = Bun.file(this.logFile);
586
+ this.currentSize = file.size ?? 0;
587
+ } catch {
588
+ this.logFile = null;
589
+ }
590
+ }
591
+ shouldLog(level) {
592
+ return LOG_LEVELS[level] >= LOG_LEVELS[this.config.level];
593
+ }
594
+ writeToConsole(level, message, meta) {
595
+ if (!this.config.console)
596
+ return;
597
+ const color = COLORS[level];
598
+ const mergedMeta = this.mergeMeta(meta);
599
+ const displayMeta = this.config.redactSensitive && mergedMeta ? redact(mergedMeta) : mergedMeta;
600
+ const metaStr = displayMeta && Object.keys(displayMeta).length > 0 ? ` ${JSON.stringify(displayMeta)}` : "";
601
+ const prefix = `${COLORS.dim}${formatTimestamp()}${COLORS.reset}`;
602
+ const corrStr = this.correlationContext.correlationId ? ` ${COLORS.dim}[${this.correlationContext.correlationId.slice(0, 8)}]${COLORS.reset}` : "";
603
+ const levelStr = `${color}${COLORS.bright}[${level.toUpperCase().padEnd(5)}]${COLORS.reset}`;
604
+ console.log(`${prefix}${corrStr} ${levelStr} ${message}${metaStr}`);
605
+ }
606
+ mergeMeta(meta) {
607
+ if (!meta && Object.keys(this.correlationContext).length === 0)
608
+ return;
609
+ const contextWithoutCorrId = { ...this.correlationContext };
610
+ delete contextWithoutCorrId.correlationId;
611
+ if (!meta)
612
+ return contextWithoutCorrId;
613
+ if (typeof meta !== "object")
614
+ return meta;
615
+ return { ...contextWithoutCorrId, ...meta };
616
+ }
617
+ writeToFile(message) {
618
+ if (!this.logFile) {
619
+ this.initLogFile();
620
+ }
621
+ if (!this.logFile)
622
+ return;
623
+ try {
624
+ const line = message + `
625
+ `;
626
+ const bytes = Buffer.byteLength(line);
627
+ if (this.currentSize + bytes > this.config.maxSizeMB * 1024 * 1024) {
628
+ this.rotateLogs();
629
+ }
630
+ const encoder = new TextEncoder;
631
+ const data = encoder.encode(line);
632
+ Bun.write(this.logFile, data).catch(() => {});
633
+ this.currentSize += bytes;
634
+ } catch {}
635
+ }
636
+ rotateLogs() {
637
+ if (!this.logFile)
638
+ return;
639
+ const logDir = path2.dirname(this.logFile);
640
+ const baseName = path2.basename(this.logFile, ".log");
641
+ for (let i = this.config.maxFiles - 1;i >= 1; i--) {
642
+ const oldFile = path2.join(logDir, `${baseName}.${i}.log`);
643
+ const newFile = path2.join(logDir, `${baseName}.${i + 1}.log`);
644
+ try {
645
+ if (existsSync2(oldFile)) {
646
+ if (i === this.config.maxFiles - 1) {
647
+ unlinkSync(oldFile);
648
+ } else {
649
+ renameSync(oldFile, newFile);
650
+ }
651
+ }
652
+ } catch {}
653
+ }
654
+ try {
655
+ renameSync(this.logFile, path2.join(logDir, `${baseName}.1.log`));
656
+ this.currentSize = 0;
657
+ } catch {}
658
+ }
659
+ debug(message, meta) {
660
+ if (!this.shouldLog("debug"))
661
+ return;
662
+ const mergedMeta = this.mergeMeta(meta);
663
+ const formatted = formatMessage("debug", message, mergedMeta, this.correlationContext.correlationId);
664
+ this.writeToConsole("debug", message, meta);
665
+ this.writeToFile(formatted);
666
+ emitLogEntry({ timestamp: formatTimestamp(), level: "debug", source: "core", message, meta: mergedMeta });
667
+ }
668
+ info(message, meta) {
669
+ if (!this.shouldLog("info"))
670
+ return;
671
+ const mergedMeta = this.mergeMeta(meta);
672
+ const formatted = formatMessage("info", message, mergedMeta, this.correlationContext.correlationId);
673
+ this.writeToConsole("info", message, meta);
674
+ this.writeToFile(formatted);
675
+ emitLogEntry({ timestamp: formatTimestamp(), level: "info", source: "core", message, meta: mergedMeta });
676
+ }
677
+ warn(message, meta) {
678
+ if (!this.shouldLog("warn"))
679
+ return;
680
+ const mergedMeta = this.mergeMeta(meta);
681
+ const formatted = formatMessage("warn", message, mergedMeta, this.correlationContext.correlationId);
682
+ this.writeToConsole("warn", message, meta);
683
+ this.writeToFile(formatted);
684
+ emitLogEntry({ timestamp: formatTimestamp(), level: "warn", source: "core", message, meta: mergedMeta });
685
+ }
686
+ error(message, meta) {
687
+ if (!this.shouldLog("error"))
688
+ return;
689
+ const mergedMeta = this.mergeMeta(meta);
690
+ const formatted = formatMessage("error", message, mergedMeta, this.correlationContext.correlationId);
691
+ this.writeToConsole("error", message, meta);
692
+ this.writeToFile(formatted);
693
+ emitLogEntry({ timestamp: formatTimestamp(), level: "error", source: "core", message, meta: mergedMeta });
694
+ }
695
+ child(context) {
696
+ return new ChildLogger(this, context, this.correlationContext);
697
+ }
698
+ setLevel(level) {
699
+ this.config.level = level;
700
+ }
701
+ }
702
+
703
+ class ChildLogger {
704
+ parent;
705
+ context;
706
+ correlationContext;
707
+ constructor(parent, context, correlationContext = {}) {
708
+ this.parent = parent;
709
+ this.context = context;
710
+ this.correlationContext = correlationContext;
711
+ }
712
+ prefix(message) {
713
+ return `[${this.context}] ${message}`;
714
+ }
715
+ withCorrelationId(id) {
716
+ this.correlationContext.correlationId = id;
717
+ return this;
718
+ }
719
+ setContext(context) {
720
+ this.correlationContext = { ...this.correlationContext, ...context };
721
+ }
722
+ debug(message, meta) {
723
+ this.parent.debug(this.prefix(message), this.mergeMeta(meta));
724
+ }
725
+ info(message, meta) {
726
+ this.parent.info(this.prefix(message), this.mergeMeta(meta));
727
+ }
728
+ warn(message, meta) {
729
+ this.parent.warn(this.prefix(message), this.mergeMeta(meta));
730
+ }
731
+ error(message, meta) {
732
+ this.parent.error(this.prefix(message), this.mergeMeta(meta));
733
+ }
734
+ child(subContext) {
735
+ return new ChildLogger(this.parent, `${this.context}:${subContext}`, this.correlationContext);
736
+ }
737
+ mergeMeta(meta) {
738
+ if (!meta && Object.keys(this.correlationContext).length === 0)
739
+ return;
740
+ if (!meta)
741
+ return { ...this.correlationContext };
742
+ if (typeof meta !== "object")
743
+ return meta;
744
+ return { ...this.correlationContext, ...meta };
745
+ }
746
+ }
747
+ function getLogger() {
748
+ if (!_logger) {
749
+ const config = loadConfig();
750
+ _logger = new Logger({ level: config.logging?.level });
751
+ }
752
+ return _logger;
753
+ }
754
+ var _logListeners, LOG_LEVELS, SENSITIVE_PATTERNS, COLORS, _logger = null, logger;
755
+ var init_logger = __esm(() => {
756
+ init_loader();
757
+ _logListeners = new Set;
758
+ LOG_LEVELS = {
759
+ debug: 0,
760
+ info: 1,
761
+ warn: 2,
762
+ error: 3
763
+ };
764
+ SENSITIVE_PATTERNS = [
765
+ /api[_-]?key/i,
766
+ /token/i,
767
+ /secret/i,
768
+ /password/i,
769
+ /credential/i,
770
+ /auth/i
771
+ ];
772
+ COLORS = {
773
+ debug: "\x1B[36m",
774
+ info: "\x1B[32m",
775
+ warn: "\x1B[33m",
776
+ error: "\x1B[31m",
777
+ reset: "\x1B[0m",
778
+ dim: "\x1B[2m",
779
+ bright: "\x1B[1m"
780
+ };
781
+ logger = {
782
+ child: (opts) => getLogger().child(opts),
783
+ debug: (msg, meta) => getLogger().debug(msg, meta),
784
+ info: (msg, meta) => getLogger().info(msg, meta),
785
+ warn: (msg, meta) => getLogger().warn(msg, meta),
786
+ error: (msg, meta) => getLogger().error(msg, meta),
787
+ setCorrelationContext: (ctx) => getLogger().setCorrelationContext(ctx),
788
+ clearCorrelationContext: () => getLogger().clearCorrelationContext(),
789
+ getCorrelationId: () => getLogger().getCorrelationId(),
790
+ withCorrelationId: (id) => getLogger().withCorrelationId(id),
791
+ setLevel: (level) => getLogger().setLevel(level),
792
+ setHandler: (handler) => {}
793
+ };
794
+ });
795
+
796
+ // ../core/src/storage/schema.ts
797
+ var SCHEMA = `
798
+ PRAGMA journal_mode = WAL;
799
+ PRAGMA foreign_keys = ON;
800
+
801
+ -- ENCRYPTION KEY (stored separately, used for encrypting sensitive data)
802
+ -- The encryption key is derived from HIVE_MASTER_KEY env var or generated on first run
803
+
804
+ -- CONFIGURATION (all linked to user)
805
+
806
+ CREATE TABLE IF NOT EXISTS users (
807
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
808
+ name TEXT,
809
+ language TEXT,
810
+ timezone TEXT,
811
+ occupation TEXT,
812
+ notes TEXT,
813
+ master_key_hash TEXT,
814
+ email TEXT UNIQUE,
815
+ password_hash TEXT,
816
+ preferred_cron_channel TEXT NOT NULL DEFAULT 'auto',
817
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
818
+ );
819
+
820
+ -- Providers: linked to user (API key encrypted)
821
+ -- Solo la empresa (OpenAI, Groq, ElevenLabs, etc.)
822
+ -- La API key es del provider, no del modelo
823
+ -- category: 'llm', 'stt', 'tts' (default: llm)
824
+ CREATE TABLE IF NOT EXISTS providers (
825
+ id TEXT PRIMARY KEY,
826
+ name TEXT NOT NULL UNIQUE,
827
+ api_key_encrypted TEXT,
828
+ api_key_iv TEXT,
829
+ headers_encrypted TEXT,
830
+ headers_iv TEXT,
831
+ base_url TEXT,
832
+ category TEXT NOT NULL DEFAULT 'llm',
833
+ num_ctx INTEGER,
834
+ num_gpu INTEGER DEFAULT -1,
835
+ enabled INTEGER NOT NULL DEFAULT 1,
836
+ active INTEGER NOT NULL DEFAULT 0,
837
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
838
+ );
839
+
840
+ -- Models: linked to provider
841
+ -- model_type: 'llm', 'stt', 'tts', 'vision', 'embedding'
842
+ -- stt models: whisper-1, whisper-large-v3
843
+ -- tts models: tts-1, gpt-4o-mini-tts, eleven_multilingual_v2
844
+ CREATE TABLE IF NOT EXISTS models (
845
+ id TEXT PRIMARY KEY,
846
+ provider_id TEXT REFERENCES providers(id) ON DELETE CASCADE,
847
+ name TEXT NOT NULL,
848
+ model_type TEXT NOT NULL DEFAULT 'llm',
849
+ context_window INTEGER NOT NULL DEFAULT 20000,
850
+ capabilities TEXT,
851
+ enabled INTEGER NOT NULL DEFAULT 1,
852
+ active INTEGER NOT NULL DEFAULT 0
853
+ );
854
+
855
+ -- Agents: linked to user + provider/model
856
+ -- role: 'coordinator' | 'worker'
857
+ -- system_prompt: explicit prompt (description is a human-readable summary)
858
+ -- tools_json: JSON array of tool IDs this agent can use (NULL = all)
859
+ -- skills_json: JSON array of skill IDs this agent can use (NULL = all)
860
+ -- parent_id: agent that created this one (NULL for coordinator)
861
+ -- max_iterations: loop limit per invocation
862
+ CREATE TABLE IF NOT EXISTS agents (
863
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
864
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
865
+ name TEXT NOT NULL,
866
+ description TEXT,
867
+ system_prompt TEXT,
868
+ tone TEXT,
869
+ role TEXT NOT NULL DEFAULT 'coordinator' CHECK(role IN ('coordinator', 'worker')),
870
+ status TEXT NOT NULL DEFAULT 'idle',
871
+ enabled INTEGER NOT NULL DEFAULT 1,
872
+ provider_id TEXT REFERENCES providers(id),
873
+ model_id TEXT REFERENCES models(id),
874
+ tools_json TEXT,
875
+ skills_json TEXT,
876
+ parent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
877
+ max_iterations INTEGER NOT NULL DEFAULT 10,
878
+ headers_encrypted TEXT,
879
+ headers_iv TEXT,
880
+ workspace TEXT,
881
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
882
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
883
+ );
884
+
885
+ -- Channels: linked to user (or global if user_id is NULL)
886
+ -- voice_enabled: enables speech-to-text for incoming audio
887
+ -- tts_enabled: enables text-to-speech for outgoing responses
888
+ -- stt_provider: which STT provider to use (groq-whisper, openai-whisper)
889
+ -- tts_provider: which TTS provider to use (elevenlabs, openai-tts)
890
+ -- tts_voice_id: specific voice ID for TTS (e.g., ElevenLabs voice ID)
891
+ -- step_delivery_mode: how to send intermediate steps to user:
892
+ -- "new_message" = send new message for each step (default)
893
+ -- "edit" = edit same message (Telegram/Discord only)
894
+ -- "thread" = use threading (Slack only)
895
+ CREATE TABLE IF NOT EXISTS channels (
896
+ id TEXT PRIMARY KEY,
897
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
898
+ type TEXT NOT NULL,
899
+ config_encrypted TEXT,
900
+ config_iv TEXT,
901
+ enabled INTEGER NOT NULL DEFAULT 1,
902
+ active INTEGER NOT NULL DEFAULT 0,
903
+ status TEXT NOT NULL DEFAULT 'disconnected',
904
+ last_active INTEGER,
905
+ voice_enabled INTEGER NOT NULL DEFAULT 0,
906
+ tts_enabled INTEGER NOT NULL DEFAULT 0,
907
+ stt_provider TEXT,
908
+ tts_provider TEXT,
909
+ tts_voice_id TEXT,
910
+ step_delivery_mode TEXT DEFAULT 'new_messages'
911
+ );
912
+
913
+ -- MCP Servers: linked to user (or global if user_id is NULL)
914
+ CREATE TABLE IF NOT EXISTS mcp_servers (
915
+ id TEXT PRIMARY KEY,
916
+ name TEXT NOT NULL,
917
+ transport TEXT NOT NULL,
918
+ command TEXT,
919
+ args TEXT,
920
+ env_encrypted TEXT,
921
+ env_iv TEXT,
922
+ headers_encrypted TEXT,
923
+ headers_iv TEXT,
924
+ url TEXT,
925
+ enabled INTEGER NOT NULL DEFAULT 1,
926
+ active INTEGER NOT NULL DEFAULT 0,
927
+ builtin INTEGER NOT NULL DEFAULT 0,
928
+ status TEXT NOT NULL DEFAULT 'disconnected',
929
+ tools_count INTEGER DEFAULT 0
930
+ );
931
+
932
+ -- MCP Servers: external tool providers (stdio, SSE, etc.)
933
+ -- MCP tools are loaded at runtime from connected servers, not stored in DB
934
+ CREATE TABLE IF NOT EXISTS mcp_servers (
935
+ id TEXT PRIMARY KEY,
936
+ name TEXT NOT NULL,
937
+ transport TEXT NOT NULL,
938
+ command TEXT,
939
+ args TEXT,
940
+ env_encrypted TEXT,
941
+ env_iv TEXT,
942
+ headers_encrypted TEXT,
943
+ headers_iv TEXT,
944
+ url TEXT,
945
+ enabled INTEGER NOT NULL DEFAULT 1,
946
+ active INTEGER NOT NULL DEFAULT 0,
947
+ builtin INTEGER NOT NULL DEFAULT 0,
948
+ status TEXT NOT NULL DEFAULT 'disconnected',
949
+ tools_count INTEGER DEFAULT 0
950
+ );
951
+
952
+ -- Note: MCP tools are NOT stored in DB. They are loaded from MCP servers at runtime
953
+ -- and made available directly via context-compiler (Direct Connection architecture)
954
+
955
+ -- Skills: can be global (system) or user-specific
956
+ -- Simplified schema with body field for markdown content
957
+ CREATE TABLE IF NOT EXISTS skills (
958
+ id TEXT PRIMARY KEY, -- 'web_research'
959
+ name TEXT NOT NULL, -- 'Web Research'
960
+ category TEXT NOT NULL, -- 'web'
961
+ tools TEXT NOT NULL, -- 'web_search,web_fetch'
962
+ triggers TEXT NOT NULL, -- 'investiga,busca,research'
963
+ body TEXT NOT NULL, -- Markdown completo
964
+ version INTEGER DEFAULT 1,
965
+ active INTEGER DEFAULT 1, -- 1=activo, 0=desactivado
966
+ created_at TEXT DEFAULT (datetime('now')),
967
+ updated_at TEXT DEFAULT (datetime('now'))
968
+ );
969
+
970
+ -- \xCDndices para filtros directos
971
+ CREATE INDEX IF NOT EXISTS idx_skills_category ON skills(category);
972
+ CREATE INDEX IF NOT EXISTS idx_skills_active ON skills(active);
973
+
974
+ -- Tools: global (bundled), not user-specific
975
+ -- category: 'bundled', 'workspace', 'project', 'builtin', 'voice'
976
+ CREATE TABLE IF NOT EXISTS tools (
977
+ id TEXT PRIMARY KEY,
978
+ name TEXT NOT NULL UNIQUE,
979
+ description TEXT,
980
+ category TEXT,
981
+ enabled INTEGER NOT NULL DEFAULT 1,
982
+ active INTEGER NOT NULL DEFAULT 1,
983
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
984
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
985
+ );
986
+
987
+ -- Ethics: global templates (user selects one)
988
+ CREATE TABLE IF NOT EXISTS ethics (
989
+ id TEXT PRIMARY KEY,
990
+ name TEXT NOT NULL,
991
+ description TEXT,
992
+ content TEXT NOT NULL,
993
+ is_default INTEGER NOT NULL DEFAULT 0,
994
+ enabled INTEGER NOT NULL DEFAULT 1,
995
+ active INTEGER NOT NULL DEFAULT 0
996
+ );
997
+
998
+ -- Code Bridge: external CLI tools configuration (global)
999
+ CREATE TABLE IF NOT EXISTS code_bridge (
1000
+ id TEXT PRIMARY KEY,
1001
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
1002
+ name TEXT NOT NULL UNIQUE,
1003
+ cli_command TEXT NOT NULL,
1004
+ enabled INTEGER NOT NULL DEFAULT 0,
1005
+ active INTEGER NOT NULL DEFAULT 0,
1006
+ port INTEGER DEFAULT 18791,
1007
+ config TEXT
1008
+ );
1009
+
1010
+ -- Code Bridge Config: key-value store for configuration (voice_wake_word, etc.)
1011
+ CREATE TABLE IF NOT EXISTS code_bridge_config (
1012
+ id TEXT PRIMARY KEY,
1013
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
1014
+ key TEXT NOT NULL,
1015
+ value TEXT,
1016
+ UNIQUE(user_id, key)
1017
+ );
1018
+
1019
+ -- USER IDENTITIES (channel + user mapping)
1020
+
1021
+ CREATE TABLE IF NOT EXISTS user_identities (
1022
+ user_id TEXT NOT NULL REFERENCES users(id),
1023
+ channel TEXT NOT NULL,
1024
+ channel_user_id TEXT NOT NULL,
1025
+ linked_at INTEGER NOT NULL DEFAULT (unixepoch()),
1026
+ PRIMARY KEY (user_id, channel)
1027
+ );
1028
+
1029
+ -- USER CHANNELS (user-specific channel configurations)
1030
+ -- Stores per-user channel account configurations (Telegram bot, Discord bot, etc.)
1031
+ -- config: JSON object with channel-specific settings (bot token, webhook URL, etc.)
1032
+ -- active: 1 = enabled and running, 0 = disabled
1033
+ CREATE TABLE IF NOT EXISTS user_channels (
1034
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1035
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
1036
+ channel TEXT NOT NULL,
1037
+ account_id TEXT NOT NULL,
1038
+ config TEXT,
1039
+ active INTEGER NOT NULL DEFAULT 1,
1040
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1041
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
1042
+ UNIQUE(user_id, channel, account_id)
1043
+ );
1044
+
1045
+ CREATE INDEX IF NOT EXISTS idx_user_channels_user ON user_channels(user_id);
1046
+ CREATE INDEX IF NOT EXISTS idx_user_channels_channel ON user_channels(channel);
1047
+
1048
+ -- ONBOARDING PROGRESS
1049
+
1050
+ CREATE TABLE IF NOT EXISTS onboarding_progress (
1051
+ id TEXT PRIMARY KEY,
1052
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
1053
+ step TEXT NOT NULL,
1054
+ data TEXT,
1055
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
1056
+ );
1057
+
1058
+ -- USAGE TRACKING (tokens, costs)
1059
+ CREATE TABLE IF NOT EXISTS usage_records (
1060
+ id TEXT PRIMARY KEY,
1061
+ provider TEXT NOT NULL,
1062
+ model TEXT NOT NULL,
1063
+ input_tokens INTEGER NOT NULL DEFAULT 0,
1064
+ output_tokens INTEGER NOT NULL DEFAULT 0,
1065
+ cost_usd REAL NOT NULL DEFAULT 0,
1066
+ latency_ms INTEGER,
1067
+
1068
+ -- TOON Savings
1069
+ toon_saved_tokens INTEGER NOT NULL DEFAULT 0,
1070
+ toon_saved_cost REAL NOT NULL DEFAULT 0,
1071
+
1072
+ -- TOON Metrics (complete compression analysis)
1073
+ toon_json_bytes INTEGER NOT NULL DEFAULT 0,
1074
+ toon_toon_bytes INTEGER NOT NULL DEFAULT 0,
1075
+ toon_saved_bytes INTEGER NOT NULL DEFAULT 0,
1076
+ toon_saved_percent REAL NOT NULL DEFAULT 0,
1077
+ toon_json_tokens INTEGER NOT NULL DEFAULT 0,
1078
+ toon_toon_tokens INTEGER NOT NULL DEFAULT 0,
1079
+ toon_saved_tokens_pct REAL NOT NULL DEFAULT 0,
1080
+
1081
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
1082
+ );
1083
+
1084
+ -- CRON JOBS: scheduled tasks with notification channel
1085
+ -- notify_channel_id: which channel to send notifications to when task runs
1086
+ -- max_runs: NULL = unlimited; auto-disables when run_count reaches max_runs
1087
+ -- expires_at: NULL = never expires; auto-disables after this UTC timestamp
1088
+ CREATE TABLE IF NOT EXISTS cron_jobs (
1089
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
1090
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
1091
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1092
+ name TEXT NOT NULL,
1093
+ cron_expression TEXT NOT NULL,
1094
+ task_type TEXT NOT NULL DEFAULT 'message',
1095
+ task_config TEXT,
1096
+ notify_channel_id TEXT,
1097
+ enabled INTEGER NOT NULL DEFAULT 1,
1098
+ max_runs INTEGER,
1099
+ run_count INTEGER NOT NULL DEFAULT 0,
1100
+ expires_at INTEGER,
1101
+ last_run INTEGER,
1102
+ next_run INTEGER,
1103
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
1104
+ );
1105
+
1106
+ -- SCHEDULER: New Croner-based scheduled tasks (replaces cron_jobs)
1107
+ -- task_type: 'recurring' (uses cron_expression) or 'one_shot' (uses fire_at)
1108
+ -- cron_expression: stored in user's local time, not UTC
1109
+ -- fire_at: ISO 8601 format in user's local time (e.g., "2026-04-01T09:00:00")
1110
+ -- timezone: IANA timezone string (e.g., "America/Bogota") inherited from users.timezone
1111
+ -- protect: overrun protection (1 = enabled, 0 = disabled)
1112
+ -- payload: JSON string with at least { prompt: string } or { message: string }
1113
+ -- CHECK constraint: recurring requires cron_expression, one_shot requires fire_at
1114
+ CREATE TABLE IF NOT EXISTS scheduled_tasks (
1115
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(8)))),
1116
+ name TEXT NOT NULL,
1117
+ description TEXT DEFAULT '',
1118
+ task_type TEXT NOT NULL CHECK(task_type IN ('recurring', 'one_shot')),
1119
+ cron_expression TEXT,
1120
+ fire_at TEXT,
1121
+ timezone TEXT NOT NULL DEFAULT 'UTC',
1122
+ max_runs INTEGER,
1123
+ protect INTEGER NOT NULL DEFAULT 1,
1124
+ interval_sec INTEGER,
1125
+ agent_id TEXT,
1126
+ channel TEXT DEFAULT 'system',
1127
+ payload TEXT DEFAULT '{}',
1128
+ tool_name TEXT,
1129
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'paused', 'completed', 'failed', 'cancelled')),
1130
+ run_count INTEGER NOT NULL DEFAULT 0,
1131
+ error_count INTEGER NOT NULL DEFAULT 0,
1132
+ last_error TEXT,
1133
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
1134
+ updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
1135
+ last_run_at TEXT,
1136
+ next_run_at TEXT,
1137
+ completed_at TEXT,
1138
+ CHECK(
1139
+ (task_type = 'recurring' AND cron_expression IS NOT NULL) OR
1140
+ (task_type = 'one_shot' AND fire_at IS NOT NULL)
1141
+ )
1142
+ );
1143
+
1144
+ -- SCHEDULER: Task execution history
1145
+ -- Stores results of each task execution for debugging and auditing
1146
+ CREATE TABLE IF NOT EXISTS task_runs (
1147
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(8)))),
1148
+ task_id TEXT NOT NULL REFERENCES scheduled_tasks(id) ON DELETE CASCADE,
1149
+ status TEXT NOT NULL CHECK(status IN ('running', 'success', 'failed', 'timeout')),
1150
+ started_at TEXT NOT NULL,
1151
+ finished_at TEXT,
1152
+ duration_ms INTEGER,
1153
+ error_message TEXT,
1154
+ payload_snapshot TEXT,
1155
+ agent_response TEXT
1156
+ );
1157
+
1158
+ -- INDICES for scheduled_tasks
1159
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_status ON scheduled_tasks(status);
1160
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_type ON scheduled_tasks(task_type);
1161
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_next_run ON scheduled_tasks(next_run_at);
1162
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_agent ON scheduled_tasks(agent_id);
1163
+
1164
+ -- INDICES for task_runs
1165
+ CREATE INDEX IF NOT EXISTS idx_task_runs_task ON task_runs(task_id);
1166
+ CREATE INDEX IF NOT EXISTS idx_task_runs_started ON task_runs(started_at);
1167
+ CREATE INDEX IF NOT EXISTS idx_task_runs_status ON task_runs(status);
1168
+
1169
+ -- TRIGGER: Update updated_at on scheduled_tasks UPDATE
1170
+ CREATE TRIGGER IF NOT EXISTS update_scheduled_tasks_updated_at
1171
+ AFTER UPDATE ON scheduled_tasks
1172
+ BEGIN
1173
+ UPDATE scheduled_tasks SET updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = NEW.id;
1174
+ END;
1175
+
1176
+ -- INDICES
1177
+
1178
+ CREATE INDEX IF NOT EXISTS idx_models_provider ON models(provider_id);
1179
+ CREATE INDEX IF NOT EXISTS idx_models_type ON models(model_type);
1180
+ CREATE INDEX IF NOT EXISTS idx_agents_user ON agents(user_id);
1181
+ CREATE INDEX IF NOT EXISTS idx_channels_user ON channels(user_id);
1182
+ CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(type);
1183
+ CREATE INDEX IF NOT EXISTS idx_ethics ON ethics(id);
1184
+ CREATE INDEX IF NOT EXISTS idx_code_bridge ON code_bridge(id);
1185
+ CREATE INDEX IF NOT EXISTS idx_identities_user ON user_identities(user_id);
1186
+ CREATE INDEX IF NOT EXISTS idx_usage_provider_model ON usage_records(provider, model);
1187
+ CREATE INDEX IF NOT EXISTS idx_usage_created_at ON usage_records(created_at);
1188
+ CREATE INDEX IF NOT EXISTS idx_code_bridge_config_user ON code_bridge_config(user_id);
1189
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_user ON cron_jobs(user_id);
1190
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_enabled ON cron_jobs(enabled);
1191
+ CREATE INDEX IF NOT EXISTS idx_cron_jobs_next_run ON cron_jobs(next_run);
1192
+ `, PROJECTS_SCHEMA = `
1193
+ -- PROJECTS: tareas multi-paso con seguimiento de progreso
1194
+ CREATE TABLE IF NOT EXISTS projects (
1195
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
1196
+ user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
1197
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
1198
+ name TEXT NOT NULL,
1199
+ description TEXT,
1200
+ type TEXT NOT NULL DEFAULT 'general',
1201
+ task TEXT,
1202
+ progress INTEGER NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 100),
1203
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','active','paused','done','failed')),
1204
+ context TEXT,
1205
+ parent_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1206
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1207
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
1208
+ started_at INTEGER,
1209
+ completed_at INTEGER
1210
+ );
1211
+
1212
+ -- TASKS: subtareas at\xF3micas asociadas a un proyecto, con agente asignado
1213
+ -- depends_on: JSON array of task IDs that must complete before this one
1214
+ -- priority: higher = more urgent
1215
+ -- error: reason for failure if status='failed'
1216
+ CREATE TABLE IF NOT EXISTS tasks (
1217
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1218
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
1219
+ agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
1220
+ parent_task_id INTEGER REFERENCES tasks(id) ON DELETE CASCADE,
1221
+ name TEXT NOT NULL,
1222
+ description TEXT,
1223
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','in_progress','completed','failed','blocked')),
1224
+ progress INTEGER NOT NULL DEFAULT 0 CHECK(progress >= 0 AND progress <= 100),
1225
+ priority INTEGER NOT NULL DEFAULT 0,
1226
+ depends_on TEXT,
1227
+ result TEXT,
1228
+ error TEXT,
1229
+ metadata TEXT,
1230
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1231
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
1232
+ completed_at INTEGER
1233
+ );
1234
+
1235
+ CREATE INDEX IF NOT EXISTS idx_projects_user ON projects(user_id);
1236
+ CREATE INDEX IF NOT EXISTS idx_projects_agent ON projects(agent_id);
1237
+ CREATE INDEX IF NOT EXISTS idx_projects_parent ON projects(parent_id);
1238
+ CREATE INDEX IF NOT EXISTS idx_projects_status ON projects(status);
1239
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id);
1240
+ CREATE INDEX IF NOT EXISTS idx_tasks_agent ON tasks(agent_id);
1241
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
1242
+ CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
1243
+ `, CONTEXT_ENGINE_SCHEMA = `
1244
+ -- CONVERSATIONS: full message history per thread (replaces lg_checkpoints)
1245
+ -- role: 'user' | 'assistant' | 'tool' | 'system'
1246
+ -- tool_calls_json: JSON array of tool calls if the message triggered any
1247
+ -- token_count: estimated tokens for context budget tracking
1248
+ CREATE TABLE IF NOT EXISTS conversations (
1249
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1250
+ thread_id TEXT NOT NULL,
1251
+ channel TEXT NOT NULL DEFAULT 'webchat',
1252
+ role TEXT NOT NULL CHECK(role IN ('user','assistant','tool','system')),
1253
+ content TEXT NOT NULL,
1254
+ tool_calls_json TEXT,
1255
+ tool_call_id TEXT,
1256
+ token_count INTEGER NOT NULL DEFAULT 0,
1257
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1258
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
1259
+ );
1260
+
1261
+ -- SUMMARIES: compressed digests of long conversations
1262
+ -- The Context Compiler uses the summary instead of full history
1263
+ CREATE TABLE IF NOT EXISTS summaries (
1264
+ thread_id TEXT PRIMARY KEY,
1265
+ summary TEXT NOT NULL,
1266
+ messages_covered INTEGER NOT NULL DEFAULT 0,
1267
+ last_message_id INTEGER,
1268
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1269
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
1270
+ );
1271
+
1272
+ -- SCRATCHPAD: persistent key-value notes per conversation
1273
+ -- Survives context compression. Written by agents via save_note tool.
1274
+ CREATE TABLE IF NOT EXISTS scratchpad (
1275
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1276
+ thread_id TEXT NOT NULL,
1277
+ key TEXT NOT NULL,
1278
+ value TEXT NOT NULL,
1279
+ source TEXT,
1280
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1281
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
1282
+ UNIQUE(thread_id, key)
1283
+ );
1284
+
1285
+ -- TRACES: execution log for every agent invocation (ACE Generator output)
1286
+ -- success: 1 = ok, 0 = failure
1287
+ -- tokens_used: total tokens consumed in this invocation
1288
+ CREATE TABLE IF NOT EXISTS traces (
1289
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1290
+ thread_id TEXT NOT NULL,
1291
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
1292
+ agent_name TEXT NOT NULL,
1293
+ tool_used TEXT,
1294
+ input_summary TEXT NOT NULL,
1295
+ output_summary TEXT NOT NULL,
1296
+ success INTEGER NOT NULL DEFAULT 1,
1297
+ error_message TEXT,
1298
+ duration_ms INTEGER,
1299
+ tokens_used INTEGER,
1300
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
1301
+ );
1302
+
1303
+ -- REFLECTIONS: insights extracted by the ACE Reflector from traces
1304
+ -- insight_type: 'success_pattern' | 'failure_pattern' | 'optimization' | 'ethics_violation'
1305
+ -- confidence: 0.0 to 1.0
1306
+ CREATE TABLE IF NOT EXISTS reflections (
1307
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1308
+ trace_ids TEXT NOT NULL,
1309
+ insight_type TEXT NOT NULL CHECK(insight_type IN ('success_pattern','failure_pattern','optimization','ethics_violation')),
1310
+ description TEXT NOT NULL,
1311
+ affected_tools TEXT,
1312
+ affected_agents TEXT,
1313
+ confidence REAL NOT NULL DEFAULT 0.5,
1314
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
1315
+ );
1316
+
1317
+ -- PLAYBOOK: evolved rules injected by Context Compiler (ACE Curator output)
1318
+ -- category: 'tool_selection' | 'response_quality' | 'error_avoidance' | 'optimization' | 'agent_creation'
1319
+ -- applicable_to: JSON array of contexts where this rule applies
1320
+ -- helpful_count / harmful_count: feedback from execution outcomes
1321
+ -- active: 0 = pruned by Curator
1322
+ CREATE TABLE IF NOT EXISTS playbook (
1323
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1324
+ rule TEXT NOT NULL,
1325
+ category TEXT NOT NULL CHECK(category IN ('tool_selection','response_quality','error_avoidance','optimization','agent_creation')),
1326
+ applicable_to TEXT,
1327
+ helpful_count INTEGER NOT NULL DEFAULT 0,
1328
+ harmful_count INTEGER NOT NULL DEFAULT 0,
1329
+ source_reflection_id INTEGER REFERENCES reflections(id) ON DELETE SET NULL,
1330
+ active INTEGER NOT NULL DEFAULT 1,
1331
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1332
+ updated_at INTEGER NOT NULL DEFAULT (unixepoch())
1333
+ );
1334
+
1335
+ -- TOOL_CACHE: cached results for deterministic/expensive tool calls
1336
+ -- cache_key: hash of tool_id + serialized params
1337
+ -- ttl_seconds: 0 = no expiry
1338
+ CREATE TABLE IF NOT EXISTS tool_cache (
1339
+ cache_key TEXT PRIMARY KEY,
1340
+ tool_id TEXT NOT NULL,
1341
+ result TEXT NOT NULL,
1342
+ ttl_seconds INTEGER NOT NULL DEFAULT 3600,
1343
+ created_at INTEGER NOT NULL DEFAULT (unixepoch())
1344
+ );
1345
+
1346
+ -- FTS5 indexes for fast semantic search in Context Compiler
1347
+ -- Created by initializeDatabase() via CONTEXT_ENGINE_SCHEMA
1348
+ -- Populated by syncToolsToFTS() and syncSkillsToFTS() from gateway/initializer.ts
1349
+ -- Triggers are NOT used - data is cleared and re-inserted on each sync to avoid schema drift
1350
+
1351
+ CREATE VIRTUAL TABLE IF NOT EXISTS playbook_fts USING fts5(
1352
+ rule,
1353
+ category,
1354
+ applicable_to
1355
+ );
1356
+
1357
+ -- FTS5: tool catalog search (populated by syncToolCatalogToFTS from gateway/initializer.ts)
1358
+ CREATE VIRTUAL TABLE IF NOT EXISTS tools_fts USING fts5(
1359
+ tool_name,
1360
+ name,
1361
+ description,
1362
+ category
1363
+ );
1364
+
1365
+ -- FTS5: skills catalog search (populated by syncSkillsToFTS from gateway/initializer.ts)
1366
+ -- Simplified schema - standalone FTS5 table (no content table sync)
1367
+ -- Note: FTS5 tables are created programmatically in seed.ts to avoid "already exists" errors
1368
+
1369
+ -- REFRESH TOKENS: JWT refresh token storage (hash-based for security)
1370
+ -- Stores hashed refresh tokens with expiry and user linkage
1371
+ CREATE TABLE IF NOT EXISTS refresh_tokens (
1372
+ id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
1373
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
1374
+ token_hash TEXT NOT NULL UNIQUE,
1375
+ expires_at INTEGER NOT NULL,
1376
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1377
+ revoked INTEGER NOT NULL DEFAULT 0
1378
+ );
1379
+
1380
+ CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user ON refresh_tokens(user_id);
1381
+ CREATE INDEX IF NOT EXISTS idx_refresh_tokens_hash ON refresh_tokens(token_hash);
1382
+ CREATE INDEX IF NOT EXISTS idx_refresh_tokens_expires ON refresh_tokens(expires_at);
1383
+
1384
+ -- Agent Bus: Message queue for worker-to-worker communication
1385
+ CREATE TABLE IF NOT EXISTS agent_bus_messages (
1386
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1387
+ event_type TEXT NOT NULL,
1388
+ from_worker_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
1389
+ to_worker_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
1390
+ topic TEXT,
1391
+ content TEXT NOT NULL,
1392
+ metadata TEXT,
1393
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
1394
+ read INTEGER NOT NULL DEFAULT 0
1395
+ );
1396
+
1397
+ CREATE INDEX IF NOT EXISTS idx_agent_bus_from_worker ON agent_bus_messages(from_worker_id);
1398
+ CREATE INDEX IF NOT EXISTS idx_agent_bus_to_worker ON agent_bus_messages(to_worker_id);
1399
+ CREATE INDEX IF NOT EXISTS idx_agent_bus_event_type ON agent_bus_messages(event_type);
1400
+ CREATE INDEX IF NOT EXISTS idx_agent_bus_read ON agent_bus_messages(read);
1401
+
1402
+ -- INDICES
1403
+ CREATE INDEX IF NOT EXISTS idx_conversations_thread ON conversations(thread_id);
1404
+ CREATE INDEX IF NOT EXISTS idx_conversations_role ON conversations(role);
1405
+ CREATE INDEX IF NOT EXISTS idx_scratchpad_thread ON scratchpad(thread_id);
1406
+ CREATE INDEX IF NOT EXISTS idx_traces_thread ON traces(thread_id);
1407
+ CREATE INDEX IF NOT EXISTS idx_traces_agent ON traces(agent_id);
1408
+ CREATE INDEX IF NOT EXISTS idx_traces_success ON traces(success);
1409
+ CREATE INDEX IF NOT EXISTS idx_reflections_type ON reflections(insight_type);
1410
+ CREATE INDEX IF NOT EXISTS idx_playbook_active ON playbook(active);
1411
+ CREATE INDEX IF NOT EXISTS idx_playbook_category ON playbook(category);
1412
+ CREATE INDEX IF NOT EXISTS idx_tool_cache_tool ON tool_cache(tool_id);
1413
+
1414
+
1415
+ `;
1416
+
1417
+ // ../core/src/storage/sqlite.ts
1418
+ var exports_sqlite = {};
1419
+ __export(exports_sqlite, {
1420
+ initializeDatabase: () => initializeDatabase,
1421
+ getDbPathLazy: () => getDbPathLazy,
1422
+ getDb: () => getDb,
1423
+ dbService: () => dbService,
1424
+ DatabaseService: () => DatabaseService
1425
+ });
1426
+ import { Database } from "bun:sqlite";
1427
+ import * as path3 from "path";
1428
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
1429
+ function getDbPath() {
1430
+ return path3.join(getHiveDir(), "data", "hive.db");
1431
+ }
1432
+ function getDbPathLazy() {
1433
+ return getDbPath();
1434
+ }
1435
+ function getDb() {
1436
+ if (!_db)
1437
+ throw new Error("DB no inicializada. Llama initializeDatabase() primero.");
1438
+ return _db;
1439
+ }
1440
+ function initializeDatabase() {
1441
+ const hiveDir = getHiveDir();
1442
+ const dir = path3.join(hiveDir, "data");
1443
+ if (!existsSync3(dir)) {
1444
+ mkdirSync3(dir, { recursive: true });
1445
+ }
1446
+ const dbPath = getDbPath();
1447
+ const dbFileExists = existsSync3(dbPath);
1448
+ _db = new Database(dbPath, { create: true });
1449
+ _db.exec(SCHEMA);
1450
+ _db.exec(PROJECTS_SCHEMA);
1451
+ _db.exec(CONTEXT_ENGINE_SCHEMA);
1452
+ ensureSchemaSync();
1453
+ return _db;
1454
+ }
1455
+ function ensureColumnExists(tableName, columnName, columnDefinition) {
1456
+ if (!_db)
1457
+ return;
1458
+ try {
1459
+ const info = _db.query(`PRAGMA table_info(${tableName})`).all();
1460
+ const exists = info.some((col) => col.name === columnName);
1461
+ if (!exists) {
1462
+ logger.info(`\uD83D\uDEE0\uFE0F A\xF1adiendo columna faltante '${columnName}' a la tabla '${tableName}'`);
1463
+ _db.exec(`ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnDefinition}`);
1464
+ }
1465
+ } catch (err) {
1466
+ logger.warn(`\u26A0\uFE0F No se pudo verificar/a\xF1adir la columna '${columnName}' en '${tableName}':`, { error: err.message });
1467
+ }
1468
+ }
1469
+ function ensureSchemaSync() {
1470
+ if (!_db)
1471
+ return;
1472
+ ensureColumnExists("users", "email", "TEXT");
1473
+ ensureColumnExists("users", "password_hash", "TEXT");
1474
+ ensureColumnExists("mcp_servers", "tools_count", "INTEGER DEFAULT 0");
1475
+ ensureColumnExists("mcp_servers", "status", "TEXT NOT NULL DEFAULT 'disconnected'");
1476
+ ensureColumnExists("mcp_servers", "env_encrypted", "TEXT");
1477
+ ensureColumnExists("mcp_servers", "env_iv", "TEXT");
1478
+ ensureColumnExists("mcp_servers", "headers_encrypted", "TEXT");
1479
+ ensureColumnExists("mcp_servers", "headers_iv", "TEXT");
1480
+ ensureColumnExists("providers", "api_key_encrypted", "TEXT");
1481
+ ensureColumnExists("providers", "api_key_iv", "TEXT");
1482
+ ensureColumnExists("providers", "headers_encrypted", "TEXT");
1483
+ ensureColumnExists("providers", "headers_iv", "TEXT");
1484
+ ensureColumnExists("providers", "num_ctx", "INTEGER");
1485
+ ensureColumnExists("providers", "num_gpu", "INTEGER DEFAULT -1");
1486
+ ensureColumnExists("agents", "headers_encrypted", "TEXT");
1487
+ ensureColumnExists("agents", "headers_iv", "TEXT");
1488
+ ensureColumnExists("agents", "system_prompt", "TEXT");
1489
+ ensureColumnExists("agents", "role", "TEXT NOT NULL DEFAULT 'coordinator'");
1490
+ ensureColumnExists("agents", "tools_json", "TEXT");
1491
+ ensureColumnExists("agents", "skills_json", "TEXT");
1492
+ ensureColumnExists("agents", "parent_id", "TEXT");
1493
+ ensureColumnExists("agents", "max_iterations", "INTEGER NOT NULL DEFAULT 10");
1494
+ ensureColumnExists("agents", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1495
+ ensureColumnExists("agents", "workspace", "TEXT");
1496
+ ensureColumnExists("tasks", "priority", "INTEGER NOT NULL DEFAULT 0");
1497
+ ensureColumnExists("tasks", "depends_on", "TEXT");
1498
+ ensureColumnExists("tasks", "error", "TEXT");
1499
+ ensureColumnExists("tasks", "completed_at", "INTEGER");
1500
+ ensureColumnExists("tools", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1501
+ ensureColumnExists("tools", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1502
+ ensureColumnExists("skills", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1503
+ ensureColumnExists("skills", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1504
+ ensureColumnExists("cron_jobs", "max_runs", "INTEGER");
1505
+ ensureColumnExists("cron_jobs", "run_count", "INTEGER NOT NULL DEFAULT 0");
1506
+ ensureColumnExists("cron_jobs", "expires_at", "INTEGER");
1507
+ ensureColumnExists("conversations", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1508
+ ensureColumnExists("conversations", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1509
+ ensureColumnExists("conversations", "reasoning_content", "TEXT");
1510
+ ensureColumnExists("summaries", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1511
+ ensureColumnExists("summaries", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1512
+ ensureColumnExists("scratchpad", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1513
+ ensureColumnExists("scratchpad", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1514
+ ensureColumnExists("traces", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1515
+ ensureColumnExists("reflections", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1516
+ ensureColumnExists("playbook", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1517
+ ensureColumnExists("playbook", "updated_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1518
+ ensureColumnExists("tool_cache", "created_at", "INTEGER NOT NULL DEFAULT (unixepoch())");
1519
+ if (_db) {
1520
+ _db.query(`UPDATE providers SET base_url = 'https://api.groq.com/openai/v1' WHERE id = 'groq' AND base_url = 'https://api.groq.com/v1'`).run();
1521
+ _db.query(`UPDATE providers SET base_url = 'https://api.openai.com/v1' WHERE id = 'openai' AND base_url = 'https://api.openai.com'`).run();
1522
+ _db.query(`UPDATE providers SET base_url = NULL WHERE id = 'gemini' AND base_url = 'https://generativelanguage.googleapis.com/v1beta'`).run();
1523
+ }
1524
+ }
1525
+
1526
+ class DatabaseService {
1527
+ log = logger.child("sqlite");
1528
+ get db() {
1529
+ if (!_db) {
1530
+ initializeDatabase();
1531
+ }
1532
+ return _db;
1533
+ }
1534
+ close() {
1535
+ if (_db) {
1536
+ _db.close();
1537
+ _db = null;
1538
+ }
1539
+ }
1540
+ updateMCPServer(id, updates) {
1541
+ const fields = [];
1542
+ const values = { $id: id };
1543
+ if (updates.enabled !== undefined) {
1544
+ fields.push("enabled = $enabled");
1545
+ values.$enabled = updates.enabled ? 1 : 0;
1546
+ }
1547
+ if (updates.active !== undefined) {
1548
+ fields.push("active = $active");
1549
+ values.$active = updates.active ? 1 : 0;
1550
+ }
1551
+ if (updates.status !== undefined) {
1552
+ fields.push("status = $status");
1553
+ values.$status = updates.status;
1554
+ }
1555
+ if (updates.tools_count !== undefined) {
1556
+ fields.push("tools_count = $tools_count");
1557
+ values.$tools_count = updates.tools_count;
1558
+ }
1559
+ if (updates.transport !== undefined) {
1560
+ fields.push("transport = $transport");
1561
+ values.$transport = updates.transport;
1562
+ }
1563
+ if (updates.command !== undefined) {
1564
+ fields.push("command = $command");
1565
+ values.$command = updates.command;
1566
+ }
1567
+ if (updates.args !== undefined) {
1568
+ fields.push("args = $args");
1569
+ values.$args = JSON.stringify(updates.args);
1570
+ }
1571
+ if (updates.url !== undefined) {
1572
+ fields.push("url = $url");
1573
+ values.$url = updates.url;
1574
+ }
1575
+ if (updates.env_encrypted !== undefined) {
1576
+ fields.push("env_encrypted = $env_encrypted");
1577
+ values.$env_encrypted = updates.env_encrypted;
1578
+ }
1579
+ if (updates.env_iv !== undefined) {
1580
+ fields.push("env_iv = $env_iv");
1581
+ values.$env_iv = updates.env_iv;
1582
+ }
1583
+ if (updates.headers_encrypted !== undefined) {
1584
+ fields.push("headers_encrypted = $headers_encrypted");
1585
+ values.$headers_encrypted = updates.headers_encrypted;
1586
+ }
1587
+ if (updates.headers_iv !== undefined) {
1588
+ fields.push("headers_iv = $headers_iv");
1589
+ values.$headers_iv = updates.headers_iv;
1590
+ }
1591
+ if (fields.length === 0)
1592
+ return;
1593
+ const query = `UPDATE mcp_servers SET ${fields.join(", ")} WHERE id = $id`;
1594
+ try {
1595
+ this.db.query(query).run(values);
1596
+ this.log.debug(`MCP server ${id} updated in DB`);
1597
+ } catch (error) {
1598
+ this.log.error(`Failed to update MCP server ${id}: ${error.message}`);
1599
+ }
1600
+ }
1601
+ getActiveAgentWorkspace() {
1602
+ try {
1603
+ const row = this.db.query("SELECT workspace FROM agents WHERE role = 'coordinator' LIMIT 1").get();
1604
+ const ws = row?.workspace;
1605
+ return ws && ws !== "null" ? ws : null;
1606
+ } catch {
1607
+ return null;
1608
+ }
1609
+ }
1610
+ listMCPServers() {
1611
+ try {
1612
+ return this.db.query("SELECT * FROM mcp_servers").all();
1613
+ } catch (error) {
1614
+ this.log.error(`Failed to list MCP servers: ${error.message}`);
1615
+ return [];
1616
+ }
1617
+ }
1618
+ createTask(task) {
1619
+ const result = this.db.query(`
1620
+ INSERT INTO tasks (project_id, agent_id, parent_task_id, name, description)
1621
+ VALUES (?, ?, ?, ?, ?)
1622
+ `).run(task.project_id, task.agent_id ?? null, task.parent_task_id ?? null, task.name, task.description ?? null);
1623
+ return Number(result.lastInsertRowid);
1624
+ }
1625
+ updateTask(taskId, updates) {
1626
+ const fields = ["updated_at = unixepoch()"];
1627
+ const values = [];
1628
+ if (updates.status !== undefined) {
1629
+ fields.push("status = ?");
1630
+ values.push(updates.status);
1631
+ }
1632
+ if (updates.progress !== undefined) {
1633
+ fields.push("progress = ?");
1634
+ values.push(updates.progress);
1635
+ }
1636
+ if (updates.result !== undefined) {
1637
+ fields.push("result = ?");
1638
+ values.push(updates.result);
1639
+ }
1640
+ if (updates.agent_id !== undefined) {
1641
+ fields.push("agent_id = ?");
1642
+ values.push(updates.agent_id);
1643
+ }
1644
+ values.push(taskId);
1645
+ const res = this.db.query(`UPDATE tasks SET ${fields.join(", ")} WHERE id = ?`).run(...values);
1646
+ return res.changes > 0;
1647
+ }
1648
+ getTasksByProject(projectId) {
1649
+ return this.db.query("SELECT * FROM tasks WHERE project_id = ? ORDER BY id ASC").all(projectId);
1650
+ }
1651
+ getProjectWithTasks(projectId) {
1652
+ const project = this.db.query("SELECT * FROM projects WHERE id = ?").get(projectId);
1653
+ if (!project)
1654
+ return null;
1655
+ project.tasks = this.getTasksByProject(projectId);
1656
+ return project;
1657
+ }
1658
+ recalcProjectProgress(projectId) {
1659
+ const row = this.db.query("SELECT AVG(progress) as avg_progress FROM tasks WHERE project_id = ?").get(projectId);
1660
+ const avg = Math.round(row?.avg_progress ?? 0);
1661
+ this.db.query("UPDATE projects SET progress = ?, updated_at = unixepoch() WHERE id = ?").run(avg, projectId);
1662
+ return avg;
1663
+ }
1664
+ saveMCPServer(server) {
1665
+ try {
1666
+ this.db.query(`
1667
+ INSERT OR REPLACE INTO mcp_servers (id, name, transport, command, args, url, env_encrypted, env_iv, headers_encrypted, headers_iv, enabled, active, builtin, tools_count, status)
1668
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1669
+ `).run(server.id || server.name, server.name, server.transport, server.command || null, JSON.stringify(server.args || []), server.url || null, server.env_encrypted || null, server.env_iv || null, server.headers_encrypted || null, server.headers_iv || null, server.enabled ? 1 : 0, server.active ? 1 : 0, server.builtin ? 1 : 0, server.tools_count || 0, server.status || "disconnected");
1670
+ } catch (error) {
1671
+ this.log.error(`Failed to save MCP server ${server.name}: ${error.message}`);
1672
+ }
1673
+ }
1674
+ }
1675
+ var _db = null, dbService;
1676
+ var init_sqlite = __esm(() => {
1677
+ init_logger();
1678
+ init_loader();
1679
+ dbService = new DatabaseService;
1680
+ });
1681
+
1682
+ // ../../node_modules/.bun/croner@10.0.1/node_modules/croner/dist/croner.js
1683
+ function T(s) {
1684
+ return Date.UTC(s.y, s.m - 1, s.d, s.h, s.i, s.s);
1685
+ }
1686
+ function D(s, e) {
1687
+ return s.y === e.y && s.m === e.m && s.d === e.d && s.h === e.h && s.i === e.i && s.s === e.s;
1688
+ }
1689
+ function A(s, e) {
1690
+ let t = new Date(Date.parse(s));
1691
+ if (isNaN(t))
1692
+ throw new Error("Invalid ISO8601 passed to timezone parser.");
1693
+ let r = s.substring(9);
1694
+ return r.includes("Z") || r.includes("+") || r.includes("-") ? b(t.getUTCFullYear(), t.getUTCMonth() + 1, t.getUTCDate(), t.getUTCHours(), t.getUTCMinutes(), t.getUTCSeconds(), "Etc/UTC") : b(t.getFullYear(), t.getMonth() + 1, t.getDate(), t.getHours(), t.getMinutes(), t.getSeconds(), e);
1695
+ }
1696
+ function v(s, e, t) {
1697
+ return k(A(s, e), t);
1698
+ }
1699
+ function k(s, e) {
1700
+ let t = new Date(T(s)), r = g(t, s.tz), n = T(s), i = T(r), a = n - i, o = new Date(t.getTime() + a), h = g(o, s.tz);
1701
+ if (D(h, s)) {
1702
+ let u = new Date(o.getTime() - 3600000), d = g(u, s.tz);
1703
+ return D(d, s) ? u : o;
1704
+ }
1705
+ let l = new Date(o.getTime() + T(s) - T(h)), y = g(l, s.tz);
1706
+ if (D(y, s))
1707
+ return l;
1708
+ if (e)
1709
+ throw new Error("Invalid date passed to fromTZ()");
1710
+ return o.getTime() > l.getTime() ? o : l;
1711
+ }
1712
+ function g(s, e) {
1713
+ let t, r;
1714
+ try {
1715
+ t = new Intl.DateTimeFormat("en-US", { timeZone: e, year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false }), r = t.formatToParts(s);
1716
+ } catch (i) {
1717
+ let a = i instanceof Error ? i.message : String(i);
1718
+ throw new RangeError(`toTZ: Invalid timezone '${e}' or date. Please provide a valid IANA timezone (e.g., 'America/New_York', 'Europe/Stockholm'). Original error: ${a}`);
1719
+ }
1720
+ let n = { year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0 };
1721
+ for (let i of r)
1722
+ (i.type === "year" || i.type === "month" || i.type === "day" || i.type === "hour" || i.type === "minute" || i.type === "second") && (n[i.type] = parseInt(i.value, 10));
1723
+ if (isNaN(n.year) || isNaN(n.month) || isNaN(n.day) || isNaN(n.hour) || isNaN(n.minute) || isNaN(n.second))
1724
+ throw new Error(`toTZ: Failed to parse all date components from timezone '${e}'. This may indicate an invalid date or timezone configuration. Parsed components: ${JSON.stringify(n)}`);
1725
+ return n.hour === 24 && (n.hour = 0), { y: n.year, m: n.month, d: n.day, h: n.hour, i: n.minute, s: n.second, tz: e };
1726
+ }
1727
+ function b(s, e, t, r, n, i, a) {
1728
+ return { y: s, m: e, d: t, h: r, i: n, s: i, tz: a };
1729
+ }
1730
+ function R(s2) {
1731
+ if (s2 === undefined && (s2 = {}), delete s2.name, s2.legacyMode !== undefined && s2.domAndDow === undefined ? s2.domAndDow = !s2.legacyMode : s2.domAndDow === undefined && (s2.domAndDow = false), s2.legacyMode = !s2.domAndDow, s2.paused = s2.paused === undefined ? false : s2.paused, s2.maxRuns = s2.maxRuns === undefined ? 1 / 0 : s2.maxRuns, s2.catch = s2.catch === undefined ? false : s2.catch, s2.interval = s2.interval === undefined ? 0 : parseInt(s2.interval.toString(), 10), s2.utcOffset = s2.utcOffset === undefined ? undefined : parseInt(s2.utcOffset.toString(), 10), s2.dayOffset = s2.dayOffset === undefined ? 0 : parseInt(s2.dayOffset.toString(), 10), s2.unref = s2.unref === undefined ? false : s2.unref, s2.mode = s2.mode === undefined ? "auto" : s2.mode, s2.alternativeWeekdays = s2.alternativeWeekdays === undefined ? false : s2.alternativeWeekdays, s2.sloppyRanges = s2.sloppyRanges === undefined ? false : s2.sloppyRanges, !["auto", "5-part", "6-part", "7-part", "5-or-6-parts", "6-or-7-parts"].includes(s2.mode))
1732
+ throw new Error("CronOptions: mode must be one of 'auto', '5-part', '6-part', '7-part', '5-or-6-parts', or '6-or-7-parts'.");
1733
+ if (s2.startAt && (s2.startAt = new m(s2.startAt, s2.timezone)), s2.stopAt && (s2.stopAt = new m(s2.stopAt, s2.timezone)), s2.interval !== null) {
1734
+ if (isNaN(s2.interval))
1735
+ throw new Error("CronOptions: Supplied value for interval is not a number");
1736
+ if (s2.interval < 0)
1737
+ throw new Error("CronOptions: Supplied value for interval can not be negative");
1738
+ }
1739
+ if (s2.utcOffset !== undefined) {
1740
+ if (isNaN(s2.utcOffset))
1741
+ throw new Error("CronOptions: Invalid value passed for utcOffset, should be number representing minutes offset from UTC.");
1742
+ if (s2.utcOffset < -870 || s2.utcOffset > 870)
1743
+ throw new Error("CronOptions: utcOffset out of bounds.");
1744
+ if (s2.utcOffset !== undefined && s2.timezone)
1745
+ throw new Error("CronOptions: Combining 'utcOffset' with 'timezone' is not allowed.");
1746
+ }
1747
+ if (s2.unref !== true && s2.unref !== false)
1748
+ throw new Error("CronOptions: Unref should be either true, false or undefined(false).");
1749
+ if (s2.dayOffset !== undefined && s2.dayOffset !== 0 && isNaN(s2.dayOffset))
1750
+ throw new Error("CronOptions: Invalid value passed for dayOffset, should be a number representing days to offset.");
1751
+ return s2;
1752
+ }
1753
+ function p(s2) {
1754
+ return Object.prototype.toString.call(s2) === "[object Function]" || typeof s2 == "function" || s2 instanceof Function;
1755
+ }
1756
+ function _(s2) {
1757
+ return p(s2);
1758
+ }
1759
+ function x(s2) {
1760
+ typeof Deno < "u" && typeof Deno.unrefTimer < "u" ? Deno.unrefTimer(s2) : s2 && typeof s2.unref < "u" && s2.unref();
1761
+ }
1762
+ var O, C = class {
1763
+ pattern;
1764
+ timezone;
1765
+ mode;
1766
+ alternativeWeekdays;
1767
+ sloppyRanges;
1768
+ second;
1769
+ minute;
1770
+ hour;
1771
+ day;
1772
+ month;
1773
+ dayOfWeek;
1774
+ year;
1775
+ lastDayOfMonth;
1776
+ lastWeekday;
1777
+ nearestWeekdays;
1778
+ starDOM;
1779
+ starDOW;
1780
+ starYear;
1781
+ useAndLogic;
1782
+ constructor(e, t, r) {
1783
+ this.pattern = e, this.timezone = t, this.mode = r?.mode ?? "auto", this.alternativeWeekdays = r?.alternativeWeekdays ?? false, this.sloppyRanges = r?.sloppyRanges ?? false, this.second = Array(60).fill(0), this.minute = Array(60).fill(0), this.hour = Array(24).fill(0), this.day = Array(31).fill(0), this.month = Array(12).fill(0), this.dayOfWeek = Array(7).fill(0), this.year = Array(1e4).fill(0), this.lastDayOfMonth = false, this.lastWeekday = false, this.nearestWeekdays = Array(31).fill(0), this.starDOM = false, this.starDOW = false, this.starYear = false, this.useAndLogic = false, this.parse();
1784
+ }
1785
+ parse() {
1786
+ if (!(typeof this.pattern == "string" || this.pattern instanceof String))
1787
+ throw new TypeError("CronPattern: Pattern has to be of type string.");
1788
+ this.pattern.indexOf("@") >= 0 && (this.pattern = this.handleNicknames(this.pattern).trim());
1789
+ let e = this.pattern.match(/\S+/g) || [""], t = e.length;
1790
+ if (e.length < 5 || e.length > 7)
1791
+ throw new TypeError("CronPattern: invalid configuration format ('" + this.pattern + "'), exactly five, six, or seven space separated parts are required.");
1792
+ if (this.mode !== "auto") {
1793
+ let n;
1794
+ switch (this.mode) {
1795
+ case "5-part":
1796
+ n = 5;
1797
+ break;
1798
+ case "6-part":
1799
+ n = 6;
1800
+ break;
1801
+ case "7-part":
1802
+ n = 7;
1803
+ break;
1804
+ case "5-or-6-parts":
1805
+ n = [5, 6];
1806
+ break;
1807
+ case "6-or-7-parts":
1808
+ n = [6, 7];
1809
+ break;
1810
+ default:
1811
+ n = 0;
1812
+ }
1813
+ if (!(Array.isArray(n) ? n.includes(t) : t === n)) {
1814
+ let a = Array.isArray(n) ? n.join(" or ") : n.toString();
1815
+ throw new TypeError(`CronPattern: mode '${this.mode}' requires exactly ${a} parts, but pattern '${this.pattern}' has ${t} parts.`);
1816
+ }
1817
+ }
1818
+ if (e.length === 5 && e.unshift("0"), e.length === 6 && e.push("*"), e[3].toUpperCase() === "LW" ? (this.lastWeekday = true, e[3] = "") : e[3].toUpperCase().indexOf("L") >= 0 && (e[3] = e[3].replace(/L/gi, ""), this.lastDayOfMonth = true), e[3] == "*" && (this.starDOM = true), e[6] == "*" && (this.starYear = true), e[4].length >= 3 && (e[4] = this.replaceAlphaMonths(e[4])), e[5].length >= 3 && (e[5] = this.alternativeWeekdays ? this.replaceAlphaDaysQuartz(e[5]) : this.replaceAlphaDays(e[5])), e[5].startsWith("+") && (this.useAndLogic = true, e[5] = e[5].substring(1), e[5] === ""))
1819
+ throw new TypeError("CronPattern: Day-of-week field cannot be empty after '+' modifier.");
1820
+ switch (e[5] == "*" && (this.starDOW = true), this.pattern.indexOf("?") >= 0 && (e[0] = e[0].replace(/\?/g, "*"), e[1] = e[1].replace(/\?/g, "*"), e[2] = e[2].replace(/\?/g, "*"), e[3] = e[3].replace(/\?/g, "*"), e[4] = e[4].replace(/\?/g, "*"), e[5] = e[5].replace(/\?/g, "*"), e[6] && (e[6] = e[6].replace(/\?/g, "*"))), this.mode) {
1821
+ case "5-part":
1822
+ e[0] = "0", e[6] = "*";
1823
+ break;
1824
+ case "6-part":
1825
+ e[6] = "*";
1826
+ break;
1827
+ case "5-or-6-parts":
1828
+ e[6] = "*";
1829
+ break;
1830
+ case "6-or-7-parts":
1831
+ break;
1832
+ case "7-part":
1833
+ case "auto":
1834
+ break;
1835
+ }
1836
+ this.throwAtIllegalCharacters(e), this.partToArray("second", e[0], 0, 1), this.partToArray("minute", e[1], 0, 1), this.partToArray("hour", e[2], 0, 1), this.partToArray("day", e[3], -1, 1), this.partToArray("month", e[4], -1, 1);
1837
+ let r = this.alternativeWeekdays ? -1 : 0;
1838
+ this.partToArray("dayOfWeek", e[5], r, 63), this.partToArray("year", e[6], 0, 1), !this.alternativeWeekdays && this.dayOfWeek[7] && (this.dayOfWeek[0] = this.dayOfWeek[7]);
1839
+ }
1840
+ partToArray(e, t, r, n) {
1841
+ let i = this[e], a = e === "day" && this.lastDayOfMonth, o = e === "day" && this.lastWeekday;
1842
+ if (t === "" && !a && !o)
1843
+ throw new TypeError("CronPattern: configuration entry " + e + " (" + t + ") is empty, check for trailing spaces.");
1844
+ if (t === "*")
1845
+ return i.fill(n);
1846
+ let h = t.split(",");
1847
+ if (h.length > 1)
1848
+ for (let l = 0;l < h.length; l++)
1849
+ this.partToArray(e, h[l], r, n);
1850
+ else
1851
+ t.indexOf("-") !== -1 && t.indexOf("/") !== -1 ? this.handleRangeWithStepping(t, e, r, n) : t.indexOf("-") !== -1 ? this.handleRange(t, e, r, n) : t.indexOf("/") !== -1 ? this.handleStepping(t, e, r, n) : t !== "" && this.handleNumber(t, e, r, n);
1852
+ }
1853
+ throwAtIllegalCharacters(e) {
1854
+ for (let t = 0;t < e.length; t++)
1855
+ if ((t === 3 ? /[^/*0-9,\-WwLl]+/ : t === 5 ? /[^/*0-9,\-#Ll]+/ : /[^/*0-9,\-]+/).test(e[t]))
1856
+ throw new TypeError("CronPattern: configuration entry " + t + " (" + e[t] + ") contains illegal characters.");
1857
+ }
1858
+ handleNumber(e, t, r, n) {
1859
+ let i = this.extractNth(e, t), a = e.toUpperCase().includes("W");
1860
+ if (t !== "day" && a)
1861
+ throw new TypeError("CronPattern: Nearest weekday modifier (W) only allowed in day-of-month.");
1862
+ a && (t = "nearestWeekdays");
1863
+ let o = parseInt(i[0], 10) + r;
1864
+ if (isNaN(o))
1865
+ throw new TypeError("CronPattern: " + t + " is not a number: '" + e + "'");
1866
+ this.setPart(t, o, i[1] || n);
1867
+ }
1868
+ setPart(e, t, r) {
1869
+ if (!Object.prototype.hasOwnProperty.call(this, e))
1870
+ throw new TypeError("CronPattern: Invalid part specified: " + e);
1871
+ if (e === "dayOfWeek") {
1872
+ if (t === 7 && (t = 0), t < 0 || t > 6)
1873
+ throw new RangeError("CronPattern: Invalid value for dayOfWeek: " + t);
1874
+ this.setNthWeekdayOfMonth(t, r);
1875
+ return;
1876
+ }
1877
+ if (e === "second" || e === "minute") {
1878
+ if (t < 0 || t >= 60)
1879
+ throw new RangeError("CronPattern: Invalid value for " + e + ": " + t);
1880
+ } else if (e === "hour") {
1881
+ if (t < 0 || t >= 24)
1882
+ throw new RangeError("CronPattern: Invalid value for " + e + ": " + t);
1883
+ } else if (e === "day" || e === "nearestWeekdays") {
1884
+ if (t < 0 || t >= 31)
1885
+ throw new RangeError("CronPattern: Invalid value for " + e + ": " + t);
1886
+ } else if (e === "month") {
1887
+ if (t < 0 || t >= 12)
1888
+ throw new RangeError("CronPattern: Invalid value for " + e + ": " + t);
1889
+ } else if (e === "year" && (t < 1 || t >= 1e4))
1890
+ throw new RangeError("CronPattern: Invalid value for " + e + ": " + t + " (supported range: 1-9999)");
1891
+ this[e][t] = r;
1892
+ }
1893
+ validateNotNaN(e, t) {
1894
+ if (isNaN(e))
1895
+ throw new TypeError(t);
1896
+ }
1897
+ validateRange(e, t, r, n, i) {
1898
+ if (e > t)
1899
+ throw new TypeError("CronPattern: From value is larger than to value: '" + i + "'");
1900
+ if (r !== undefined) {
1901
+ if (r === 0)
1902
+ throw new TypeError("CronPattern: Syntax error, illegal stepping: 0");
1903
+ if (r > this[n].length)
1904
+ throw new TypeError("CronPattern: Syntax error, steps cannot be greater than maximum value of part (" + this[n].length + ")");
1905
+ }
1906
+ }
1907
+ handleRangeWithStepping(e, t, r, n) {
1908
+ if (e.toUpperCase().includes("W"))
1909
+ throw new TypeError("CronPattern: Syntax error, W is not allowed in ranges with stepping.");
1910
+ let i = this.extractNth(e, t), a = i[0].match(/^(\d+)-(\d+)\/(\d+)$/);
1911
+ if (a === null)
1912
+ throw new TypeError("CronPattern: Syntax error, illegal range with stepping: '" + e + "'");
1913
+ let [, o, h, l] = a, y = parseInt(o, 10) + r, u = parseInt(h, 10) + r, d = parseInt(l, 10);
1914
+ this.validateNotNaN(y, "CronPattern: Syntax error, illegal lower range (NaN)"), this.validateNotNaN(u, "CronPattern: Syntax error, illegal upper range (NaN)"), this.validateNotNaN(d, "CronPattern: Syntax error, illegal stepping: (NaN)"), this.validateRange(y, u, d, t, e);
1915
+ for (let c = y;c <= u; c += d)
1916
+ this.setPart(t, c, i[1] || n);
1917
+ }
1918
+ extractNth(e, t) {
1919
+ let r = e, n;
1920
+ if (r.includes("#")) {
1921
+ if (t !== "dayOfWeek")
1922
+ throw new Error("CronPattern: nth (#) only allowed in day-of-week field");
1923
+ n = r.split("#")[1], r = r.split("#")[0];
1924
+ } else if (r.toUpperCase().endsWith("L")) {
1925
+ if (t !== "dayOfWeek")
1926
+ throw new Error("CronPattern: L modifier only allowed in day-of-week field (use L alone for day-of-month)");
1927
+ n = "L", r = r.slice(0, -1);
1928
+ }
1929
+ return [r, n];
1930
+ }
1931
+ handleRange(e, t, r, n) {
1932
+ if (e.toUpperCase().includes("W"))
1933
+ throw new TypeError("CronPattern: Syntax error, W is not allowed in a range.");
1934
+ let i = this.extractNth(e, t), a = i[0].split("-");
1935
+ if (a.length !== 2)
1936
+ throw new TypeError("CronPattern: Syntax error, illegal range: '" + e + "'");
1937
+ let o = parseInt(a[0], 10) + r, h = parseInt(a[1], 10) + r;
1938
+ this.validateNotNaN(o, "CronPattern: Syntax error, illegal lower range (NaN)"), this.validateNotNaN(h, "CronPattern: Syntax error, illegal upper range (NaN)"), this.validateRange(o, h, undefined, t, e);
1939
+ for (let l = o;l <= h; l++)
1940
+ this.setPart(t, l, i[1] || n);
1941
+ }
1942
+ handleStepping(e, t, r, n) {
1943
+ if (e.toUpperCase().includes("W"))
1944
+ throw new TypeError("CronPattern: Syntax error, W is not allowed in parts with stepping.");
1945
+ let i = this.extractNth(e, t), a = i[0].split("/");
1946
+ if (a.length !== 2)
1947
+ throw new TypeError("CronPattern: Syntax error, illegal stepping: '" + e + "'");
1948
+ if (this.sloppyRanges)
1949
+ a[0] === "" && (a[0] = "*");
1950
+ else {
1951
+ if (a[0] === "")
1952
+ throw new TypeError("CronPattern: Syntax error, stepping with missing prefix ('" + e + "') is not allowed. Use wildcard (*/step) or range (min-max/step) instead.");
1953
+ if (a[0] !== "*")
1954
+ throw new TypeError("CronPattern: Syntax error, stepping with numeric prefix ('" + e + "') is not allowed. Use wildcard (*/step) or range (min-max/step) instead.");
1955
+ }
1956
+ let o = 0;
1957
+ a[0] !== "*" && (o = parseInt(a[0], 10) + r);
1958
+ let h = parseInt(a[1], 10);
1959
+ this.validateNotNaN(h, "CronPattern: Syntax error, illegal stepping: (NaN)"), this.validateRange(0, this[t].length - 1, h, t, e);
1960
+ for (let l = o;l < this[t].length; l += h)
1961
+ this.setPart(t, l, i[1] || n);
1962
+ }
1963
+ replaceAlphaDays(e) {
1964
+ return e.replace(/-sun/gi, "-7").replace(/sun/gi, "0").replace(/mon/gi, "1").replace(/tue/gi, "2").replace(/wed/gi, "3").replace(/thu/gi, "4").replace(/fri/gi, "5").replace(/sat/gi, "6");
1965
+ }
1966
+ replaceAlphaDaysQuartz(e) {
1967
+ return e.replace(/sun/gi, "1").replace(/mon/gi, "2").replace(/tue/gi, "3").replace(/wed/gi, "4").replace(/thu/gi, "5").replace(/fri/gi, "6").replace(/sat/gi, "7");
1968
+ }
1969
+ replaceAlphaMonths(e) {
1970
+ return e.replace(/jan/gi, "1").replace(/feb/gi, "2").replace(/mar/gi, "3").replace(/apr/gi, "4").replace(/may/gi, "5").replace(/jun/gi, "6").replace(/jul/gi, "7").replace(/aug/gi, "8").replace(/sep/gi, "9").replace(/oct/gi, "10").replace(/nov/gi, "11").replace(/dec/gi, "12");
1971
+ }
1972
+ handleNicknames(e) {
1973
+ let t = e.trim().toLowerCase();
1974
+ if (t === "@yearly" || t === "@annually")
1975
+ return "0 0 1 1 *";
1976
+ if (t === "@monthly")
1977
+ return "0 0 1 * *";
1978
+ if (t === "@weekly")
1979
+ return "0 0 * * 0";
1980
+ if (t === "@daily" || t === "@midnight")
1981
+ return "0 0 * * *";
1982
+ if (t === "@hourly")
1983
+ return "0 * * * *";
1984
+ if (t === "@reboot")
1985
+ throw new TypeError("CronPattern: @reboot is not supported in this environment. This is an event-based trigger that requires system startup detection.");
1986
+ return e;
1987
+ }
1988
+ setNthWeekdayOfMonth(e, t) {
1989
+ if (typeof t != "number" && t.toUpperCase() === "L")
1990
+ this.dayOfWeek[e] = this.dayOfWeek[e] | 32;
1991
+ else if (t === 63)
1992
+ this.dayOfWeek[e] = 63;
1993
+ else if (t < 6 && t > 0)
1994
+ this.dayOfWeek[e] = this.dayOfWeek[e] | O[t - 1];
1995
+ else
1996
+ throw new TypeError(`CronPattern: nth weekday out of range, should be 1-5 or L. Value: ${t}, Type: ${typeof t}`);
1997
+ }
1998
+ }, P, f, m = class s {
1999
+ tz;
2000
+ ms;
2001
+ second;
2002
+ minute;
2003
+ hour;
2004
+ day;
2005
+ month;
2006
+ year;
2007
+ constructor(e, t) {
2008
+ if (this.tz = t, e && e instanceof Date)
2009
+ if (!isNaN(e))
2010
+ this.fromDate(e);
2011
+ else
2012
+ throw new TypeError("CronDate: Invalid date passed to CronDate constructor");
2013
+ else if (e == null)
2014
+ this.fromDate(new Date);
2015
+ else if (e && typeof e == "string")
2016
+ this.fromString(e);
2017
+ else if (e instanceof s)
2018
+ this.fromCronDate(e);
2019
+ else
2020
+ throw new TypeError("CronDate: Invalid type (" + typeof e + ") passed to CronDate constructor");
2021
+ }
2022
+ getLastDayOfMonth(e, t) {
2023
+ return t !== 1 ? P[t] : new Date(Date.UTC(e, t + 1, 0)).getUTCDate();
2024
+ }
2025
+ getLastWeekday(e, t) {
2026
+ let r = this.getLastDayOfMonth(e, t), i = new Date(Date.UTC(e, t, r)).getUTCDay();
2027
+ return i === 0 ? r - 2 : i === 6 ? r - 1 : r;
2028
+ }
2029
+ getNearestWeekday(e, t, r) {
2030
+ let n = this.getLastDayOfMonth(e, t);
2031
+ if (r > n)
2032
+ return -1;
2033
+ let a = new Date(Date.UTC(e, t, r)).getUTCDay();
2034
+ return a === 0 ? r === n ? r - 2 : r + 1 : a === 6 ? r === 1 ? r + 2 : r - 1 : r;
2035
+ }
2036
+ isNthWeekdayOfMonth(e, t, r, n) {
2037
+ let a = new Date(Date.UTC(e, t, r)).getUTCDay(), o = 0;
2038
+ for (let h = 1;h <= r; h++)
2039
+ new Date(Date.UTC(e, t, h)).getUTCDay() === a && o++;
2040
+ if (n & 63 && O[o - 1] & n)
2041
+ return true;
2042
+ if (n & 32) {
2043
+ let h = this.getLastDayOfMonth(e, t);
2044
+ for (let l = r + 1;l <= h; l++)
2045
+ if (new Date(Date.UTC(e, t, l)).getUTCDay() === a)
2046
+ return false;
2047
+ return true;
2048
+ }
2049
+ return false;
2050
+ }
2051
+ fromDate(e) {
2052
+ if (this.tz !== undefined)
2053
+ if (typeof this.tz == "number")
2054
+ this.ms = e.getUTCMilliseconds(), this.second = e.getUTCSeconds(), this.minute = e.getUTCMinutes() + this.tz, this.hour = e.getUTCHours(), this.day = e.getUTCDate(), this.month = e.getUTCMonth(), this.year = e.getUTCFullYear(), this.apply();
2055
+ else
2056
+ try {
2057
+ let t = g(e, this.tz);
2058
+ this.ms = e.getMilliseconds(), this.second = t.s, this.minute = t.i, this.hour = t.h, this.day = t.d, this.month = t.m - 1, this.year = t.y;
2059
+ } catch (t) {
2060
+ let r = t instanceof Error ? t.message : String(t);
2061
+ throw new TypeError(`CronDate: Failed to convert date to timezone '${this.tz}'. This may happen with invalid timezone names or dates. Original error: ${r}`);
2062
+ }
2063
+ else
2064
+ this.ms = e.getMilliseconds(), this.second = e.getSeconds(), this.minute = e.getMinutes(), this.hour = e.getHours(), this.day = e.getDate(), this.month = e.getMonth(), this.year = e.getFullYear();
2065
+ }
2066
+ fromCronDate(e) {
2067
+ this.tz = e.tz, this.year = e.year, this.month = e.month, this.day = e.day, this.hour = e.hour, this.minute = e.minute, this.second = e.second, this.ms = e.ms;
2068
+ }
2069
+ apply() {
2070
+ if (this.month > 11 || this.month < 0 || this.day > P[this.month] || this.day < 1 || this.hour > 59 || this.minute > 59 || this.second > 59 || this.hour < 0 || this.minute < 0 || this.second < 0) {
2071
+ let e = new Date(Date.UTC(this.year, this.month, this.day, this.hour, this.minute, this.second, this.ms));
2072
+ return this.ms = e.getUTCMilliseconds(), this.second = e.getUTCSeconds(), this.minute = e.getUTCMinutes(), this.hour = e.getUTCHours(), this.day = e.getUTCDate(), this.month = e.getUTCMonth(), this.year = e.getUTCFullYear(), true;
2073
+ } else
2074
+ return false;
2075
+ }
2076
+ fromString(e) {
2077
+ if (typeof this.tz == "number") {
2078
+ let t = v(e);
2079
+ this.ms = t.getUTCMilliseconds(), this.second = t.getUTCSeconds(), this.minute = t.getUTCMinutes(), this.hour = t.getUTCHours(), this.day = t.getUTCDate(), this.month = t.getUTCMonth(), this.year = t.getUTCFullYear(), this.apply();
2080
+ } else
2081
+ return this.fromDate(v(e, this.tz));
2082
+ }
2083
+ findNext(e, t, r, n) {
2084
+ return this._findMatch(e, t, r, n, 1);
2085
+ }
2086
+ _findMatch(e, t, r, n, i) {
2087
+ let a = this[t], o;
2088
+ r.lastDayOfMonth && (o = this.getLastDayOfMonth(this.year, this.month));
2089
+ let h = !r.starDOW && t == "day" ? new Date(Date.UTC(this.year, this.month, 1, 0, 0, 0, 0)).getUTCDay() : undefined, l = this[t] + n, y = i === 1 ? (u) => u < r[t].length : (u) => u >= 0;
2090
+ for (let u = l;y(u); u += i) {
2091
+ let d = r[t][u];
2092
+ if (t === "day" && !d) {
2093
+ for (let c = 0;c < r.nearestWeekdays.length; c++)
2094
+ if (r.nearestWeekdays[c]) {
2095
+ let M = this.getNearestWeekday(this.year, this.month, c - n);
2096
+ if (M === -1)
2097
+ continue;
2098
+ if (M === u - n) {
2099
+ d = 1;
2100
+ break;
2101
+ }
2102
+ }
2103
+ }
2104
+ if (t === "day" && r.lastWeekday) {
2105
+ let c = this.getLastWeekday(this.year, this.month);
2106
+ u - n === c && (d = 1);
2107
+ }
2108
+ if (t === "day" && r.lastDayOfMonth && u - n == o && (d = 1), t === "day" && !r.starDOW) {
2109
+ let c = r.dayOfWeek[(h + (u - n - 1)) % 7];
2110
+ if (c && c & 63)
2111
+ c = this.isNthWeekdayOfMonth(this.year, this.month, u - n, c) ? 1 : 0;
2112
+ else if (c)
2113
+ throw new Error(`CronDate: Invalid value for dayOfWeek encountered. ${c}`);
2114
+ r.useAndLogic ? d = d && c : !e.domAndDow && !r.starDOM ? d = d || c : d = d && c;
2115
+ }
2116
+ if (d)
2117
+ return this[t] = u - n, a !== this[t] ? 2 : 1;
2118
+ }
2119
+ return 3;
2120
+ }
2121
+ recurse(e, t, r) {
2122
+ if (r === 0 && !e.starYear) {
2123
+ if (this.year >= 0 && this.year < e.year.length && e.year[this.year] === 0) {
2124
+ let i = -1;
2125
+ for (let a = this.year + 1;a < e.year.length && a < 1e4; a++)
2126
+ if (e.year[a] === 1) {
2127
+ i = a;
2128
+ break;
2129
+ }
2130
+ if (i === -1)
2131
+ return null;
2132
+ this.year = i, this.month = 0, this.day = 1, this.hour = 0, this.minute = 0, this.second = 0, this.ms = 0;
2133
+ }
2134
+ if (this.year >= 1e4)
2135
+ return null;
2136
+ }
2137
+ let n = this.findNext(t, f[r][0], e, f[r][2]);
2138
+ if (n > 1) {
2139
+ let i = r + 1;
2140
+ for (;i < f.length; )
2141
+ this[f[i][0]] = -f[i][2], i++;
2142
+ if (n === 3) {
2143
+ if (this[f[r][1]]++, this[f[r][0]] = -f[r][2], this.apply(), r === 0 && !e.starYear) {
2144
+ for (;this.year >= 0 && this.year < e.year.length && e.year[this.year] === 0 && this.year < 1e4; )
2145
+ this.year++;
2146
+ if (this.year >= 1e4 || this.year >= e.year.length)
2147
+ return null;
2148
+ }
2149
+ return this.recurse(e, t, 0);
2150
+ } else if (this.apply())
2151
+ return this.recurse(e, t, r - 1);
2152
+ }
2153
+ return r += 1, r >= f.length ? this : (e.starYear ? this.year >= 3000 : this.year >= 1e4) ? null : this.recurse(e, t, r);
2154
+ }
2155
+ increment(e, t, r) {
2156
+ return this.second += t.interval !== undefined && t.interval > 1 && r ? t.interval : 1, this.ms = 0, this.apply(), this.recurse(e, t, 0);
2157
+ }
2158
+ decrement(e, t) {
2159
+ return this.second -= t.interval !== undefined && t.interval > 1 ? t.interval : 1, this.ms = 0, this.apply(), this.recurseBackward(e, t, 0, 0);
2160
+ }
2161
+ recurseBackward(e, t, r, n = 0) {
2162
+ if (n > 1e4)
2163
+ return null;
2164
+ if (r === 0 && !e.starYear) {
2165
+ if (this.year >= 0 && this.year < e.year.length && e.year[this.year] === 0) {
2166
+ let a = -1;
2167
+ for (let o = this.year - 1;o >= 0; o--)
2168
+ if (e.year[o] === 1) {
2169
+ a = o;
2170
+ break;
2171
+ }
2172
+ if (a === -1)
2173
+ return null;
2174
+ this.year = a, this.month = 11, this.day = 31, this.hour = 23, this.minute = 59, this.second = 59, this.ms = 0;
2175
+ }
2176
+ if (this.year < 0)
2177
+ return null;
2178
+ }
2179
+ let i = this.findPrevious(t, f[r][0], e, f[r][2]);
2180
+ if (i > 1) {
2181
+ let a = r + 1;
2182
+ for (;a < f.length; ) {
2183
+ let o = f[a][0], h = f[a][2], l = this.getMaxPatternValue(o, e, h);
2184
+ this[o] = l, a++;
2185
+ }
2186
+ if (i === 3) {
2187
+ if (this[f[r][1]]--, r === 0) {
2188
+ let y = this.getLastDayOfMonth(this.year, this.month);
2189
+ this.day > y && (this.day = y);
2190
+ }
2191
+ if (r === 1)
2192
+ if (this.day <= 0)
2193
+ this.day = 1;
2194
+ else {
2195
+ let y = this.year, u = this.month;
2196
+ for (;u < 0; )
2197
+ u += 12, y--;
2198
+ for (;u > 11; )
2199
+ u -= 12, y++;
2200
+ let d = u !== 1 ? P[u] : new Date(Date.UTC(y, u + 1, 0)).getUTCDate();
2201
+ this.day > d && (this.day = d);
2202
+ }
2203
+ this.apply();
2204
+ let o = f[r][0], h = f[r][2], l = this.getMaxPatternValue(o, e, h);
2205
+ if (o === "day") {
2206
+ let y = this.getLastDayOfMonth(this.year, this.month);
2207
+ this[o] = Math.min(l, y);
2208
+ } else
2209
+ this[o] = l;
2210
+ if (this.apply(), r === 0) {
2211
+ let y = f[1][2], u = this.getMaxPatternValue("day", e, y), d = this.getLastDayOfMonth(this.year, this.month), c = Math.min(u, d);
2212
+ c !== this.day && (this.day = c, this.hour = this.getMaxPatternValue("hour", e, f[2][2]), this.minute = this.getMaxPatternValue("minute", e, f[3][2]), this.second = this.getMaxPatternValue("second", e, f[4][2]));
2213
+ }
2214
+ if (r === 0 && !e.starYear) {
2215
+ for (;this.year >= 0 && this.year < e.year.length && e.year[this.year] === 0; )
2216
+ this.year--;
2217
+ if (this.year < 0)
2218
+ return null;
2219
+ }
2220
+ return this.recurseBackward(e, t, 0, n + 1);
2221
+ } else if (this.apply())
2222
+ return this.recurseBackward(e, t, r - 1, n + 1);
2223
+ }
2224
+ return r += 1, r >= f.length ? this : this.year < 0 ? null : this.recurseBackward(e, t, r, n + 1);
2225
+ }
2226
+ getMaxPatternValue(e, t, r) {
2227
+ if (e === "day" && t.lastDayOfMonth)
2228
+ return this.getLastDayOfMonth(this.year, this.month);
2229
+ if (e === "day" && !t.starDOW)
2230
+ return this.getLastDayOfMonth(this.year, this.month);
2231
+ for (let n = t[e].length - 1;n >= 0; n--)
2232
+ if (t[e][n])
2233
+ return n - r;
2234
+ return t[e].length - 1 - r;
2235
+ }
2236
+ findPrevious(e, t, r, n) {
2237
+ return this._findMatch(e, t, r, n, -1);
2238
+ }
2239
+ getDate(e) {
2240
+ return e || this.tz === undefined ? new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.ms) : typeof this.tz == "number" ? new Date(Date.UTC(this.year, this.month, this.day, this.hour, this.minute - this.tz, this.second, this.ms)) : k(b(this.year, this.month + 1, this.day, this.hour, this.minute, this.second, this.tz), false);
2241
+ }
2242
+ getTime() {
2243
+ return this.getDate(false).getTime();
2244
+ }
2245
+ match(e, t) {
2246
+ if (!e.starYear && (this.year < 0 || this.year >= e.year.length || e.year[this.year] === 0))
2247
+ return false;
2248
+ for (let r = 0;r < f.length; r++) {
2249
+ let n = f[r][0], i = f[r][2], a = this[n];
2250
+ if (a + i < 0 || a + i >= e[n].length)
2251
+ return false;
2252
+ let o = e[n][a + i];
2253
+ if (n === "day") {
2254
+ if (!o) {
2255
+ for (let h = 0;h < e.nearestWeekdays.length; h++)
2256
+ if (e.nearestWeekdays[h]) {
2257
+ let l = this.getNearestWeekday(this.year, this.month, h - i);
2258
+ if (l !== -1 && l === a) {
2259
+ o = 1;
2260
+ break;
2261
+ }
2262
+ }
2263
+ }
2264
+ if (e.lastWeekday) {
2265
+ let h = this.getLastWeekday(this.year, this.month);
2266
+ a === h && (o = 1);
2267
+ }
2268
+ if (e.lastDayOfMonth) {
2269
+ let h = this.getLastDayOfMonth(this.year, this.month);
2270
+ a === h && (o = 1);
2271
+ }
2272
+ if (!e.starDOW) {
2273
+ let h = new Date(Date.UTC(this.year, this.month, 1, 0, 0, 0, 0)).getUTCDay(), l = e.dayOfWeek[(h + (a - 1)) % 7];
2274
+ l && l & 63 && (l = this.isNthWeekdayOfMonth(this.year, this.month, a, l) ? 1 : 0), e.useAndLogic ? o = o && l : !t.domAndDow && !e.starDOM ? o = o || l : o = o && l;
2275
+ }
2276
+ }
2277
+ if (!o)
2278
+ return false;
2279
+ }
2280
+ return true;
2281
+ }
2282
+ }, W, w, E = class {
2283
+ name;
2284
+ options;
2285
+ _states;
2286
+ fn;
2287
+ getTz() {
2288
+ return this.options.timezone || this.options.utcOffset;
2289
+ }
2290
+ applyDayOffset(e) {
2291
+ if (this.options.dayOffset !== undefined && this.options.dayOffset !== 0) {
2292
+ let t = this.options.dayOffset * 24 * 60 * 60 * 1000;
2293
+ return new Date(e.getTime() + t);
2294
+ }
2295
+ return e;
2296
+ }
2297
+ constructor(e, t, r) {
2298
+ let n, i;
2299
+ if (p(t))
2300
+ i = t;
2301
+ else if (typeof t == "object")
2302
+ n = t;
2303
+ else if (t !== undefined)
2304
+ throw new Error("Cron: Invalid argument passed for optionsIn. Should be one of function, or object (options).");
2305
+ if (p(r))
2306
+ i = r;
2307
+ else if (typeof r == "object")
2308
+ n = r;
2309
+ else if (r !== undefined)
2310
+ throw new Error("Cron: Invalid argument passed for funcIn. Should be one of function, or object (options).");
2311
+ if (this.name = n?.name, this.options = R(n), this._states = { kill: false, blocking: false, previousRun: undefined, currentRun: undefined, once: undefined, currentTimeout: undefined, maxRuns: n ? n.maxRuns : undefined, paused: n ? n.paused : false, pattern: new C("* * * * *", undefined, { mode: "auto" }) }, e && (e instanceof Date || typeof e == "string" && e.indexOf(":") > 0) ? this._states.once = new m(e, this.getTz()) : this._states.pattern = new C(e, this.options.timezone, { mode: this.options.mode, alternativeWeekdays: this.options.alternativeWeekdays, sloppyRanges: this.options.sloppyRanges }), this.name) {
2312
+ if (w.find((o) => o.name === this.name))
2313
+ throw new Error("Cron: Tried to initialize new named job '" + this.name + "', but name already taken.");
2314
+ w.push(this);
2315
+ }
2316
+ return i !== undefined && _(i) && (this.fn = i, this.schedule()), this;
2317
+ }
2318
+ nextRun(e) {
2319
+ let t = this._next(e);
2320
+ return t ? this.applyDayOffset(t.getDate(false)) : null;
2321
+ }
2322
+ nextRuns(e, t) {
2323
+ this._states.maxRuns !== undefined && e > this._states.maxRuns && (e = this._states.maxRuns);
2324
+ let r = t || this._states.currentRun || undefined;
2325
+ return this._enumerateRuns(e, r, "next");
2326
+ }
2327
+ previousRuns(e, t) {
2328
+ return this._enumerateRuns(e, t || undefined, "previous");
2329
+ }
2330
+ _enumerateRuns(e, t, r) {
2331
+ let n = [], i = t ? new m(t, this.getTz()) : null, a = r === "next" ? this._next : this._previous;
2332
+ for (;e--; ) {
2333
+ let o = a.call(this, i);
2334
+ if (!o)
2335
+ break;
2336
+ let h = o.getDate(false);
2337
+ n.push(this.applyDayOffset(h)), i = o;
2338
+ }
2339
+ return n;
2340
+ }
2341
+ match(e) {
2342
+ if (this._states.once) {
2343
+ let r = new m(e, this.getTz());
2344
+ r.ms = 0;
2345
+ let n = new m(this._states.once, this.getTz());
2346
+ return n.ms = 0, r.getTime() === n.getTime();
2347
+ }
2348
+ let t = new m(e, this.getTz());
2349
+ return t.ms = 0, t.match(this._states.pattern, this.options);
2350
+ }
2351
+ getPattern() {
2352
+ if (!this._states.once)
2353
+ return this._states.pattern ? this._states.pattern.pattern : undefined;
2354
+ }
2355
+ getOnce() {
2356
+ return this._states.once ? this._states.once.getDate() : null;
2357
+ }
2358
+ isRunning() {
2359
+ let e = this.nextRun(this._states.currentRun), t = !this._states.paused, r = this.fn !== undefined, n = !this._states.kill;
2360
+ return t && r && n && e !== null;
2361
+ }
2362
+ isStopped() {
2363
+ return this._states.kill;
2364
+ }
2365
+ isBusy() {
2366
+ return this._states.blocking;
2367
+ }
2368
+ currentRun() {
2369
+ return this._states.currentRun ? this._states.currentRun.getDate() : null;
2370
+ }
2371
+ previousRun() {
2372
+ return this._states.previousRun ? this._states.previousRun.getDate() : null;
2373
+ }
2374
+ msToNext(e) {
2375
+ let t = this._next(e);
2376
+ return t ? e instanceof m || e instanceof Date ? t.getTime() - e.getTime() : t.getTime() - new m(e).getTime() : null;
2377
+ }
2378
+ stop() {
2379
+ this._states.kill = true, this._states.currentTimeout && clearTimeout(this._states.currentTimeout);
2380
+ let e = w.indexOf(this);
2381
+ e >= 0 && w.splice(e, 1);
2382
+ }
2383
+ pause() {
2384
+ return this._states.paused = true, !this._states.kill;
2385
+ }
2386
+ resume() {
2387
+ return this._states.paused = false, !this._states.kill;
2388
+ }
2389
+ schedule(e) {
2390
+ if (e && this.fn)
2391
+ throw new Error("Cron: It is not allowed to schedule two functions using the same Croner instance.");
2392
+ e && (this.fn = e);
2393
+ let t = this.msToNext(), r = this.nextRun(this._states.currentRun);
2394
+ return t == null || isNaN(t) || r === null ? this : (t > W && (t = W), this._states.currentTimeout = setTimeout(() => this._checkTrigger(r), t), this._states.currentTimeout && this.options.unref && x(this._states.currentTimeout), this);
2395
+ }
2396
+ async _trigger(e) {
2397
+ this._states.blocking = true, this._states.currentRun = new m(undefined, this.getTz());
2398
+ try {
2399
+ if (this.options.catch)
2400
+ try {
2401
+ this.fn !== undefined && await this.fn(this, this.options.context);
2402
+ } catch (t) {
2403
+ if (p(this.options.catch))
2404
+ try {
2405
+ this.options.catch(t, this);
2406
+ } catch {}
2407
+ }
2408
+ else
2409
+ this.fn !== undefined && await this.fn(this, this.options.context);
2410
+ } finally {
2411
+ this._states.previousRun = new m(e, this.getTz()), this._states.blocking = false;
2412
+ }
2413
+ }
2414
+ async trigger() {
2415
+ await this._trigger();
2416
+ }
2417
+ runsLeft() {
2418
+ return this._states.maxRuns;
2419
+ }
2420
+ _checkTrigger(e) {
2421
+ let t = new Date, r = !this._states.paused && t.getTime() >= e.getTime(), n = this._states.blocking && this.options.protect;
2422
+ r && !n ? (this._states.maxRuns !== undefined && this._states.maxRuns--, this._trigger()) : r && n && p(this.options.protect) && setTimeout(() => this.options.protect(this), 0), this.schedule();
2423
+ }
2424
+ _next(e) {
2425
+ let t = !!(e || this._states.currentRun), r = false;
2426
+ !e && this.options.startAt && this.options.interval && ([e, t] = this._calculatePreviousRun(e, t), r = !e), e = new m(e, this.getTz()), this.options.startAt && e && e.getTime() < this.options.startAt.getTime() && (e = this.options.startAt);
2427
+ let n = this._states.once || new m(e, this.getTz());
2428
+ return !r && n !== this._states.once && (n = n.increment(this._states.pattern, this.options, t)), this._states.once && this._states.once.getTime() <= e.getTime() || n === null || this._states.maxRuns !== undefined && this._states.maxRuns <= 0 || this._states.kill || this.options.stopAt && n.getTime() >= this.options.stopAt.getTime() ? null : n;
2429
+ }
2430
+ _previous(e) {
2431
+ let t = new m(e, this.getTz());
2432
+ this.options.stopAt && t.getTime() > this.options.stopAt.getTime() && (t = this.options.stopAt);
2433
+ let r = new m(t, this.getTz());
2434
+ return this._states.once ? this._states.once.getTime() < t.getTime() ? this._states.once : null : (r = r.decrement(this._states.pattern, this.options), r === null || this.options.startAt && r.getTime() < this.options.startAt.getTime() ? null : r);
2435
+ }
2436
+ _calculatePreviousRun(e, t) {
2437
+ let r = new m(undefined, this.getTz()), n = e;
2438
+ if (this.options.startAt.getTime() <= r.getTime()) {
2439
+ n = this.options.startAt;
2440
+ let i = n.getTime() + this.options.interval * 1000;
2441
+ for (;i <= r.getTime(); )
2442
+ n = new m(n, this.getTz()).increment(this._states.pattern, this.options, true), i = n.getTime() + this.options.interval * 1000;
2443
+ t = true;
2444
+ }
2445
+ return n === null && (n = undefined), [n, t];
2446
+ }
2447
+ };
2448
+ var init_croner = __esm(() => {
2449
+ O = [1, 2, 4, 8, 16];
2450
+ P = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
2451
+ f = [["month", "year", 0], ["day", "month", -1], ["hour", "day", 0], ["minute", "hour", 0], ["second", "minute", 0]];
2452
+ W = 30 * 1000;
2453
+ w = [];
2454
+ });
2455
+
2456
+ // ../core/src/tools/cron/index.ts
2457
+ function setSchedulerInstance2(scheduler) {
2458
+ _scheduler2 = scheduler;
2459
+ }
2460
+ function getUserTimezone2() {
2461
+ const db = getDb();
2462
+ const user = db.query("SELECT timezone FROM users LIMIT 1").get();
2463
+ return user?.timezone || "UTC";
2464
+ }
2465
+ function resolveBestChannel(userId, explicitChannel) {
2466
+ const db = getDb();
2467
+ const user = db.query("SELECT preferred_cron_channel FROM users WHERE id = ?").get(userId);
2468
+ const activeChannels = db.query(`
2469
+ SELECT ui.channel FROM user_identities ui
2470
+ JOIN channels c ON c.id = ui.channel
2471
+ WHERE ui.user_id = ? AND c.active = 1 AND c.status = 'connected'
2472
+ `).all(userId);
2473
+ const identities = activeChannels.length > 0 ? activeChannels : db.query("SELECT channel FROM user_identities WHERE user_id = ?").all(userId);
2474
+ if (identities.length === 0) {
2475
+ return "webchat";
2476
+ }
2477
+ let bestChannel = "";
2478
+ if (explicitChannel && explicitChannel !== "system") {
2479
+ if (identities.some((i) => i.channel === explicitChannel)) {
2480
+ bestChannel = explicitChannel;
2481
+ }
2482
+ }
2483
+ if (!bestChannel && user?.preferred_cron_channel && user.preferred_cron_channel !== "auto") {
2484
+ if (identities.some((i) => i.channel === user.preferred_cron_channel)) {
2485
+ bestChannel = user.preferred_cron_channel;
2486
+ }
2487
+ }
2488
+ if (!bestChannel) {
2489
+ const preferred = ["telegram", "discord", "slack", "whatsapp", "webchat"];
2490
+ for (const p2 of preferred) {
2491
+ if (identities.some((i) => i.channel === p2)) {
2492
+ bestChannel = p2;
2493
+ break;
2494
+ }
2495
+ }
2496
+ }
2497
+ if (!bestChannel) {
2498
+ bestChannel = identities[0].channel;
2499
+ }
2500
+ return bestChannel;
2501
+ }
2502
+ function createTools2() {
2503
+ return [
2504
+ cronCreateTool,
2505
+ cronListTool,
2506
+ cronPauseTool,
2507
+ cronResumeTool,
2508
+ cronDeleteTool,
2509
+ cronTriggerTool,
2510
+ cronHistoryTool
2511
+ ];
2512
+ }
2513
+ var log2, _scheduler2 = null, cronCreateTool, cronListTool, cronPauseTool, cronResumeTool, cronDeleteTool, cronTriggerTool, cronHistoryTool;
2514
+ var init_cron = __esm(() => {
2515
+ init_sqlite();
2516
+ init_logger();
2517
+ init_croner();
2518
+ log2 = logger.child("CronTools");
2519
+ cronCreateTool = {
2520
+ name: "cron.create",
2521
+ description: "Create a new scheduled task. Use for recurring reminders, daily reports, automated checks. Spanish: crear tarea programada, agendar recordatorio, programar reporte",
2522
+ parameters: {
2523
+ type: "object",
2524
+ properties: {
2525
+ name: { type: "string", description: "Name for the scheduled task (e.g., 'daily-report', 'morning-reminder')" },
2526
+ task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Type: 'recurring' for cron-based, 'one_shot' for single execution" },
2527
+ cron_expression: { type: "string", description: "Cron expression (5-7 fields) for recurring tasks. Example: '0 9 * * *' (daily at 9 AM)" },
2528
+ fire_at: { type: "string", description: "ISO 8601 datetime for one_shot tasks. Example: '2026-04-01T09:00:00'" },
2529
+ payload: { type: "object", description: "Payload with 'prompt' or 'message' field containing the task content" },
2530
+ agent_id: { type: "string", description: "Target agent ID (optional, defaults to Coordinator)" },
2531
+ tool_name: { type: "string", description: "Specific tool to execute (optional)" },
2532
+ max_runs: { type: "number", description: "Maximum executions (optional, null = unlimited)" },
2533
+ channel: { type: "string", description: "Notification channel (system, telegram, discord, whatsapp, cli)" }
2534
+ },
2535
+ required: ["name", "task_type"]
2536
+ },
2537
+ execute: async (params) => {
2538
+ const db = getDb();
2539
+ const timezone = getUserTimezone2();
2540
+ const name = params.name;
2541
+ const task_type = params.task_type;
2542
+ const cron_expression = params.cron_expression;
2543
+ const fire_at = params.fire_at;
2544
+ const payload = params.payload;
2545
+ const agent_id = params.agent_id;
2546
+ const tool_name = params.tool_name;
2547
+ const max_runs = params.max_runs;
2548
+ const channel = params.channel || "system";
2549
+ if (!name) {
2550
+ return { ok: false, error: "Missing required field: name" };
2551
+ }
2552
+ if (!task_type) {
2553
+ return { ok: false, error: "Missing required field: task_type (recurring or one_shot)" };
2554
+ }
2555
+ if (task_type === "recurring" && !cron_expression) {
2556
+ return { ok: false, error: "recurring task requires cron_expression" };
2557
+ }
2558
+ if (task_type === "one_shot" && !fire_at) {
2559
+ return { ok: false, error: "one_shot task requires fire_at" };
2560
+ }
2561
+ if (cron_expression) {
2562
+ try {
2563
+ new E(cron_expression);
2564
+ } catch (err) {
2565
+ return { ok: false, error: `Invalid cron expression: ${err.message}` };
2566
+ }
2567
+ }
2568
+ if (fire_at) {
2569
+ const fireAtDate = new Date(fire_at);
2570
+ if (fireAtDate.getTime() <= Date.now()) {
2571
+ return { ok: false, error: "fire_at must be in the future" };
2572
+ }
2573
+ }
2574
+ if (payload && !payload._internal) {
2575
+ const hasPrompt = !!(payload.prompt || payload.message);
2576
+ if (!hasPrompt) {
2577
+ return { ok: false, error: "Payload must contain 'prompt' or 'message' field" };
2578
+ }
2579
+ }
2580
+ if (agent_id) {
2581
+ const agent = db.query("SELECT id FROM agents WHERE id = ?").get(agent_id);
2582
+ if (!agent) {
2583
+ return { ok: false, error: `Agent not found: ${agent_id}` };
2584
+ }
2585
+ }
2586
+ try {
2587
+ if (_scheduler2) {
2588
+ const result = _scheduler2.create({
2589
+ name,
2590
+ task_type,
2591
+ cron_expression,
2592
+ fire_at,
2593
+ timezone,
2594
+ payload: payload || { prompt: `Execute scheduled task: ${name}` },
2595
+ agent_id: agent_id || null,
2596
+ tool_name: tool_name || null,
2597
+ max_runs: max_runs || null,
2598
+ channel
2599
+ });
2600
+ log2.info(`[create] Task "${name}" created via scheduler: ${result.id}`);
2601
+ return {
2602
+ ok: true,
2603
+ task_id: result.id,
2604
+ next_run: result.nextRun,
2605
+ message: `Task "${name}" scheduled. Next run: ${result.nextRun ? new Date(result.nextRun).toLocaleString() : "unknown"}`
2606
+ };
2607
+ } else {
2608
+ const id = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
2609
+ const now = new Date().toISOString();
2610
+ db.query(`
2611
+ INSERT INTO scheduled_tasks (
2612
+ id, name, task_type, cron_expression, fire_at, timezone, payload,
2613
+ agent_id, tool_name, max_runs, channel, created_at, updated_at
2614
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2615
+ `).run(id, name, task_type, cron_expression || null, fire_at || null, timezone, JSON.stringify(payload || {}), agent_id || null, tool_name || null, max_runs || null, channel, now, now);
2616
+ return {
2617
+ ok: true,
2618
+ task_id: id,
2619
+ message: `Task "${name}" saved (scheduler not active)`
2620
+ };
2621
+ }
2622
+ } catch (err) {
2623
+ log2.error(`[create] Failed: ${err.message}`);
2624
+ return { ok: false, error: `Failed to create task: ${err.message}` };
2625
+ }
2626
+ }
2627
+ };
2628
+ cronListTool = {
2629
+ name: "cron.list",
2630
+ description: "List all scheduled tasks with their next execution times. Spanish: ver tareas programadas, listar cronograma",
2631
+ parameters: {
2632
+ type: "object",
2633
+ properties: {
2634
+ status: { type: "string", enum: ["active", "paused", "completed", "failed", "cancelled"], description: "Filter by status" },
2635
+ task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Filter by type" }
2636
+ }
2637
+ },
2638
+ execute: async (params) => {
2639
+ const db = getDb();
2640
+ const status = params.status;
2641
+ const task_type = params.task_type;
2642
+ try {
2643
+ let query = "SELECT * FROM scheduled_tasks WHERE 1=1";
2644
+ const args = [];
2645
+ if (status) {
2646
+ query += " AND status = ?";
2647
+ args.push(status);
2648
+ }
2649
+ if (task_type) {
2650
+ query += " AND task_type = ?";
2651
+ args.push(task_type);
2652
+ }
2653
+ query += " ORDER BY next_run_at ASC";
2654
+ const tasks = db.query(query).all(...args);
2655
+ return {
2656
+ ok: true,
2657
+ tasks: tasks.map((t) => ({
2658
+ id: t.id,
2659
+ name: t.name,
2660
+ type: t.task_type,
2661
+ status: t.status,
2662
+ cron_expression: t.cron_expression,
2663
+ fire_at: t.fire_at,
2664
+ next_run: t.next_run_at,
2665
+ last_run: t.last_run_at,
2666
+ run_count: t.run_count,
2667
+ channel: t.channel
2668
+ })),
2669
+ count: tasks.length
2670
+ };
2671
+ } catch (err) {
2672
+ log2.error(`[list] Failed: ${err.message}`);
2673
+ return { ok: false, error: `Failed to list tasks: ${err.message}` };
2674
+ }
2675
+ }
2676
+ };
2677
+ cronPauseTool = {
2678
+ name: "cron.pause",
2679
+ description: "Pause a scheduled task. Spanish: pausar tarea programada, detener temporalmente",
2680
+ parameters: {
2681
+ type: "object",
2682
+ properties: {
2683
+ task_id: { type: "string", description: "ID of the task to pause" }
2684
+ },
2685
+ required: ["task_id"]
2686
+ },
2687
+ execute: async (params) => {
2688
+ const task_id = params.task_id;
2689
+ if (!task_id) {
2690
+ return { ok: false, error: "Missing required field: task_id" };
2691
+ }
2692
+ try {
2693
+ if (_scheduler2) {
2694
+ const success = _scheduler2.pause(task_id);
2695
+ if (success) {
2696
+ return { ok: true, message: `Task "${task_id}" paused` };
2697
+ } else {
2698
+ return { ok: false, error: `Task "${task_id}" not found or already paused` };
2699
+ }
2700
+ } else {
2701
+ const db = getDb();
2702
+ const result = db.query("UPDATE scheduled_tasks SET status = 'paused' WHERE id = ?").run(task_id);
2703
+ if (result.changes > 0) {
2704
+ return { ok: true, message: `Task "${task_id}" paused (scheduler not active)` };
2705
+ } else {
2706
+ return { ok: false, error: `Task "${task_id}" not found` };
2707
+ }
2708
+ }
2709
+ } catch (err) {
2710
+ log2.error(`[pause] Failed: ${err.message}`);
2711
+ return { ok: false, error: `Failed to pause task: ${err.message}` };
2712
+ }
2713
+ }
2714
+ };
2715
+ cronResumeTool = {
2716
+ name: "cron.resume",
2717
+ description: "Resume a paused scheduled task. Spanish: reanudar tarea programada, continuar",
2718
+ parameters: {
2719
+ type: "object",
2720
+ properties: {
2721
+ task_id: { type: "string", description: "ID of the task to resume" }
2722
+ },
2723
+ required: ["task_id"]
2724
+ },
2725
+ execute: async (params) => {
2726
+ const task_id = params.task_id;
2727
+ if (!task_id) {
2728
+ return { ok: false, error: "Missing required field: task_id" };
2729
+ }
2730
+ try {
2731
+ if (_scheduler2) {
2732
+ const success = _scheduler2.resume(task_id);
2733
+ if (success) {
2734
+ return { ok: true, message: `Task "${task_id}" resumed` };
2735
+ } else {
2736
+ return { ok: false, error: `Task "${task_id}" not found or already active` };
2737
+ }
2738
+ } else {
2739
+ const db = getDb();
2740
+ const result = db.query("UPDATE scheduled_tasks SET status = 'active' WHERE id = ?").run(task_id);
2741
+ if (result.changes > 0) {
2742
+ return { ok: true, message: `Task "${task_id}" resumed (scheduler not active)` };
2743
+ } else {
2744
+ return { ok: false, error: `Task "${task_id}" not found` };
2745
+ }
2746
+ }
2747
+ } catch (err) {
2748
+ log2.error(`[resume] Failed: ${err.message}`);
2749
+ return { ok: false, error: `Failed to resume task: ${err.message}` };
2750
+ }
2751
+ }
2752
+ };
2753
+ cronDeleteTool = {
2754
+ name: "cron.delete",
2755
+ description: "Delete a scheduled task permanently. Spanish: eliminar tarea programada, cancelar recordatorio",
2756
+ parameters: {
2757
+ type: "object",
2758
+ properties: {
2759
+ task_id: { type: "string", description: "ID of the task to delete" }
2760
+ },
2761
+ required: ["task_id"]
2762
+ },
2763
+ execute: async (params) => {
2764
+ const task_id = params.task_id;
2765
+ if (!task_id) {
2766
+ return { ok: false, error: "Missing required field: task_id" };
2767
+ }
2768
+ try {
2769
+ if (_scheduler2) {
2770
+ const success = _scheduler2.delete(task_id);
2771
+ if (success) {
2772
+ return { ok: true, message: `Task "${task_id}" deleted` };
2773
+ } else {
2774
+ return { ok: false, error: `Task "${task_id}" not found` };
2775
+ }
2776
+ } else {
2777
+ const db = getDb();
2778
+ const result = db.query("DELETE FROM scheduled_tasks WHERE id = ?").run(task_id);
2779
+ if (result.changes > 0) {
2780
+ return { ok: true, message: `Task "${task_id}" deleted (scheduler not active)` };
2781
+ } else {
2782
+ return { ok: false, error: `Task "${task_id}" not found` };
2783
+ }
2784
+ }
2785
+ } catch (err) {
2786
+ log2.error(`[delete] Failed: ${err.message}`);
2787
+ return { ok: false, error: `Failed to delete task: ${err.message}` };
2788
+ }
2789
+ }
2790
+ };
2791
+ cronTriggerTool = {
2792
+ name: "cron.trigger",
2793
+ description: "Manually trigger a scheduled task execution immediately. Spanish: ejecutar tarea ahora, forzar ejecuci\xF3n",
2794
+ parameters: {
2795
+ type: "object",
2796
+ properties: {
2797
+ task_id: { type: "string", description: "ID of the task to trigger" }
2798
+ },
2799
+ required: ["task_id"]
2800
+ },
2801
+ execute: async (params) => {
2802
+ const task_id = params.task_id;
2803
+ if (!task_id) {
2804
+ return { ok: false, error: "Missing required field: task_id" };
2805
+ }
2806
+ try {
2807
+ if (_scheduler2) {
2808
+ const success = _scheduler2.trigger(task_id);
2809
+ if (success) {
2810
+ return { ok: true, message: `Task "${task_id}" triggered` };
2811
+ } else {
2812
+ return { ok: false, error: `Task "${task_id}" not found or not active` };
2813
+ }
2814
+ } else {
2815
+ return { ok: false, error: "Scheduler not active - cannot trigger tasks" };
2816
+ }
2817
+ } catch (err) {
2818
+ log2.error(`[trigger] Failed: ${err.message}`);
2819
+ return { ok: false, error: `Failed to trigger task: ${err.message}` };
2820
+ }
2821
+ }
2822
+ };
2823
+ cronHistoryTool = {
2824
+ name: "cron.history",
2825
+ description: "Get execution history for a scheduled task. Spanish: historial de ejecuciones, logs de tarea",
2826
+ parameters: {
2827
+ type: "object",
2828
+ properties: {
2829
+ task_id: { type: "string", description: "ID of the task" },
2830
+ limit: { type: "number", description: "Maximum number of records (default: 10)" }
2831
+ },
2832
+ required: ["task_id"]
2833
+ },
2834
+ execute: async (params) => {
2835
+ const task_id = params.task_id;
2836
+ const limit = params.limit || 10;
2837
+ if (!task_id) {
2838
+ return { ok: false, error: "Missing required field: task_id" };
2839
+ }
2840
+ try {
2841
+ const db = getDb();
2842
+ const runs = db.query(`
2843
+ SELECT * FROM task_runs
2844
+ WHERE task_id = ?
2845
+ ORDER BY started_at DESC
2846
+ LIMIT ?
2847
+ `).all(task_id, limit);
2848
+ return {
2849
+ ok: true,
2850
+ history: runs.map((r) => ({
2851
+ id: r.id,
2852
+ status: r.status,
2853
+ started_at: r.started_at,
2854
+ finished_at: r.finished_at,
2855
+ duration_ms: r.duration_ms,
2856
+ error_message: r.error_message
2857
+ })),
2858
+ count: runs.length
2859
+ };
2860
+ } catch (err) {
2861
+ log2.error(`[history] Failed: ${err.message}`);
2862
+ return { ok: false, error: `Failed to get history: ${err.message}` };
2863
+ }
2864
+ }
2865
+ };
2866
+ });
2867
+
2868
+ // ../core/src/tools/schedule.ts
2869
+ init_sqlite();
2870
+ init_logger();
2871
+ init_croner();
2872
+ var log = logger.child("ScheduleTools");
2873
+ var _scheduler = null;
2874
+ function setSchedulerInstance(scheduler) {
2875
+ _scheduler = scheduler;
2876
+ }
2877
+ function getUserTimezone() {
2878
+ const db = getDb();
2879
+ const user = db.query("SELECT timezone FROM users LIMIT 1").get();
2880
+ return user?.timezone || "UTC";
2881
+ }
2882
+ var scheduleCreateTool = {
2883
+ name: "hive.schedule.create",
2884
+ description: "Create a new scheduled task. Use for recurring reminders, daily reports, automated checks. Spanish: crear tarea programada, agendar recordatorio, programar reporte",
2885
+ parameters: {
2886
+ type: "object",
2887
+ properties: {
2888
+ name: { type: "string", description: "Name for the scheduled task (e.g., 'daily-report', 'morning-reminder')" },
2889
+ task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Type: 'recurring' for cron-based, 'one_shot' for single execution" },
2890
+ cron_expression: { type: "string", description: "Cron expression (5-7 fields) for recurring tasks. Example: '0 9 * * *' (daily at 9 AM)" },
2891
+ fire_at: { type: "string", description: "ISO 8601 datetime for one_shot tasks. Example: '2026-04-01T09:00:00'" },
2892
+ payload: { type: "object", description: "Payload with 'prompt' or 'message' field containing the task content" },
2893
+ agent_id: { type: "string", description: "Target agent ID (optional, defaults to Coordinator)" },
2894
+ tool_name: { type: "string", description: "Specific tool to execute (optional)" },
2895
+ max_runs: { type: "number", description: "Maximum executions (optional, null = unlimited)" },
2896
+ channel: { type: "string", description: "Notification channel (system, telegram, discord, whatsapp, cli)" }
2897
+ },
2898
+ required: ["name", "task_type"]
2899
+ },
2900
+ execute: async (params) => {
2901
+ const db = getDb();
2902
+ const timezone = getUserTimezone();
2903
+ const name = params.name;
2904
+ const task_type = params.task_type;
2905
+ const cron_expression = params.cron_expression;
2906
+ const fire_at = params.fire_at;
2907
+ const payload = params.payload;
2908
+ const agent_id = params.agent_id;
2909
+ const tool_name = params.tool_name;
2910
+ const max_runs = params.max_runs;
2911
+ const channel = params.channel || "system";
2912
+ if (!name) {
2913
+ return { ok: false, error: "Missing required field: name" };
2914
+ }
2915
+ if (!task_type) {
2916
+ return { ok: false, error: "Missing required field: task_type (recurring or one_shot)" };
2917
+ }
2918
+ if (task_type === "recurring" && !cron_expression) {
2919
+ return { ok: false, error: "recurring task requires cron_expression" };
2920
+ }
2921
+ if (task_type === "one_shot" && !fire_at) {
2922
+ return { ok: false, error: "one_shot task requires fire_at" };
2923
+ }
2924
+ if (cron_expression) {
2925
+ try {
2926
+ new E(cron_expression);
2927
+ } catch (err) {
2928
+ return { ok: false, error: `Invalid cron expression: ${err.message}` };
2929
+ }
2930
+ }
2931
+ if (fire_at) {
2932
+ const fireAtDate = new Date(fire_at);
2933
+ if (fireAtDate.getTime() <= Date.now()) {
2934
+ return { ok: false, error: "fire_at must be in the future" };
2935
+ }
2936
+ }
2937
+ if (payload && !payload._internal) {
2938
+ const hasPrompt = !!(payload.prompt || payload.message);
2939
+ if (!hasPrompt) {
2940
+ return { ok: false, error: "Payload must contain 'prompt' or 'message' field" };
2941
+ }
2942
+ }
2943
+ if (agent_id) {
2944
+ const agent = db.query("SELECT id FROM agents WHERE id = ?").get(agent_id);
2945
+ if (!agent) {
2946
+ return { ok: false, error: `Agent not found: ${agent_id}` };
2947
+ }
2948
+ }
2949
+ try {
2950
+ if (_scheduler) {
2951
+ const result = _scheduler.create({
2952
+ name,
2953
+ task_type,
2954
+ cron_expression,
2955
+ fire_at,
2956
+ timezone,
2957
+ payload: payload || { prompt: `Execute scheduled task: ${name}` },
2958
+ agent_id: agent_id || null,
2959
+ tool_name: tool_name || null,
2960
+ max_runs: max_runs || null,
2961
+ channel
2962
+ });
2963
+ log.info(`[create] Task "${name}" created via scheduler: ${result.id}`);
2964
+ return {
2965
+ ok: true,
2966
+ task_id: result.id,
2967
+ next_run: result.nextRun,
2968
+ message: `Task "${name}" scheduled. Next run: ${result.nextRun ? new Date(result.nextRun).toLocaleString() : "unknown"}`
2969
+ };
2970
+ } else {
2971
+ const id = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
2972
+ const now = new Date().toISOString();
2973
+ db.query(`
2974
+ INSERT INTO scheduled_tasks (
2975
+ id, name, task_type, cron_expression, fire_at, timezone, payload,
2976
+ agent_id, tool_name, max_runs, channel, created_at, updated_at
2977
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2978
+ `).run(id, name, task_type, cron_expression || null, fire_at || null, timezone, JSON.stringify(payload || {}), agent_id || null, tool_name || null, max_runs || null, channel, now, now);
2979
+ return {
2980
+ ok: true,
2981
+ task_id: id,
2982
+ message: `Task "${name}" saved (scheduler not active)`
2983
+ };
2984
+ }
2985
+ } catch (err) {
2986
+ log.error(`[create] Failed: ${err.message}`);
2987
+ return { ok: false, error: `Failed to create task: ${err.message}` };
2988
+ }
2989
+ }
2990
+ };
2991
+ var scheduleListTool = {
2992
+ name: "hive.schedule.list",
2993
+ description: "List all scheduled tasks with their next execution times. Spanish: ver tareas programadas, listar cronograma",
2994
+ parameters: {
2995
+ type: "object",
2996
+ properties: {
2997
+ status: { type: "string", enum: ["active", "paused", "completed", "failed", "cancelled"], description: "Filter by status" },
2998
+ task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Filter by type" }
2999
+ }
3000
+ },
3001
+ execute: async (params) => {
3002
+ const db = getDb();
3003
+ const status = params.status;
3004
+ const task_type = params.task_type;
3005
+ try {
3006
+ let query = "SELECT * FROM scheduled_tasks WHERE 1=1";
3007
+ const args = [];
3008
+ if (status) {
3009
+ query += " AND status = ?";
3010
+ args.push(status);
3011
+ }
3012
+ if (task_type) {
3013
+ query += " AND task_type = ?";
3014
+ args.push(task_type);
3015
+ }
3016
+ query += " ORDER BY next_run_at ASC";
3017
+ const tasks = db.query(query).all(...args);
3018
+ return {
3019
+ ok: true,
3020
+ tasks: tasks.map((t) => ({
3021
+ id: t.id,
3022
+ name: t.name,
3023
+ type: t.task_type,
3024
+ status: t.status,
3025
+ cron_expression: t.cron_expression,
3026
+ fire_at: t.fire_at,
3027
+ next_run: t.next_run_at,
3028
+ last_run: t.last_run_at,
3029
+ run_count: t.run_count,
3030
+ channel: t.channel
3031
+ })),
3032
+ count: tasks.length
3033
+ };
3034
+ } catch (err) {
3035
+ log.error(`[list] Failed: ${err.message}`);
3036
+ return { ok: false, error: `Failed to list tasks: ${err.message}` };
3037
+ }
3038
+ }
3039
+ };
3040
+ var schedulePauseTool = {
3041
+ name: "hive.schedule.pause",
3042
+ description: "Pause a scheduled task. Spanish: pausar tarea programada, detener temporalmente",
3043
+ parameters: {
3044
+ type: "object",
3045
+ properties: {
3046
+ task_id: { type: "string", description: "ID of the task to pause" }
3047
+ },
3048
+ required: ["task_id"]
3049
+ },
3050
+ execute: async (params) => {
3051
+ const task_id = params.task_id;
3052
+ if (!task_id) {
3053
+ return { ok: false, error: "Missing required field: task_id" };
3054
+ }
3055
+ try {
3056
+ if (_scheduler) {
3057
+ const success = _scheduler.pause(task_id);
3058
+ if (success) {
3059
+ return { ok: true, message: `Task "${task_id}" paused` };
3060
+ } else {
3061
+ return { ok: false, error: `Task "${task_id}" not found or already paused` };
3062
+ }
3063
+ } else {
3064
+ const db = getDb();
3065
+ const result = db.query("UPDATE scheduled_tasks SET status = 'paused' WHERE id = ?").run(task_id);
3066
+ if (result.changes > 0) {
3067
+ return { ok: true, message: `Task "${task_id}" paused (scheduler not active)` };
3068
+ } else {
3069
+ return { ok: false, error: `Task "${task_id}" not found` };
3070
+ }
3071
+ }
3072
+ } catch (err) {
3073
+ log.error(`[pause] Failed: ${err.message}`);
3074
+ return { ok: false, error: `Failed to pause task: ${err.message}` };
3075
+ }
3076
+ }
3077
+ };
3078
+ var scheduleResumeTool = {
3079
+ name: "hive.schedule.resume",
3080
+ description: "Resume a paused scheduled task. Spanish: reanudar tarea programada, continuar",
3081
+ parameters: {
3082
+ type: "object",
3083
+ properties: {
3084
+ task_id: { type: "string", description: "ID of the task to resume" }
3085
+ },
3086
+ required: ["task_id"]
3087
+ },
3088
+ execute: async (params) => {
3089
+ const task_id = params.task_id;
3090
+ if (!task_id) {
3091
+ return { ok: false, error: "Missing required field: task_id" };
3092
+ }
3093
+ try {
3094
+ if (_scheduler) {
3095
+ const success = _scheduler.resume(task_id);
3096
+ if (success) {
3097
+ return { ok: true, message: `Task "${task_id}" resumed` };
3098
+ } else {
3099
+ return { ok: false, error: `Task "${task_id}" not found or already active` };
3100
+ }
3101
+ } else {
3102
+ const db = getDb();
3103
+ const result = db.query("UPDATE scheduled_tasks SET status = 'active' WHERE id = ?").run(task_id);
3104
+ if (result.changes > 0) {
3105
+ return { ok: true, message: `Task "${task_id}" resumed (scheduler not active)` };
3106
+ } else {
3107
+ return { ok: false, error: `Task "${task_id}" not found` };
3108
+ }
3109
+ }
3110
+ } catch (err) {
3111
+ log.error(`[resume] Failed: ${err.message}`);
3112
+ return { ok: false, error: `Failed to resume task: ${err.message}` };
3113
+ }
3114
+ }
3115
+ };
3116
+ var scheduleDeleteTool = {
3117
+ name: "hive.schedule.delete",
3118
+ description: "Delete a scheduled task permanently. Spanish: eliminar tarea programada, cancelar recordatorio",
3119
+ parameters: {
3120
+ type: "object",
3121
+ properties: {
3122
+ task_id: { type: "string", description: "ID of the task to delete" }
3123
+ },
3124
+ required: ["task_id"]
3125
+ },
3126
+ execute: async (params) => {
3127
+ const task_id = params.task_id;
3128
+ if (!task_id) {
3129
+ return { ok: false, error: "Missing required field: task_id" };
3130
+ }
3131
+ try {
3132
+ if (_scheduler) {
3133
+ const success = _scheduler.delete(task_id);
3134
+ if (success) {
3135
+ return { ok: true, message: `Task "${task_id}" deleted` };
3136
+ } else {
3137
+ return { ok: false, error: `Task "${task_id}" not found` };
3138
+ }
3139
+ } else {
3140
+ const db = getDb();
3141
+ const result = db.query("DELETE FROM scheduled_tasks WHERE id = ?").run(task_id);
3142
+ if (result.changes > 0) {
3143
+ return { ok: true, message: `Task "${task_id}" deleted (scheduler not active)` };
3144
+ } else {
3145
+ return { ok: false, error: `Task "${task_id}" not found` };
3146
+ }
3147
+ }
3148
+ } catch (err) {
3149
+ log.error(`[delete] Failed: ${err.message}`);
3150
+ return { ok: false, error: `Failed to delete task: ${err.message}` };
3151
+ }
3152
+ }
3153
+ };
3154
+ var scheduleTriggerTool = {
3155
+ name: "hive.schedule.trigger",
3156
+ description: "Manually trigger a scheduled task execution immediately. Spanish: ejecutar tarea ahora, forzar ejecuci\xF3n",
3157
+ parameters: {
3158
+ type: "object",
3159
+ properties: {
3160
+ task_id: { type: "string", description: "ID of the task to trigger" }
3161
+ },
3162
+ required: ["task_id"]
3163
+ },
3164
+ execute: async (params) => {
3165
+ const task_id = params.task_id;
3166
+ if (!task_id) {
3167
+ return { ok: false, error: "Missing required field: task_id" };
3168
+ }
3169
+ try {
3170
+ if (_scheduler) {
3171
+ const success = _scheduler.trigger(task_id);
3172
+ if (success) {
3173
+ return { ok: true, message: `Task "${task_id}" triggered` };
3174
+ } else {
3175
+ return { ok: false, error: `Task "${task_id}" not found or not active` };
3176
+ }
3177
+ } else {
3178
+ return { ok: false, error: "Scheduler not active - cannot trigger tasks" };
3179
+ }
3180
+ } catch (err) {
3181
+ log.error(`[trigger] Failed: ${err.message}`);
3182
+ return { ok: false, error: `Failed to trigger task: ${err.message}` };
3183
+ }
3184
+ }
3185
+ };
3186
+ var scheduleHistoryTool = {
3187
+ name: "hive.schedule.history",
3188
+ description: "Get execution history for a scheduled task. Spanish: historial de ejecuciones, logs de tarea",
3189
+ parameters: {
3190
+ type: "object",
3191
+ properties: {
3192
+ task_id: { type: "string", description: "ID of the task" },
3193
+ limit: { type: "number", description: "Maximum number of records (default: 10)" }
3194
+ },
3195
+ required: ["task_id"]
3196
+ },
3197
+ execute: async (params) => {
3198
+ const task_id = params.task_id;
3199
+ const limit = params.limit || 10;
3200
+ if (!task_id) {
3201
+ return { ok: false, error: "Missing required field: task_id" };
3202
+ }
3203
+ try {
3204
+ const db = getDb();
3205
+ const runs = db.query(`
3206
+ SELECT * FROM task_runs
3207
+ WHERE task_id = ?
3208
+ ORDER BY started_at DESC
3209
+ LIMIT ?
3210
+ `).all(task_id, limit);
3211
+ return {
3212
+ ok: true,
3213
+ history: runs.map((r) => ({
3214
+ id: r.id,
3215
+ status: r.status,
3216
+ started_at: r.started_at,
3217
+ finished_at: r.finished_at,
3218
+ duration_ms: r.duration_ms,
3219
+ error_message: r.error_message
3220
+ })),
3221
+ count: runs.length
3222
+ };
3223
+ } catch (err) {
3224
+ log.error(`[history] Failed: ${err.message}`);
3225
+ return { ok: false, error: `Failed to get history: ${err.message}` };
3226
+ }
3227
+ }
3228
+ };
3229
+ function createTools() {
3230
+ return [
3231
+ scheduleCreateTool,
3232
+ scheduleListTool,
3233
+ schedulePauseTool,
3234
+ scheduleResumeTool,
3235
+ scheduleDeleteTool,
3236
+ scheduleTriggerTool,
3237
+ scheduleHistoryTool
3238
+ ];
3239
+ }
3240
+
2
3241
  // src/cron/index.ts
3
- import {
4
- scheduleCreateTool,
5
- scheduleListTool,
6
- schedulePauseTool,
7
- scheduleResumeTool,
8
- scheduleDeleteTool,
9
- scheduleTriggerTool,
10
- scheduleHistoryTool,
11
- createTools,
12
- setSchedulerInstance
13
- } from "@johpaz/hive-agents-core/tools/schedule";
14
- import {
15
- cronAddTool,
16
- cronListTool,
17
- cronEditTool,
18
- cronRemoveTool,
19
- createTools as createTools2,
20
- initCronScheduler,
21
- resolveBestChannel
22
- } from "@johpaz/hive-agents-core/tools/cron";
3242
+ init_cron();
23
3243
  export {
24
3244
  setSchedulerInstance,
25
3245
  scheduleTriggerTool,
@@ -30,11 +3250,13 @@ export {
30
3250
  scheduleDeleteTool,
31
3251
  scheduleCreateTool,
32
3252
  resolveBestChannel,
33
- initCronScheduler,
34
- cronRemoveTool,
3253
+ cronTriggerTool,
3254
+ cronResumeTool,
3255
+ cronPauseTool,
35
3256
  cronListTool,
36
- cronEditTool,
37
- cronAddTool,
3257
+ cronHistoryTool,
3258
+ cronDeleteTool,
3259
+ cronCreateTool,
38
3260
  createTools as createScheduleTools,
39
3261
  createTools2 as createCronTools
40
3262
  };