@flowdot.ai/daemon 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +51 -0
  3. package/dist/goals/DependencyResolver.d.ts +54 -0
  4. package/dist/goals/DependencyResolver.js +329 -0
  5. package/dist/goals/ErrorRecovery.d.ts +133 -0
  6. package/dist/goals/ErrorRecovery.js +489 -0
  7. package/dist/goals/GoalApiClient.d.ts +81 -0
  8. package/dist/goals/GoalApiClient.js +743 -0
  9. package/dist/goals/GoalCache.d.ts +65 -0
  10. package/dist/goals/GoalCache.js +243 -0
  11. package/dist/goals/GoalCommsHandler.d.ts +150 -0
  12. package/dist/goals/GoalCommsHandler.js +378 -0
  13. package/dist/goals/GoalExporter.d.ts +164 -0
  14. package/dist/goals/GoalExporter.js +318 -0
  15. package/dist/goals/GoalImporter.d.ts +107 -0
  16. package/dist/goals/GoalImporter.js +345 -0
  17. package/dist/goals/GoalManager.d.ts +110 -0
  18. package/dist/goals/GoalManager.js +535 -0
  19. package/dist/goals/GoalReporter.d.ts +105 -0
  20. package/dist/goals/GoalReporter.js +534 -0
  21. package/dist/goals/GoalScheduler.d.ts +102 -0
  22. package/dist/goals/GoalScheduler.js +209 -0
  23. package/dist/goals/GoalValidator.d.ts +72 -0
  24. package/dist/goals/GoalValidator.js +657 -0
  25. package/dist/goals/MetaGoalEnforcer.d.ts +111 -0
  26. package/dist/goals/MetaGoalEnforcer.js +536 -0
  27. package/dist/goals/MilestoneBreaker.d.ts +74 -0
  28. package/dist/goals/MilestoneBreaker.js +348 -0
  29. package/dist/goals/PermissionBridge.d.ts +109 -0
  30. package/dist/goals/PermissionBridge.js +326 -0
  31. package/dist/goals/ProgressTracker.d.ts +113 -0
  32. package/dist/goals/ProgressTracker.js +324 -0
  33. package/dist/goals/ReviewScheduler.d.ts +106 -0
  34. package/dist/goals/ReviewScheduler.js +360 -0
  35. package/dist/goals/TaskExecutor.d.ts +116 -0
  36. package/dist/goals/TaskExecutor.js +370 -0
  37. package/dist/goals/TaskFeedback.d.ts +126 -0
  38. package/dist/goals/TaskFeedback.js +402 -0
  39. package/dist/goals/TaskGenerator.d.ts +75 -0
  40. package/dist/goals/TaskGenerator.js +329 -0
  41. package/dist/goals/TaskQueue.d.ts +84 -0
  42. package/dist/goals/TaskQueue.js +331 -0
  43. package/dist/goals/TaskSanitizer.d.ts +61 -0
  44. package/dist/goals/TaskSanitizer.js +464 -0
  45. package/dist/goals/errors.d.ts +116 -0
  46. package/dist/goals/errors.js +299 -0
  47. package/dist/goals/index.d.ts +24 -0
  48. package/dist/goals/index.js +23 -0
  49. package/dist/goals/types.d.ts +395 -0
  50. package/dist/goals/types.js +230 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.js +3 -0
  53. package/dist/loop/DaemonIPC.d.ts +67 -0
  54. package/dist/loop/DaemonIPC.js +358 -0
  55. package/dist/loop/IntervalParser.d.ts +39 -0
  56. package/dist/loop/IntervalParser.js +217 -0
  57. package/dist/loop/LoopDaemon.d.ts +123 -0
  58. package/dist/loop/LoopDaemon.js +1821 -0
  59. package/dist/loop/LoopExecutor.d.ts +93 -0
  60. package/dist/loop/LoopExecutor.js +326 -0
  61. package/dist/loop/LoopManager.d.ts +79 -0
  62. package/dist/loop/LoopManager.js +476 -0
  63. package/dist/loop/LoopScheduler.d.ts +69 -0
  64. package/dist/loop/LoopScheduler.js +329 -0
  65. package/dist/loop/LoopStore.d.ts +57 -0
  66. package/dist/loop/LoopStore.js +406 -0
  67. package/dist/loop/LoopValidator.d.ts +55 -0
  68. package/dist/loop/LoopValidator.js +603 -0
  69. package/dist/loop/errors.d.ts +115 -0
  70. package/dist/loop/errors.js +312 -0
  71. package/dist/loop/index.d.ts +11 -0
  72. package/dist/loop/index.js +10 -0
  73. package/dist/loop/notifications/Notifier.d.ts +28 -0
  74. package/dist/loop/notifications/Notifier.js +78 -0
  75. package/dist/loop/notifications/SlackNotifier.d.ts +28 -0
  76. package/dist/loop/notifications/SlackNotifier.js +203 -0
  77. package/dist/loop/notifications/TerminalNotifier.d.ts +18 -0
  78. package/dist/loop/notifications/TerminalNotifier.js +72 -0
  79. package/dist/loop/notifications/WebhookNotifier.d.ts +24 -0
  80. package/dist/loop/notifications/WebhookNotifier.js +123 -0
  81. package/dist/loop/notifications/index.d.ts +24 -0
  82. package/dist/loop/notifications/index.js +109 -0
  83. package/dist/loop/types.d.ts +280 -0
  84. package/dist/loop/types.js +222 -0
  85. package/package.json +92 -0
@@ -0,0 +1,115 @@
1
+ import type { LoopId, LoopRunId, LoopStatus } from './types.js';
2
+ export type LoopErrorCode = 'LOOP_NOT_FOUND' | 'LOOP_ALREADY_EXISTS' | 'LOOP_NAME_TAKEN' | 'LOOP_INVALID_STATUS' | 'LOOP_EXPIRED' | 'LOOP_LIMIT_EXCEEDED' | 'INVALID_INTERVAL' | 'INTERVAL_TOO_SHORT' | 'INTERVAL_PARSE_ERROR' | 'INVALID_CRON_EXPRESSION' | 'LOOP_EXECUTION_ERROR' | 'LOOP_EXECUTION_TIMEOUT' | 'LOOP_RUN_NOT_FOUND' | 'DAEMON_NOT_RUNNING' | 'DAEMON_ALREADY_RUNNING' | 'DAEMON_START_FAILED' | 'DAEMON_STOP_FAILED' | 'DAEMON_COMMUNICATION_ERROR' | 'DAEMON_CONNECTION_ERROR' | 'DAEMON_IPC_TIMEOUT' | 'SCHEDULER_ERROR' | 'INVALID_LOOP_STATE' | 'STORAGE_READ_ERROR' | 'STORAGE_WRITE_ERROR' | 'STORAGE_CORRUPT' | 'STORAGE_MIGRATION_ERROR' | 'VALIDATION_ERROR' | 'INVALID_PROMPT' | 'INVALID_NAME' | 'INVALID_OPTIONS' | 'NOTIFICATION_FAILED' | 'WEBHOOK_ERROR' | 'SLACK_ERROR' | 'CONFIG_ERROR' | 'INVALID_CONFIG' | 'MANAGER_NOT_INITIALIZED';
3
+ export declare class LoopError extends Error {
4
+ readonly code: LoopErrorCode;
5
+ readonly context: Record<string, unknown>;
6
+ readonly timestamp: Date;
7
+ constructor(code: LoopErrorCode, message: string, context?: Record<string, unknown>);
8
+ toJSON(): Record<string, unknown>;
9
+ toLogString(): string;
10
+ }
11
+ export declare class LoopNotFoundError extends LoopError {
12
+ constructor(identifier: string, identifierType?: 'id' | 'name');
13
+ }
14
+ export declare class LoopAlreadyExistsError extends LoopError {
15
+ constructor(loopId: LoopId);
16
+ }
17
+ export declare class LoopNameTakenError extends LoopError {
18
+ constructor(name: string);
19
+ }
20
+ export declare class LoopInvalidStatusError extends LoopError {
21
+ constructor(loopId: LoopId, currentStatus: LoopStatus, operation: string, allowedStatuses: LoopStatus[]);
22
+ }
23
+ export declare class LoopExpiredError extends LoopError {
24
+ constructor(loopId: LoopId, expiredAt: Date);
25
+ }
26
+ export declare class LoopLimitExceededError extends LoopError {
27
+ constructor(currentCount: number, maxCount: number);
28
+ }
29
+ export declare class InvalidIntervalError extends LoopError {
30
+ constructor(interval: string, reason: string);
31
+ }
32
+ export declare class IntervalTooShortError extends LoopError {
33
+ constructor(interval: string, milliseconds: number, minimumMs: number);
34
+ }
35
+ export declare class InvalidCronExpressionError extends LoopError {
36
+ constructor(expression: string, parseError: string);
37
+ }
38
+ export declare class LoopExecutionError extends LoopError {
39
+ constructor(loopId: LoopId, runId: LoopRunId, cause: string, originalError?: Error);
40
+ }
41
+ export declare class LoopExecutionTimeoutError extends LoopError {
42
+ constructor(loopId: LoopId, runId: LoopRunId, timeoutMs: number);
43
+ }
44
+ export declare class LoopRunNotFoundError extends LoopError {
45
+ constructor(loopId: LoopId, runId: LoopRunId);
46
+ }
47
+ export declare class DaemonNotRunningError extends LoopError {
48
+ constructor();
49
+ }
50
+ export declare class DaemonAlreadyRunningError extends LoopError {
51
+ constructor(pid: number);
52
+ }
53
+ export declare class DaemonStartFailedError extends LoopError {
54
+ constructor(reason: string, originalError?: Error);
55
+ }
56
+ export declare class DaemonStopFailedError extends LoopError {
57
+ constructor(pid: number, reason: string);
58
+ }
59
+ export declare class DaemonCommunicationError extends LoopError {
60
+ constructor(operation: string, reason: string);
61
+ }
62
+ export declare class DaemonConnectionError extends LoopError {
63
+ constructor(host: string, port: number, reason: string);
64
+ }
65
+ export declare class DaemonIPCTimeoutError extends LoopError {
66
+ constructor(operation: string, timeoutMs: number);
67
+ }
68
+ export declare class SchedulerError extends LoopError {
69
+ constructor(message: string, context?: Record<string, unknown>);
70
+ }
71
+ export declare class InvalidLoopStateError extends LoopError {
72
+ constructor(loopId: LoopId, currentStatus: LoopStatus, expectedState: string);
73
+ }
74
+ export declare class StorageReadError extends LoopError {
75
+ constructor(path: string, reason: string, originalError?: Error);
76
+ }
77
+ export declare class StorageWriteError extends LoopError {
78
+ constructor(path: string, reason: string, originalError?: Error);
79
+ }
80
+ export declare class StorageCorruptError extends LoopError {
81
+ constructor(path: string, reason: string);
82
+ }
83
+ export declare class StorageMigrationError extends LoopError {
84
+ constructor(fromVersion: number, toVersion: number, reason: string);
85
+ }
86
+ export declare class ValidationError extends LoopError {
87
+ constructor(field: string, value: unknown, reason: string);
88
+ }
89
+ export declare class InvalidPromptError extends LoopError {
90
+ constructor(reason: string);
91
+ }
92
+ export declare class InvalidNameError extends LoopError {
93
+ constructor(name: string, reason: string);
94
+ }
95
+ export declare class InvalidOptionsError extends LoopError {
96
+ constructor(option: string, value: unknown, reason: string);
97
+ }
98
+ export declare class NotificationFailedError extends LoopError {
99
+ constructor(method: string, reason: string, originalError?: Error);
100
+ }
101
+ export declare class WebhookError extends LoopError {
102
+ constructor(url: string, statusCode: number | null, reason: string);
103
+ }
104
+ export declare class SlackError extends LoopError {
105
+ constructor(reason: string, originalError?: Error);
106
+ }
107
+ export declare class ConfigError extends LoopError {
108
+ constructor(key: string, reason: string);
109
+ }
110
+ export declare class InvalidConfigError extends LoopError {
111
+ constructor(key: string, value: unknown, expectedType: string);
112
+ }
113
+ export declare function isLoopError(error: unknown): error is LoopError;
114
+ export declare function hasErrorCode(error: unknown, code: LoopErrorCode): error is LoopError;
115
+ export declare function wrapError(error: unknown, code: LoopErrorCode, message: string): LoopError;
@@ -0,0 +1,312 @@
1
+ export class LoopError extends Error {
2
+ code;
3
+ context;
4
+ timestamp;
5
+ constructor(code, message, context = {}) {
6
+ super(message);
7
+ this.name = 'LoopError';
8
+ this.code = code;
9
+ this.context = context;
10
+ this.timestamp = new Date();
11
+ if (Error.captureStackTrace) {
12
+ Error.captureStackTrace(this, this.constructor);
13
+ }
14
+ }
15
+ toJSON() {
16
+ return {
17
+ name: this.name,
18
+ code: this.code,
19
+ message: this.message,
20
+ context: this.context,
21
+ timestamp: this.timestamp.toISOString(),
22
+ stack: this.stack,
23
+ };
24
+ }
25
+ toLogString() {
26
+ const contextStr = Object.keys(this.context).length > 0
27
+ ? ` Context: ${JSON.stringify(this.context)}`
28
+ : '';
29
+ return `[${this.code}] ${this.message}${contextStr}`;
30
+ }
31
+ }
32
+ export class LoopNotFoundError extends LoopError {
33
+ constructor(identifier, identifierType = 'id') {
34
+ super('LOOP_NOT_FOUND', `Loop not found: ${identifier}`, { identifier, identifierType });
35
+ this.name = 'LoopNotFoundError';
36
+ }
37
+ }
38
+ export class LoopAlreadyExistsError extends LoopError {
39
+ constructor(loopId) {
40
+ super('LOOP_ALREADY_EXISTS', `Loop already exists with ID: ${loopId}`, { loopId });
41
+ this.name = 'LoopAlreadyExistsError';
42
+ }
43
+ }
44
+ export class LoopNameTakenError extends LoopError {
45
+ constructor(name) {
46
+ super('LOOP_NAME_TAKEN', `Loop name already in use: "${name}"`, { name });
47
+ this.name = 'LoopNameTakenError';
48
+ }
49
+ }
50
+ export class LoopInvalidStatusError extends LoopError {
51
+ constructor(loopId, currentStatus, operation, allowedStatuses) {
52
+ super('LOOP_INVALID_STATUS', `Cannot ${operation} loop in "${currentStatus}" status. Allowed: ${allowedStatuses.join(', ')}`, { loopId, currentStatus, operation, allowedStatuses });
53
+ this.name = 'LoopInvalidStatusError';
54
+ }
55
+ }
56
+ export class LoopExpiredError extends LoopError {
57
+ constructor(loopId, expiredAt) {
58
+ super('LOOP_EXPIRED', `Loop has expired at ${expiredAt.toISOString()}`, { loopId, expiredAt: expiredAt.toISOString() });
59
+ this.name = 'LoopExpiredError';
60
+ }
61
+ }
62
+ export class LoopLimitExceededError extends LoopError {
63
+ constructor(currentCount, maxCount) {
64
+ super('LOOP_LIMIT_EXCEEDED', `Maximum concurrent loops limit reached (${currentCount}/${maxCount})`, { currentCount, maxCount });
65
+ this.name = 'LoopLimitExceededError';
66
+ }
67
+ }
68
+ export class InvalidIntervalError extends LoopError {
69
+ constructor(interval, reason) {
70
+ super('INVALID_INTERVAL', `Invalid interval "${interval}": ${reason}`, { interval, reason });
71
+ this.name = 'InvalidIntervalError';
72
+ }
73
+ }
74
+ export class IntervalTooShortError extends LoopError {
75
+ constructor(interval, milliseconds, minimumMs) {
76
+ const minFormatted = formatDuration(minimumMs);
77
+ const actualFormatted = formatDuration(milliseconds);
78
+ super('INTERVAL_TOO_SHORT', `Interval "${interval}" (${actualFormatted}) is shorter than minimum allowed (${minFormatted})`, { interval, milliseconds, minimumMs });
79
+ this.name = 'IntervalTooShortError';
80
+ }
81
+ }
82
+ export class InvalidCronExpressionError extends LoopError {
83
+ constructor(expression, parseError) {
84
+ super('INVALID_CRON_EXPRESSION', `Invalid cron expression "${expression}": ${parseError}`, { expression, parseError });
85
+ this.name = 'InvalidCronExpressionError';
86
+ }
87
+ }
88
+ export class LoopExecutionError extends LoopError {
89
+ constructor(loopId, runId, cause, originalError) {
90
+ super('LOOP_EXECUTION_ERROR', `Loop execution failed: ${cause}`, {
91
+ loopId,
92
+ runId,
93
+ cause,
94
+ originalError: originalError?.message,
95
+ originalStack: originalError?.stack,
96
+ });
97
+ this.name = 'LoopExecutionError';
98
+ if (originalError) {
99
+ this.cause = originalError;
100
+ }
101
+ }
102
+ }
103
+ export class LoopExecutionTimeoutError extends LoopError {
104
+ constructor(loopId, runId, timeoutMs) {
105
+ super('LOOP_EXECUTION_TIMEOUT', `Loop execution timed out after ${formatDuration(timeoutMs)}`, { loopId, runId, timeoutMs });
106
+ this.name = 'LoopExecutionTimeoutError';
107
+ }
108
+ }
109
+ export class LoopRunNotFoundError extends LoopError {
110
+ constructor(loopId, runId) {
111
+ super('LOOP_RUN_NOT_FOUND', `Loop run not found: ${runId}`, { loopId, runId });
112
+ this.name = 'LoopRunNotFoundError';
113
+ }
114
+ }
115
+ export class DaemonNotRunningError extends LoopError {
116
+ constructor() {
117
+ super('DAEMON_NOT_RUNNING', 'Loop daemon is not running. Start it with "flowdot daemon start"', {});
118
+ this.name = 'DaemonNotRunningError';
119
+ }
120
+ }
121
+ export class DaemonAlreadyRunningError extends LoopError {
122
+ constructor(pid) {
123
+ super('DAEMON_ALREADY_RUNNING', `Loop daemon is already running (PID: ${pid})`, { pid });
124
+ this.name = 'DaemonAlreadyRunningError';
125
+ }
126
+ }
127
+ export class DaemonStartFailedError extends LoopError {
128
+ constructor(reason, originalError) {
129
+ super('DAEMON_START_FAILED', `Failed to start loop daemon: ${reason}`, {
130
+ reason,
131
+ originalError: originalError?.message,
132
+ });
133
+ this.name = 'DaemonStartFailedError';
134
+ if (originalError) {
135
+ this.cause = originalError;
136
+ }
137
+ }
138
+ }
139
+ export class DaemonStopFailedError extends LoopError {
140
+ constructor(pid, reason) {
141
+ super('DAEMON_STOP_FAILED', `Failed to stop loop daemon (PID: ${pid}): ${reason}`, { pid, reason });
142
+ this.name = 'DaemonStopFailedError';
143
+ }
144
+ }
145
+ export class DaemonCommunicationError extends LoopError {
146
+ constructor(operation, reason) {
147
+ super('DAEMON_COMMUNICATION_ERROR', `Daemon communication failed during ${operation}: ${reason}`, { operation, reason });
148
+ this.name = 'DaemonCommunicationError';
149
+ }
150
+ }
151
+ export class DaemonConnectionError extends LoopError {
152
+ constructor(host, port, reason) {
153
+ super('DAEMON_CONNECTION_ERROR', `Failed to connect to daemon at ${host}:${port}: ${reason}`, { host, port, reason });
154
+ this.name = 'DaemonConnectionError';
155
+ }
156
+ }
157
+ export class DaemonIPCTimeoutError extends LoopError {
158
+ constructor(operation, timeoutMs) {
159
+ super('DAEMON_IPC_TIMEOUT', `IPC request timed out after ${formatDuration(timeoutMs)}: ${operation}`, { operation, timeoutMs });
160
+ this.name = 'DaemonIPCTimeoutError';
161
+ }
162
+ }
163
+ export class SchedulerError extends LoopError {
164
+ constructor(message, context = {}) {
165
+ super('SCHEDULER_ERROR', message, context);
166
+ this.name = 'SchedulerError';
167
+ }
168
+ }
169
+ export class InvalidLoopStateError extends LoopError {
170
+ constructor(loopId, currentStatus, expectedState) {
171
+ super('INVALID_LOOP_STATE', `Loop ${loopId} is in "${currentStatus}" state, expected "${expectedState}"`, { loopId, currentStatus, expectedState });
172
+ this.name = 'InvalidLoopStateError';
173
+ }
174
+ }
175
+ export class StorageReadError extends LoopError {
176
+ constructor(path, reason, originalError) {
177
+ super('STORAGE_READ_ERROR', `Failed to read from storage: ${reason}`, {
178
+ path,
179
+ reason,
180
+ originalError: originalError?.message,
181
+ });
182
+ this.name = 'StorageReadError';
183
+ if (originalError) {
184
+ this.cause = originalError;
185
+ }
186
+ }
187
+ }
188
+ export class StorageWriteError extends LoopError {
189
+ constructor(path, reason, originalError) {
190
+ super('STORAGE_WRITE_ERROR', `Failed to write to storage: ${reason}`, {
191
+ path,
192
+ reason,
193
+ originalError: originalError?.message,
194
+ });
195
+ this.name = 'StorageWriteError';
196
+ if (originalError) {
197
+ this.cause = originalError;
198
+ }
199
+ }
200
+ }
201
+ export class StorageCorruptError extends LoopError {
202
+ constructor(path, reason) {
203
+ super('STORAGE_CORRUPT', `Storage data is corrupt at ${path}: ${reason}`, { path, reason });
204
+ this.name = 'StorageCorruptError';
205
+ }
206
+ }
207
+ export class StorageMigrationError extends LoopError {
208
+ constructor(fromVersion, toVersion, reason) {
209
+ super('STORAGE_MIGRATION_ERROR', `Failed to migrate storage from v${fromVersion} to v${toVersion}: ${reason}`, { fromVersion, toVersion, reason });
210
+ this.name = 'StorageMigrationError';
211
+ }
212
+ }
213
+ export class ValidationError extends LoopError {
214
+ constructor(field, value, reason) {
215
+ super('VALIDATION_ERROR', `Validation failed for "${field}": ${reason}`, { field, value, reason });
216
+ this.name = 'ValidationError';
217
+ }
218
+ }
219
+ export class InvalidPromptError extends LoopError {
220
+ constructor(reason) {
221
+ super('INVALID_PROMPT', `Invalid prompt: ${reason}`, { reason });
222
+ this.name = 'InvalidPromptError';
223
+ }
224
+ }
225
+ export class InvalidNameError extends LoopError {
226
+ constructor(name, reason) {
227
+ super('INVALID_NAME', `Invalid loop name "${name}": ${reason}`, { name, reason });
228
+ this.name = 'InvalidNameError';
229
+ }
230
+ }
231
+ export class InvalidOptionsError extends LoopError {
232
+ constructor(option, value, reason) {
233
+ super('INVALID_OPTIONS', `Invalid option "${option}": ${reason}`, { option, value, reason });
234
+ this.name = 'InvalidOptionsError';
235
+ }
236
+ }
237
+ export class NotificationFailedError extends LoopError {
238
+ constructor(method, reason, originalError) {
239
+ super('NOTIFICATION_FAILED', `Failed to send ${method} notification: ${reason}`, {
240
+ method,
241
+ reason,
242
+ originalError: originalError?.message,
243
+ });
244
+ this.name = 'NotificationFailedError';
245
+ if (originalError) {
246
+ this.cause = originalError;
247
+ }
248
+ }
249
+ }
250
+ export class WebhookError extends LoopError {
251
+ constructor(url, statusCode, reason) {
252
+ super('WEBHOOK_ERROR', `Webhook request failed: ${reason}`, { url, statusCode, reason });
253
+ this.name = 'WebhookError';
254
+ }
255
+ }
256
+ export class SlackError extends LoopError {
257
+ constructor(reason, originalError) {
258
+ super('SLACK_ERROR', `Slack notification failed: ${reason}`, {
259
+ reason,
260
+ originalError: originalError?.message,
261
+ });
262
+ this.name = 'SlackError';
263
+ if (originalError) {
264
+ this.cause = originalError;
265
+ }
266
+ }
267
+ }
268
+ export class ConfigError extends LoopError {
269
+ constructor(key, reason) {
270
+ super('CONFIG_ERROR', `Configuration error for "${key}": ${reason}`, { key, reason });
271
+ this.name = 'ConfigError';
272
+ }
273
+ }
274
+ export class InvalidConfigError extends LoopError {
275
+ constructor(key, value, expectedType) {
276
+ super('INVALID_CONFIG', `Invalid configuration value for "${key}": expected ${expectedType}`, { key, value, expectedType });
277
+ this.name = 'InvalidConfigError';
278
+ }
279
+ }
280
+ function formatDuration(ms) {
281
+ if (ms < 1000) {
282
+ return `${ms}ms`;
283
+ }
284
+ if (ms < 60000) {
285
+ return `${Math.round(ms / 1000)}s`;
286
+ }
287
+ if (ms < 3600000) {
288
+ return `${Math.round(ms / 60000)}m`;
289
+ }
290
+ if (ms < 86400000) {
291
+ return `${Math.round(ms / 3600000)}h`;
292
+ }
293
+ return `${Math.round(ms / 86400000)}d`;
294
+ }
295
+ export function isLoopError(error) {
296
+ return error instanceof LoopError;
297
+ }
298
+ export function hasErrorCode(error, code) {
299
+ return isLoopError(error) && error.code === code;
300
+ }
301
+ export function wrapError(error, code, message) {
302
+ if (isLoopError(error)) {
303
+ return error;
304
+ }
305
+ const originalError = error instanceof Error ? error : new Error(String(error));
306
+ const loopError = new LoopError(code, message, {
307
+ originalError: originalError.message,
308
+ originalStack: originalError.stack,
309
+ });
310
+ loopError.cause = originalError;
311
+ return loopError;
312
+ }
@@ -0,0 +1,11 @@
1
+ export type { LoopId, LoopRunId, LoopStatus, LoopRunStatus, NotifyMethod, ModelTier, Loop, LoopRun, LoopConfig, LoopDaemonConfig, LoopWebhookConfig, CreateLoopInput, LoopCreateInput, DaemonStatus, NotificationPayload, Notifier, IPCMessage, IPCRequest, IPCResponse, IPCEvent, AgenticResponseFunction, AgenticResponseCallback, ExecuteActionFunction, Logger, } from './types.js';
2
+ export { LoopError, LoopNotFoundError, LoopAlreadyExistsError, LoopNameTakenError, LoopInvalidStatusError, LoopExpiredError, LoopLimitExceededError, InvalidIntervalError, IntervalTooShortError, InvalidCronExpressionError, LoopExecutionError, LoopExecutionTimeoutError, LoopRunNotFoundError, DaemonNotRunningError, DaemonAlreadyRunningError, DaemonStartFailedError, DaemonStopFailedError, DaemonCommunicationError, DaemonConnectionError, DaemonIPCTimeoutError, SchedulerError, InvalidLoopStateError, StorageReadError, StorageWriteError, StorageCorruptError, StorageMigrationError, ValidationError, InvalidPromptError, InvalidNameError, InvalidOptionsError, NotificationFailedError, WebhookError, SlackError, ConfigError, InvalidConfigError, isLoopError, hasErrorCode, wrapError, } from './errors.js';
3
+ export { IntervalParser, createIntervalParser, parseInterval, validateInterval, isValidInterval, calculateNextRun, type IntervalParserOptions, type ParseResult, } from './IntervalParser.js';
4
+ export { LoopValidator, createLoopValidator, validateCreateInput, validateUpdateInput, validateLoopName, validatePrompt, DEFAULT_VALIDATION_CONSTRAINTS, type ValidationConstraints, type ValidationResult, type ValidationIssue, } from './LoopValidator.js';
5
+ export { LoopStore, createLoopStore, initializeLoopStore, type LoopStoreOptions, } from './LoopStore.js';
6
+ export { LoopScheduler, createLoopScheduler, createAndStartScheduler, type LoopSchedulerOptions, type SchedulerOptions, type SchedulerStats, type SchedulerEvents, } from './LoopScheduler.js';
7
+ export { LoopExecutor, createLoopExecutor, createLoopExecutorWithConfig, createEmptyRun, createSuccessfulRun, createFailedRun, type LoopExecutorOptions, type ExecutorOptions, type ExecutionResult, type ExecutionContext, type ExecutorEvents, type AgentResponseFunction, type AgentResponse, type AgentAction, type ActionExecutorFunction, type ActionResult, } from './LoopExecutor.js';
8
+ export { LoopManager, createLoopManager, type LoopManagerOptions, type ManagerStats, type ManagerEvents, } from './LoopManager.js';
9
+ export { IPCServer, IPCClient, isDaemonRunning, waitForDaemon, daemonRequest, type IPCServerOptions, type IPCClientOptions, type RequestHandler, } from './DaemonIPC.js';
10
+ export { LoopDaemon, startDaemon, stopDaemon, getDaemonStatus, isProcessRunning, createLoopDaemon, runDaemon, type LoopDaemonOptions, type DaemonControlOptions, type DaemonMethod, type DaemonEvents, } from './LoopDaemon.js';
11
+ export { BaseNotifier, NullNotifier, TerminalNotifier, WebhookNotifier, SlackNotifier, createNotifier, createNotifierSync, createNotifiers, isValidNotifyMethod, getAvailableNotifyMethods, MAX_MESSAGE_LENGTH, MAX_TITLE_LENGTH, type NotifierConfig, type NotifierFactoryConfig, type TerminalNotifierOptions, type WebhookNotifierOptions, type SlackNotifierOptions, } from './notifications/index.js';
@@ -0,0 +1,10 @@
1
+ export { LoopError, LoopNotFoundError, LoopAlreadyExistsError, LoopNameTakenError, LoopInvalidStatusError, LoopExpiredError, LoopLimitExceededError, InvalidIntervalError, IntervalTooShortError, InvalidCronExpressionError, LoopExecutionError, LoopExecutionTimeoutError, LoopRunNotFoundError, DaemonNotRunningError, DaemonAlreadyRunningError, DaemonStartFailedError, DaemonStopFailedError, DaemonCommunicationError, DaemonConnectionError, DaemonIPCTimeoutError, SchedulerError, InvalidLoopStateError, StorageReadError, StorageWriteError, StorageCorruptError, StorageMigrationError, ValidationError, InvalidPromptError, InvalidNameError, InvalidOptionsError, NotificationFailedError, WebhookError, SlackError, ConfigError, InvalidConfigError, isLoopError, hasErrorCode, wrapError, } from './errors.js';
2
+ export { IntervalParser, createIntervalParser, parseInterval, validateInterval, isValidInterval, calculateNextRun, } from './IntervalParser.js';
3
+ export { LoopValidator, createLoopValidator, validateCreateInput, validateUpdateInput, validateLoopName, validatePrompt, DEFAULT_VALIDATION_CONSTRAINTS, } from './LoopValidator.js';
4
+ export { LoopStore, createLoopStore, initializeLoopStore, } from './LoopStore.js';
5
+ export { LoopScheduler, createLoopScheduler, createAndStartScheduler, } from './LoopScheduler.js';
6
+ export { LoopExecutor, createLoopExecutor, createLoopExecutorWithConfig, createEmptyRun, createSuccessfulRun, createFailedRun, } from './LoopExecutor.js';
7
+ export { LoopManager, createLoopManager, } from './LoopManager.js';
8
+ export { IPCServer, IPCClient, isDaemonRunning, waitForDaemon, daemonRequest, } from './DaemonIPC.js';
9
+ export { LoopDaemon, startDaemon, stopDaemon, getDaemonStatus, isProcessRunning, createLoopDaemon, runDaemon, } from './LoopDaemon.js';
10
+ export { BaseNotifier, NullNotifier, TerminalNotifier, WebhookNotifier, SlackNotifier, createNotifier, createNotifierSync, createNotifiers, isValidNotifyMethod, getAvailableNotifyMethods, MAX_MESSAGE_LENGTH, MAX_TITLE_LENGTH, } from './notifications/index.js';
@@ -0,0 +1,28 @@
1
+ import type { NotifyMethod, NotificationPayload, Notifier, LoopWebhookConfig, Logger } from '../types.js';
2
+ export declare const MAX_MESSAGE_LENGTH = 2000;
3
+ export declare const MAX_TITLE_LENGTH = 100;
4
+ export declare abstract class BaseNotifier implements Notifier {
5
+ abstract readonly type: NotifyMethod;
6
+ protected readonly enabled: boolean;
7
+ protected readonly logger: Logger;
8
+ constructor(options?: {
9
+ logger?: Logger;
10
+ });
11
+ abstract send(payload: NotificationPayload): Promise<void>;
12
+ protected formatTitle(payload: NotificationPayload): string;
13
+ protected formatMessage(payload: NotificationPayload): string;
14
+ protected getStatusIcon(status?: string): string;
15
+ protected truncate(text: string, maxLength: number): string;
16
+ protected logNotification(payload: NotificationPayload, success: boolean): void;
17
+ protected handleError(error: unknown, payload: NotificationPayload): never;
18
+ }
19
+ export declare class NullNotifier extends BaseNotifier {
20
+ readonly type: NotifyMethod;
21
+ send(_payload: NotificationPayload): Promise<void>;
22
+ }
23
+ export interface NotifierConfig {
24
+ webhooks?: LoopWebhookConfig;
25
+ webhookUrl?: string | null;
26
+ slackWebhookUrl?: string | null;
27
+ logger?: Logger;
28
+ }
@@ -0,0 +1,78 @@
1
+ import { NotificationFailedError } from '../errors.js';
2
+ export const MAX_MESSAGE_LENGTH = 2000;
3
+ export const MAX_TITLE_LENGTH = 100;
4
+ const noopLogger = {
5
+ debug: () => { },
6
+ info: () => { },
7
+ warn: () => { },
8
+ error: () => { },
9
+ };
10
+ export class BaseNotifier {
11
+ enabled = true;
12
+ logger;
13
+ constructor(options = {}) {
14
+ this.logger = options.logger ?? noopLogger;
15
+ }
16
+ formatTitle(payload) {
17
+ const name = payload.loopName ?? payload.loopId.substring(0, 8);
18
+ const statusIcon = this.getStatusIcon(payload.status);
19
+ const title = payload.title || `Loop: ${name}`;
20
+ const formatted = statusIcon ? `${statusIcon} ${title}` : title;
21
+ return this.truncate(formatted, MAX_TITLE_LENGTH);
22
+ }
23
+ formatMessage(payload) {
24
+ const parts = [];
25
+ if (payload.message) {
26
+ parts.push(payload.message);
27
+ }
28
+ if (payload.error) {
29
+ parts.push(`\nError: ${payload.error}`);
30
+ }
31
+ parts.push(`\nTime: ${payload.timestamp.toLocaleString()}`);
32
+ return this.truncate(parts.join(''), MAX_MESSAGE_LENGTH);
33
+ }
34
+ getStatusIcon(status) {
35
+ switch (status) {
36
+ case 'success':
37
+ return '✓';
38
+ case 'error':
39
+ return '✗';
40
+ case 'running':
41
+ return '⟳';
42
+ default:
43
+ return '';
44
+ }
45
+ }
46
+ truncate(text, maxLength) {
47
+ if (text.length <= maxLength) {
48
+ return text;
49
+ }
50
+ return text.substring(0, maxLength - 3) + '...';
51
+ }
52
+ logNotification(payload, success) {
53
+ const level = success ? 'debug' : 'warn';
54
+ const message = success
55
+ ? `Notification sent via ${this.type}`
56
+ : `Notification failed via ${this.type}`;
57
+ this.logger[level]('LOOP', message, {
58
+ loopId: payload.loopId,
59
+ runId: payload.runId,
60
+ status: payload.status,
61
+ });
62
+ }
63
+ handleError(error, payload) {
64
+ const message = error instanceof Error ? error.message : String(error);
65
+ const originalError = error instanceof Error ? error : undefined;
66
+ this.logger.error('LOOP', `Notification failed: ${message}`, {
67
+ type: this.type,
68
+ loopId: payload.loopId,
69
+ runId: payload.runId,
70
+ });
71
+ throw new NotificationFailedError(this.type, message, originalError);
72
+ }
73
+ }
74
+ export class NullNotifier extends BaseNotifier {
75
+ type = 'none';
76
+ async send(_payload) {
77
+ }
78
+ }
@@ -0,0 +1,28 @@
1
+ import type { NotifyMethod, NotificationPayload, Logger } from '../types.js';
2
+ import { BaseNotifier } from './Notifier.js';
3
+ export interface SlackNotifierOptions {
4
+ webhookUrl: string;
5
+ timeout?: number;
6
+ retries?: number;
7
+ channel?: string;
8
+ username?: string;
9
+ iconEmoji?: string;
10
+ logger?: Logger;
11
+ }
12
+ export declare class SlackNotifier extends BaseNotifier {
13
+ readonly type: NotifyMethod;
14
+ private readonly webhookUrl;
15
+ private readonly timeout;
16
+ private readonly retries;
17
+ private readonly channel?;
18
+ private readonly username;
19
+ private readonly iconEmoji;
20
+ constructor(options: SlackNotifierOptions);
21
+ send(payload: NotificationPayload): Promise<void>;
22
+ private buildPayload;
23
+ private sendRequest;
24
+ private getStatusColor;
25
+ private getStatusEmoji;
26
+ private truncateForSlack;
27
+ private delay;
28
+ }