@rk0429/agentic-relay 20.1.1 → 21.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 (86) hide show
  1. package/dist/application/event-log-sink.d.ts +13 -9
  2. package/dist/application/event-log-sink.js +29 -22
  3. package/dist/application/event-log-sink.js.map +1 -1
  4. package/dist/application/routine-catalog-service.d.ts +36 -0
  5. package/dist/application/routine-catalog-service.js +90 -0
  6. package/dist/application/routine-catalog-service.js.map +1 -0
  7. package/dist/application/routine-daemon-service.d.ts +48 -0
  8. package/dist/application/routine-daemon-service.js +176 -0
  9. package/dist/application/routine-daemon-service.js.map +1 -0
  10. package/dist/application/routine-event-log-sink.d.ts +42 -0
  11. package/dist/application/routine-event-log-sink.js +82 -0
  12. package/dist/application/routine-event-log-sink.js.map +1 -0
  13. package/dist/application/routine-run-service.d.ts +56 -0
  14. package/dist/application/routine-run-service.js +370 -0
  15. package/dist/application/routine-run-service.js.map +1 -0
  16. package/dist/application/routine-scheduler.d.ts +34 -0
  17. package/dist/application/routine-scheduler.js +82 -0
  18. package/dist/application/routine-scheduler.js.map +1 -0
  19. package/dist/application/routine-status-query-service.d.ts +41 -0
  20. package/dist/application/routine-status-query-service.js +98 -0
  21. package/dist/application/routine-status-query-service.js.map +1 -0
  22. package/dist/application/spawn-agents-service.js +32 -5
  23. package/dist/application/spawn-agents-service.js.map +1 -1
  24. package/dist/application/workflow-execution-service.d.ts +5 -1
  25. package/dist/application/workflow-execution-service.js +82 -23
  26. package/dist/application/workflow-execution-service.js.map +1 -1
  27. package/dist/bin/relay.d.ts +58 -1
  28. package/dist/bin/relay.js +421 -50
  29. package/dist/bin/relay.js.map +1 -1
  30. package/dist/core/types.d.ts +1 -1
  31. package/dist/domain/loop-execution.d.ts +1 -1
  32. package/dist/domain/loop-execution.js +8 -4
  33. package/dist/domain/loop-execution.js.map +1 -1
  34. package/dist/domain/routine-definition.d.ts +18 -0
  35. package/dist/domain/routine-definition.js +19 -0
  36. package/dist/domain/routine-definition.js.map +1 -0
  37. package/dist/domain/routine-execution-state.d.ts +35 -0
  38. package/dist/domain/routine-execution-state.js +97 -0
  39. package/dist/domain/routine-execution-state.js.map +1 -0
  40. package/dist/domain/routine-firing.d.ts +23 -0
  41. package/dist/domain/routine-firing.js +47 -0
  42. package/dist/domain/routine-firing.js.map +1 -0
  43. package/dist/domain/routine-loader.d.ts +9 -0
  44. package/dist/domain/routine-loader.js +129 -0
  45. package/dist/domain/routine-loader.js.map +1 -0
  46. package/dist/domain/routine-name.d.ts +6 -0
  47. package/dist/domain/routine-name.js +31 -0
  48. package/dist/domain/routine-name.js.map +1 -0
  49. package/dist/domain/routine-schedule.d.ts +24 -0
  50. package/dist/domain/routine-schedule.js +126 -0
  51. package/dist/domain/routine-schedule.js.map +1 -0
  52. package/dist/domain/trigger-config.d.ts +25 -0
  53. package/dist/domain/trigger-config.js +86 -0
  54. package/dist/domain/trigger-config.js.map +1 -0
  55. package/dist/domain/workflow-loader.d.ts +2 -0
  56. package/dist/domain/workflow-loader.js +12 -2
  57. package/dist/domain/workflow-loader.js.map +1 -1
  58. package/dist/domain/workflow-schema.d.ts +20 -1
  59. package/dist/domain/workflow-schema.js +43 -7
  60. package/dist/domain/workflow-schema.js.map +1 -1
  61. package/dist/infrastructure/command-execution-gateway.d.ts +21 -0
  62. package/dist/infrastructure/command-execution-gateway.js +24 -0
  63. package/dist/infrastructure/command-execution-gateway.js.map +1 -0
  64. package/dist/infrastructure/store/file-utils.d.ts +2 -0
  65. package/dist/infrastructure/store/file-utils.js +23 -0
  66. package/dist/infrastructure/store/file-utils.js.map +1 -0
  67. package/dist/infrastructure/store/relay-store.d.ts +1 -0
  68. package/dist/infrastructure/store/relay-store.js +6 -22
  69. package/dist/infrastructure/store/relay-store.js.map +1 -1
  70. package/dist/infrastructure/store/routine-execution-lease-repository.d.ts +31 -0
  71. package/dist/infrastructure/store/routine-execution-lease-repository.js +191 -0
  72. package/dist/infrastructure/store/routine-execution-lease-repository.js.map +1 -0
  73. package/dist/infrastructure/store/routine-runtime-projection-repository.d.ts +25 -0
  74. package/dist/infrastructure/store/routine-runtime-projection-repository.js +126 -0
  75. package/dist/infrastructure/store/routine-runtime-projection-repository.js.map +1 -0
  76. package/dist/infrastructure/store/routine-state-repository.d.ts +21 -0
  77. package/dist/infrastructure/store/routine-state-repository.js +139 -0
  78. package/dist/infrastructure/store/routine-state-repository.js.map +1 -0
  79. package/dist/interfaces/cli/loop-progress-renderer.js +3 -1
  80. package/dist/interfaces/cli/loop-progress-renderer.js.map +1 -1
  81. package/dist/interfaces/cli/relay-cli-args.d.ts +18 -2
  82. package/dist/interfaces/cli/relay-cli-args.js +171 -50
  83. package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
  84. package/dist/interfaces/mcp/relay-mcp-server.js +2 -2
  85. package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
  86. package/package.json +2 -1
@@ -0,0 +1,82 @@
1
+ import { RoutineName } from "../domain/routine-name.js";
2
+ export class RoutineEventLogSink {
3
+ store;
4
+ constructor(store) {
5
+ this.store = store;
6
+ }
7
+ async logTriggered(opts) {
8
+ await this.appendRoutineLog("routine_triggered", "running", opts, {
9
+ routine_name: toRoutineNameValue(opts.routineName),
10
+ scheduled_time: opts.scheduledTime.toISOString(),
11
+ trigger_time: opts.triggerTime.toISOString(),
12
+ is_catch_up: opts.isCatchUp,
13
+ }, opts.occurredAt);
14
+ if (!opts.isCatchUp) {
15
+ return;
16
+ }
17
+ await this.appendRoutineLog("routine_catch_up", "running", opts, {
18
+ routine_name: toRoutineNameValue(opts.routineName),
19
+ scheduled_time: opts.scheduledTime.toISOString(),
20
+ trigger_time: opts.triggerTime.toISOString(),
21
+ skipped_count: opts.skippedCount ?? 0,
22
+ }, opts.occurredAt);
23
+ }
24
+ async logCompleted(opts) {
25
+ await this.appendRoutineLog("routine_completed", "success", opts, {
26
+ routine_name: toRoutineNameValue(opts.routineName),
27
+ duration: opts.duration,
28
+ }, opts.occurredAt);
29
+ }
30
+ async logFailed(opts) {
31
+ await this.appendRoutineLog("routine_failed", "error", opts, {
32
+ routine_name: toRoutineNameValue(opts.routineName),
33
+ duration: opts.duration,
34
+ error_code: opts.errorCode,
35
+ error_message: opts.errorMessage,
36
+ workflow_execution_id: opts.workflowExecutionId ?? null,
37
+ }, opts.occurredAt);
38
+ }
39
+ async logSkipped(opts) {
40
+ await this.appendRoutineLog("routine_skipped", "success", { ...opts, routineRunId: null }, {
41
+ routine_name: toRoutineNameValue(opts.routineName),
42
+ reason: opts.reason,
43
+ scheduled_time: opts.scheduledTime.toISOString(),
44
+ }, opts.occurredAt);
45
+ }
46
+ async logStateUpdated(opts) {
47
+ await this.appendRoutineLog("routine_state_updated", "success", opts, {
48
+ routine_name: toRoutineNameValue(opts.routineName),
49
+ state: serializeRoutineState(opts.state),
50
+ }, opts.occurredAt);
51
+ }
52
+ async appendRoutineLog(eventType, status, envelope, payload, occurredAt = new Date()) {
53
+ const routineName = toRoutineNameValue(envelope.routineName);
54
+ const routineRunId = envelope.routineRunId ?? null;
55
+ await this.store.appendLog({
56
+ timestamp: occurredAt.toISOString(),
57
+ session_id: routineRunId ?? routineName,
58
+ event_type: eventType,
59
+ status,
60
+ details: {
61
+ routine: {
62
+ routine_name: routineName,
63
+ owner_pid: envelope.ownerPid,
64
+ routine_run_id: routineRunId,
65
+ ...payload,
66
+ },
67
+ },
68
+ });
69
+ }
70
+ }
71
+ function serializeRoutineState(state) {
72
+ return {
73
+ last_executed_at: state.lastExecutedAt?.toISOString() ?? null,
74
+ last_scheduled_at: state.lastScheduledAt?.toISOString() ?? null,
75
+ next_scheduled_at: state.nextScheduledAt?.toISOString() ?? null,
76
+ last_status: state.lastStatus,
77
+ };
78
+ }
79
+ function toRoutineNameValue(value) {
80
+ return value instanceof RoutineName ? value.value : value;
81
+ }
82
+ //# sourceMappingURL=routine-event-log-sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routine-event-log-sink.js","sourceRoot":"","sources":["../../src/application/routine-event-log-sink.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { LoopExecutionResult, ResumeLoopOptions, WorkflowExecutionService } from "./workflow-execution-service.js";
2
+ import { RoutineEventLogSink } from "./routine-event-log-sink.js";
3
+ import type { RoutineCatalogEntry } from "./routine-catalog-service.js";
4
+ import { RoutineExecutionLeaseRepository } from "../infrastructure/store/routine-execution-lease-repository.js";
5
+ import { RoutineRuntimeProjectionRepository } from "../infrastructure/store/routine-runtime-projection-repository.js";
6
+ import { RoutineStateRepository } from "../infrastructure/store/routine-state-repository.js";
7
+ export interface RoutineRunServiceDeps {
8
+ workflowExecutionService: Pick<WorkflowExecutionService, "startLoop" | "resumeLoop">;
9
+ leaseRepo: RoutineExecutionLeaseRepository;
10
+ runtimeRepo: RoutineRuntimeProjectionRepository;
11
+ stateRepo: RoutineStateRepository;
12
+ eventLog: RoutineEventLogSink;
13
+ }
14
+ export type RoutineDispatchSource = "scheduled" | "manual" | "catch_up" | "skip";
15
+ export interface RoutineDispatchOptions {
16
+ source: RoutineDispatchSource;
17
+ firingTimes?: Date[];
18
+ scheduledTime?: Date;
19
+ triggerTime?: Date;
20
+ skippedSchedules?: Date[];
21
+ vars?: Record<string, string>;
22
+ cwd?: string;
23
+ signal?: AbortSignal;
24
+ skippedCount?: number;
25
+ }
26
+ export interface RoutineDispatchRunResult {
27
+ status: "completed" | "failed" | "skipped";
28
+ scheduledTime: string;
29
+ triggerTime?: string;
30
+ routineRunId?: string;
31
+ workflowExecutionId?: string;
32
+ }
33
+ export interface RoutineDispatchResult {
34
+ status: "completed" | "failed" | "skipped";
35
+ runs: RoutineDispatchRunResult[];
36
+ }
37
+ export interface RoutineRunServiceOptions {
38
+ now?: () => Date;
39
+ ownerPid?: number;
40
+ heartbeatIntervalMs?: number;
41
+ }
42
+ export declare class RoutineRunService {
43
+ private readonly deps;
44
+ private readonly now;
45
+ private readonly ownerPid;
46
+ private readonly heartbeatIntervalMs;
47
+ constructor(deps: RoutineRunServiceDeps, options?: RoutineRunServiceOptions);
48
+ dispatch(entry: RoutineCatalogEntry, options: RoutineDispatchOptions): Promise<RoutineDispatchResult>;
49
+ dispatchResume(entry: RoutineCatalogEntry, options: Omit<ResumeLoopOptions, "workflowPath" | "routineRunId">): Promise<LoopExecutionResult>;
50
+ dispatchSingle(entry: RoutineCatalogEntry, options: Omit<RoutineDispatchOptions, "firingTimes"> & {
51
+ scheduledTime: Date;
52
+ triggerTime: Date;
53
+ }): Promise<RoutineDispatchRunResult>;
54
+ private handleSkip;
55
+ private persistState;
56
+ }
@@ -0,0 +1,370 @@
1
+ import path from "node:path";
2
+ import { ValidationError } from "../core/errors.js";
3
+ import { createRoutineRunId, RoutineExecutionState } from "../domain/routine-execution-state.js";
4
+ import { FiringContext, RoutineFiring } from "../domain/routine-firing.js";
5
+ import { RoutineSchedule } from "../domain/routine-schedule.js";
6
+ const DEFAULT_HEARTBEAT_INTERVAL_MS = 15_000;
7
+ export class RoutineRunService {
8
+ deps;
9
+ now;
10
+ ownerPid;
11
+ heartbeatIntervalMs;
12
+ constructor(deps, options = {}) {
13
+ this.deps = deps;
14
+ this.now = options.now ?? (() => new Date());
15
+ this.ownerPid = options.ownerPid ?? process.pid;
16
+ this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
17
+ }
18
+ async dispatch(entry, options) {
19
+ if (options.source === "skip") {
20
+ const scheduledTime = options.skippedSchedules?.at(-1) ?? requireSingleTime(options);
21
+ await this.handleSkip(entry, scheduledTime, "missed_policy_skip");
22
+ return {
23
+ status: "skipped",
24
+ runs: [
25
+ {
26
+ status: "skipped",
27
+ scheduledTime: scheduledTime.toISOString(),
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ if (options.source === "catch_up") {
33
+ const firingTimes = requireFiringTimes(options);
34
+ if ((options.skippedSchedules?.length ?? 0) > 0) {
35
+ await this.handleSkip(entry, options.skippedSchedules.at(-1), "missed_policy_skip");
36
+ }
37
+ const runs = [];
38
+ for (const scheduledTime of firingTimes) {
39
+ const result = await this.dispatchSingle(entry, {
40
+ source: "catch_up",
41
+ scheduledTime,
42
+ triggerTime: this.now(),
43
+ vars: options.vars,
44
+ cwd: options.cwd,
45
+ signal: options.signal,
46
+ skippedCount: options.skippedCount ?? options.skippedSchedules?.length ?? 0,
47
+ });
48
+ runs.push(result);
49
+ if (result.status === "failed") {
50
+ return { status: "failed", runs };
51
+ }
52
+ }
53
+ return { status: "completed", runs };
54
+ }
55
+ const result = await this.dispatchSingle(entry, {
56
+ ...options,
57
+ scheduledTime: options.scheduledTime ??
58
+ (options.source === "manual" ? this.now() : requireSingleTime(options)),
59
+ triggerTime: options.triggerTime ?? this.now(),
60
+ });
61
+ return {
62
+ status: result.status,
63
+ runs: [result],
64
+ };
65
+ }
66
+ async dispatchResume(entry, options) {
67
+ const persisted = await this.deps.stateRepo.load(entry.definition.name);
68
+ const state = persisted?.state ?? RoutineExecutionState.create();
69
+ const schedule = new RoutineSchedule(entry.definition.trigger ?? null, state);
70
+ const routineRunId = createRoutineRunId();
71
+ const startedAt = this.now();
72
+ await this.deps.leaseRepo.acquire(entry.definition.name, this.ownerPid, routineRunId);
73
+ const heartbeat = setInterval(() => {
74
+ void this.deps.leaseRepo.heartbeat(entry.definition.name).catch(() => {
75
+ // Lease heartbeat loss is surfaced by the final execution result.
76
+ });
77
+ }, this.heartbeatIntervalMs);
78
+ try {
79
+ schedule.markFiring(routineRunId, startedAt, { isManual: true });
80
+ await this.deps.runtimeRepo.publish(entry.definition.name, {
81
+ routineRunId,
82
+ source: "manual",
83
+ startedAt: startedAt.toISOString(),
84
+ pid: this.ownerPid,
85
+ });
86
+ const result = await this.deps.workflowExecutionService.resumeLoop({
87
+ ...options,
88
+ workflowPath: entry.yamlPath,
89
+ routineRunId,
90
+ });
91
+ const finishedAt = this.now();
92
+ if (result.status === "completed") {
93
+ schedule.markCompleted(routineRunId, finishedAt);
94
+ await this.persistState(entry, schedule.executionState);
95
+ await this.deps.eventLog.logCompleted({
96
+ routineName: entry.definition.name,
97
+ ownerPid: this.ownerPid,
98
+ routineRunId,
99
+ duration: result.totalDuration,
100
+ });
101
+ await this.deps.eventLog.logStateUpdated({
102
+ routineName: entry.definition.name,
103
+ ownerPid: this.ownerPid,
104
+ routineRunId,
105
+ state: schedule.executionState,
106
+ });
107
+ return result;
108
+ }
109
+ schedule.markFailed(routineRunId, finishedAt);
110
+ await this.persistState(entry, schedule.executionState);
111
+ await this.deps.eventLog.logFailed({
112
+ routineName: entry.definition.name,
113
+ ownerPid: this.ownerPid,
114
+ routineRunId,
115
+ duration: result.totalDuration,
116
+ errorCode: "ROUTINE_EXECUTION_INTERRUPTED",
117
+ errorMessage: "Workflow execution was interrupted.",
118
+ workflowExecutionId: result.executionId,
119
+ });
120
+ await this.deps.eventLog.logStateUpdated({
121
+ routineName: entry.definition.name,
122
+ ownerPid: this.ownerPid,
123
+ routineRunId,
124
+ state: schedule.executionState,
125
+ });
126
+ return result;
127
+ }
128
+ catch (error) {
129
+ const failedAt = this.now();
130
+ const errorMessage = formatError(error);
131
+ try {
132
+ schedule.markFailed(routineRunId, failedAt);
133
+ await this.persistState(entry, schedule.executionState);
134
+ await this.deps.eventLog.logFailed({
135
+ routineName: entry.definition.name,
136
+ ownerPid: this.ownerPid,
137
+ routineRunId,
138
+ duration: 0,
139
+ errorCode: "ROUTINE_EXECUTION_FAILED",
140
+ errorMessage,
141
+ workflowExecutionId: null,
142
+ });
143
+ await this.deps.eventLog.logStateUpdated({
144
+ routineName: entry.definition.name,
145
+ ownerPid: this.ownerPid,
146
+ routineRunId,
147
+ state: schedule.executionState,
148
+ });
149
+ }
150
+ catch {
151
+ // Best-effort cleanup must not mask the original resume failure.
152
+ }
153
+ throw error;
154
+ }
155
+ finally {
156
+ clearInterval(heartbeat);
157
+ await this.deps.runtimeRepo.remove(entry.definition.name);
158
+ await this.deps.leaseRepo.release(entry.definition.name);
159
+ }
160
+ }
161
+ async dispatchSingle(entry, options) {
162
+ const persisted = await this.deps.stateRepo.load(entry.definition.name);
163
+ const state = persisted?.state ?? RoutineExecutionState.create();
164
+ const schedule = new RoutineSchedule(entry.definition.trigger ?? null, state);
165
+ const routineRunId = createRoutineRunId();
166
+ const isManual = options.source === "manual";
167
+ const isCatchUp = options.source === "catch_up";
168
+ const scheduledTime = options.scheduledTime;
169
+ const triggerTime = options.triggerTime;
170
+ try {
171
+ await this.deps.leaseRepo.acquire(entry.definition.name, this.ownerPid, routineRunId);
172
+ }
173
+ catch (error) {
174
+ if (isManual) {
175
+ throw error;
176
+ }
177
+ if (error instanceof ValidationError) {
178
+ await this.handleSkip(entry, scheduledTime, "concurrent_execution", state);
179
+ return {
180
+ status: "skipped",
181
+ scheduledTime: scheduledTime.toISOString(),
182
+ };
183
+ }
184
+ throw error;
185
+ }
186
+ const heartbeat = setInterval(() => {
187
+ void this.deps.leaseRepo.heartbeat(entry.definition.name).catch(() => {
188
+ // Lease heartbeat loss is surfaced by the final execution result.
189
+ });
190
+ }, this.heartbeatIntervalMs);
191
+ try {
192
+ const firing = new RoutineFiring({
193
+ scheduledTime,
194
+ triggerTime,
195
+ isManual,
196
+ isCatchUp,
197
+ routineRunId,
198
+ });
199
+ const firingContext = new FiringContext(firing);
200
+ schedule.markFiring(routineRunId, scheduledTime, { isManual });
201
+ await this.deps.runtimeRepo.publish(entry.definition.name, {
202
+ routineRunId,
203
+ source: isManual ? "manual" : isCatchUp ? "catch_up" : "scheduled",
204
+ startedAt: triggerTime.toISOString(),
205
+ pid: this.ownerPid,
206
+ });
207
+ await this.deps.eventLog.logTriggered({
208
+ routineName: entry.definition.name,
209
+ ownerPid: this.ownerPid,
210
+ routineRunId,
211
+ scheduledTime,
212
+ triggerTime,
213
+ isCatchUp,
214
+ skippedCount: options.skippedCount,
215
+ });
216
+ const startedAt = this.now();
217
+ const workflowResult = await this.deps.workflowExecutionService.startLoop({
218
+ workflowPath: entry.yamlPath,
219
+ cwd: options.cwd ?? path.dirname(entry.yamlPath),
220
+ signal: options.signal,
221
+ vars: {
222
+ ...firingContext.toTemplateVars(),
223
+ ...(options.vars ?? {}),
224
+ },
225
+ routineRunId,
226
+ preloadedWorkflow: entry.workflowLoadResult,
227
+ });
228
+ const completedAt = this.now();
229
+ const duration = completedAt.getTime() - startedAt.getTime();
230
+ if (workflowResult.status === "completed") {
231
+ schedule.markCompleted(routineRunId, completedAt);
232
+ await this.persistState(entry, schedule.executionState);
233
+ await this.deps.eventLog.logCompleted({
234
+ routineName: entry.definition.name,
235
+ ownerPid: this.ownerPid,
236
+ routineRunId,
237
+ duration,
238
+ });
239
+ await this.deps.eventLog.logStateUpdated({
240
+ routineName: entry.definition.name,
241
+ ownerPid: this.ownerPid,
242
+ routineRunId,
243
+ state: schedule.executionState,
244
+ });
245
+ return {
246
+ status: "completed",
247
+ scheduledTime: scheduledTime.toISOString(),
248
+ triggerTime: triggerTime.toISOString(),
249
+ routineRunId,
250
+ workflowExecutionId: workflowResult.executionId,
251
+ };
252
+ }
253
+ schedule.markFailed(routineRunId, completedAt);
254
+ await this.persistState(entry, schedule.executionState);
255
+ await this.deps.eventLog.logFailed({
256
+ routineName: entry.definition.name,
257
+ ownerPid: this.ownerPid,
258
+ routineRunId,
259
+ duration,
260
+ errorCode: "ROUTINE_EXECUTION_INTERRUPTED",
261
+ errorMessage: "Workflow execution was interrupted.",
262
+ workflowExecutionId: workflowResult.executionId,
263
+ });
264
+ await this.deps.eventLog.logStateUpdated({
265
+ routineName: entry.definition.name,
266
+ ownerPid: this.ownerPid,
267
+ routineRunId,
268
+ state: schedule.executionState,
269
+ });
270
+ return {
271
+ status: "failed",
272
+ scheduledTime: scheduledTime.toISOString(),
273
+ triggerTime: triggerTime.toISOString(),
274
+ routineRunId,
275
+ workflowExecutionId: workflowResult.executionId,
276
+ };
277
+ }
278
+ catch (error) {
279
+ const failedAt = this.now();
280
+ const errorMessage = formatError(error);
281
+ try {
282
+ schedule.markFailed(routineRunId, failedAt);
283
+ await this.persistState(entry, schedule.executionState);
284
+ await this.deps.eventLog.logFailed({
285
+ routineName: entry.definition.name,
286
+ ownerPid: this.ownerPid,
287
+ routineRunId,
288
+ duration: 0,
289
+ errorCode: "ROUTINE_EXECUTION_FAILED",
290
+ errorMessage,
291
+ workflowExecutionId: null,
292
+ });
293
+ await this.deps.eventLog.logStateUpdated({
294
+ routineName: entry.definition.name,
295
+ ownerPid: this.ownerPid,
296
+ routineRunId,
297
+ state: schedule.executionState,
298
+ });
299
+ }
300
+ catch {
301
+ // Best-effort cleanup must not mask the original dispatch failure.
302
+ }
303
+ return {
304
+ status: "failed",
305
+ scheduledTime: scheduledTime.toISOString(),
306
+ triggerTime: triggerTime.toISOString(),
307
+ routineRunId,
308
+ };
309
+ }
310
+ finally {
311
+ clearInterval(heartbeat);
312
+ await this.deps.runtimeRepo.remove(entry.definition.name);
313
+ await this.deps.leaseRepo.release(entry.definition.name);
314
+ }
315
+ }
316
+ async handleSkip(entry, scheduledTime, reason, currentState) {
317
+ const state = currentState ?? (await this.deps.stateRepo.load(entry.definition.name))?.state;
318
+ const schedule = new RoutineSchedule(entry.definition.trigger ?? null, state ?? RoutineExecutionState.create());
319
+ schedule.markSkipped(scheduledTime);
320
+ await this.persistState(entry, schedule.executionState);
321
+ await this.deps.eventLog.logSkipped({
322
+ routineName: entry.definition.name,
323
+ ownerPid: this.ownerPid,
324
+ reason,
325
+ scheduledTime,
326
+ });
327
+ await this.deps.eventLog.logStateUpdated({
328
+ routineName: entry.definition.name,
329
+ ownerPid: this.ownerPid,
330
+ routineRunId: null,
331
+ state: schedule.executionState,
332
+ });
333
+ }
334
+ async persistState(entry, state) {
335
+ await this.deps.stateRepo.save(entry.definition.name, entry.yamlPath, state);
336
+ }
337
+ }
338
+ function requireSingleTime(options) {
339
+ if (options.scheduledTime) {
340
+ return options.scheduledTime;
341
+ }
342
+ const first = options.firingTimes?.[0];
343
+ if (!first) {
344
+ throw new ValidationError("scheduledTime is required for this routine dispatch.", {
345
+ recoveryHint: "Provide a scheduledTime for scheduled or skip dispatches.",
346
+ });
347
+ }
348
+ return first;
349
+ }
350
+ function requireFiringTimes(options) {
351
+ if (options.firingTimes && options.firingTimes.length > 0) {
352
+ return [...options.firingTimes].sort((left, right) => left.getTime() - right.getTime());
353
+ }
354
+ if (options.scheduledTime) {
355
+ return [options.scheduledTime];
356
+ }
357
+ throw new ValidationError("firingTimes is required for catch-up dispatch.", {
358
+ recoveryHint: "Provide one or more firingTimes for catch-up execution.",
359
+ });
360
+ }
361
+ function formatError(error) {
362
+ if (error instanceof ValidationError) {
363
+ return error.message;
364
+ }
365
+ if (error instanceof Error) {
366
+ return error.message;
367
+ }
368
+ return String(error);
369
+ }
370
+ //# sourceMappingURL=routine-run-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routine-run-service.js","sourceRoot":"","sources":["../../src/application/routine-run-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { RoutineExecutionState } from "../domain/routine-execution-state.js";
2
+ import type { RoutineCatalogEntry } from "./routine-catalog-service.js";
3
+ export declare const DEFAULT_ROUTINE_POLL_INTERVAL_MS = 15000;
4
+ export declare const DEFAULT_ROUTINE_RESCAN_INTERVAL_MS = 60000;
5
+ export interface RoutineSchedulerDecision {
6
+ definition: RoutineCatalogEntry["definition"];
7
+ yamlPath: string;
8
+ source: "scheduled" | "catch_up" | "skip";
9
+ firingTimes: Date[];
10
+ skippedSchedules: Date[];
11
+ skippedCount: number;
12
+ }
13
+ export interface RoutineSchedulerTickResult {
14
+ now: Date;
15
+ shouldRescan: boolean;
16
+ wokeFromSleep: boolean;
17
+ decisions: RoutineSchedulerDecision[];
18
+ }
19
+ export interface RoutineSchedulerOptions {
20
+ now?: () => Date;
21
+ monotonicNow?: () => number;
22
+ pollIntervalMs?: number;
23
+ rescanIntervalMs?: number;
24
+ }
25
+ export declare class RoutineScheduler {
26
+ private readonly now;
27
+ private readonly monotonicNow;
28
+ private readonly pollIntervalMs;
29
+ private readonly rescanIntervalMs;
30
+ private lastTickAt?;
31
+ private lastRescanAt?;
32
+ constructor(options?: RoutineSchedulerOptions);
33
+ tick(catalog: Map<string, RoutineCatalogEntry>, states: Map<string, RoutineExecutionState>): RoutineSchedulerTickResult;
34
+ }
@@ -0,0 +1,82 @@
1
+ import { RoutineExecutionState } from "../domain/routine-execution-state.js";
2
+ import { RoutineSchedule } from "../domain/routine-schedule.js";
3
+ export const DEFAULT_ROUTINE_POLL_INTERVAL_MS = 15_000;
4
+ export const DEFAULT_ROUTINE_RESCAN_INTERVAL_MS = 60_000;
5
+ export class RoutineScheduler {
6
+ now;
7
+ monotonicNow;
8
+ pollIntervalMs;
9
+ rescanIntervalMs;
10
+ lastTickAt;
11
+ lastRescanAt;
12
+ constructor(options = {}) {
13
+ this.now = options.now ?? (() => new Date());
14
+ this.monotonicNow = options.monotonicNow ?? (() => performance.now());
15
+ this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_ROUTINE_POLL_INTERVAL_MS;
16
+ this.rescanIntervalMs = options.rescanIntervalMs ?? DEFAULT_ROUTINE_RESCAN_INTERVAL_MS;
17
+ }
18
+ tick(catalog, states) {
19
+ const now = this.now();
20
+ const monotonicNow = this.monotonicNow();
21
+ const wokeFromSleep = this.lastTickAt !== undefined &&
22
+ monotonicNow - this.lastTickAt > this.pollIntervalMs * 2;
23
+ const shouldRescan = this.lastRescanAt === undefined ||
24
+ monotonicNow - this.lastRescanAt >= this.rescanIntervalMs;
25
+ const decisions = [];
26
+ for (const [name, entry] of [...catalog.entries()].sort(([left], [right]) => left.localeCompare(right))) {
27
+ if (!entry.definition.trigger) {
28
+ continue;
29
+ }
30
+ const trigger = entry.definition.trigger;
31
+ const state = states.get(name) ?? RoutineExecutionState.create();
32
+ const schedule = new RoutineSchedule(trigger, state);
33
+ const evaluation = schedule.evaluateMissedFires(now);
34
+ if (evaluation.firesToExecute.length === 0 &&
35
+ evaluation.skippedSchedules.length > 0) {
36
+ decisions.push({
37
+ definition: entry.definition,
38
+ yamlPath: entry.yamlPath,
39
+ source: "skip",
40
+ firingTimes: [evaluation.skippedSchedules.at(-1)],
41
+ skippedSchedules: evaluation.skippedSchedules,
42
+ skippedCount: evaluation.skippedSchedules.length,
43
+ });
44
+ continue;
45
+ }
46
+ if (evaluation.firesToExecute.length === 0) {
47
+ continue;
48
+ }
49
+ const isCatchUp = wokeFromSleep ||
50
+ evaluation.firesToExecute.length > 1 ||
51
+ evaluation.skippedSchedules.length > 0 ||
52
+ now.getTime() - evaluation.firesToExecute[0].getTime() > this.pollIntervalMs;
53
+ decisions.push({
54
+ definition: entry.definition,
55
+ yamlPath: entry.yamlPath,
56
+ source: isCatchUp ? "catch_up" : "scheduled",
57
+ firingTimes: evaluation.firesToExecute,
58
+ skippedSchedules: evaluation.skippedSchedules,
59
+ skippedCount: evaluation.skippedSchedules.length,
60
+ });
61
+ }
62
+ decisions.sort((left, right) => {
63
+ const leftTime = left.firingTimes[0].getTime();
64
+ const rightTime = right.firingTimes[0].getTime();
65
+ if (leftTime !== rightTime) {
66
+ return leftTime - rightTime;
67
+ }
68
+ return left.definition.name.value.localeCompare(right.definition.name.value);
69
+ });
70
+ this.lastTickAt = monotonicNow;
71
+ if (shouldRescan) {
72
+ this.lastRescanAt = monotonicNow;
73
+ }
74
+ return {
75
+ now,
76
+ shouldRescan,
77
+ wokeFromSleep,
78
+ decisions,
79
+ };
80
+ }
81
+ }
82
+ //# sourceMappingURL=routine-scheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routine-scheduler.js","sourceRoot":"","sources":["../../src/application/routine-scheduler.ts"],"names":[],"mappings":"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"}