@smithers-orchestrator/scheduler 0.24.0 → 0.24.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithers-orchestrator/scheduler",
3
- "version": "0.24.0",
3
+ "version": "0.24.2",
4
4
  "description": "Pure decision engine: session, scheduler, and task state management for Smithers workflows",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -176,8 +176,8 @@
176
176
  ],
177
177
  "dependencies": {
178
178
  "effect": "^3.21.1",
179
- "@smithers-orchestrator/errors": "0.24.0",
180
- "@smithers-orchestrator/graph": "0.24.0"
179
+ "@smithers-orchestrator/errors": "0.24.2",
180
+ "@smithers-orchestrator/graph": "0.24.2"
181
181
  },
182
182
  "devDependencies": {
183
183
  "@types/bun": "latest",
@@ -1,3 +1,13 @@
1
+ import type { TaskDescriptor } from "@smithers-orchestrator/graph/TaskDescriptor";
2
+
3
+ /** A breached Aspects budget for a task that is about to be dispatched. */
4
+ export type AspectBudgetBreach = {
5
+ readonly kind: "tokens" | "latency";
6
+ readonly limit: number;
7
+ readonly current: number;
8
+ readonly onExceeded: "fail" | "warn" | "skip-remaining";
9
+ };
10
+
1
11
  export type WorkflowSessionOptions = {
2
12
  readonly runId?: string;
3
13
  readonly nowMs?: () => number;
@@ -7,4 +17,22 @@ export type WorkflowSessionOptions = {
7
17
  readonly iteration: number;
8
18
  readonly done: boolean;
9
19
  }>;
20
+ /**
21
+ * Evaluate a runnable task's Aspects budgets against the run's accumulated
22
+ * usage. Return the first breach, or `null`/`undefined` when within budget.
23
+ * Only invoked for tasks that would otherwise execute.
24
+ */
25
+ readonly evaluateAspectBudget?: (
26
+ descriptor: TaskDescriptor,
27
+ ) => AspectBudgetBreach | null | undefined;
28
+ /** Called when a task is skipped because its budget was exceeded (`skip-remaining`). */
29
+ readonly onAspectBudgetSkip?: (
30
+ descriptor: TaskDescriptor,
31
+ breach: AspectBudgetBreach,
32
+ ) => void;
33
+ /** Called when a task continues despite an exceeded budget (`warn`). */
34
+ readonly onAspectBudgetWarn?: (
35
+ descriptor: TaskDescriptor,
36
+ breach: AspectBudgetBreach,
37
+ ) => void;
10
38
  };
package/src/index.d.ts CHANGED
@@ -199,6 +199,12 @@ type WorkflowSessionService$2 = {
199
199
  readonly getCurrentGraph: () => Effect.Effect<WorkflowGraph | null>;
200
200
  };
201
201
 
202
+ type AspectBudgetBreach$1 = {
203
+ readonly kind: "tokens" | "latency";
204
+ readonly limit: number;
205
+ readonly current: number;
206
+ readonly onExceeded: "fail" | "warn" | "skip-remaining";
207
+ };
202
208
  type WorkflowSessionOptions$2 = {
203
209
  readonly runId?: string;
204
210
  readonly nowMs?: () => number;
@@ -208,6 +214,9 @@ type WorkflowSessionOptions$2 = {
208
214
  readonly iteration: number;
209
215
  readonly done: boolean;
210
216
  }>;
217
+ readonly evaluateAspectBudget?: (descriptor: TaskDescriptor$3) => AspectBudgetBreach$1 | null | undefined;
218
+ readonly onAspectBudgetSkip?: (descriptor: TaskDescriptor$3, breach: AspectBudgetBreach$1) => void;
219
+ readonly onAspectBudgetWarn?: (descriptor: TaskDescriptor$3, breach: AspectBudgetBreach$1) => void;
211
220
  };
212
221
 
213
222
  type TaskRecord$1 = {
@@ -530,6 +530,28 @@ export function makeWorkflowSession(options = {}) {
530
530
  changed = true;
531
531
  continue;
532
532
  }
533
+ const budgetBreach = options.evaluateAspectBudget?.(task);
534
+ if (budgetBreach) {
535
+ if (budgetBreach.onExceeded === "skip-remaining") {
536
+ options.onAspectBudgetSkip?.(task, budgetBreach);
537
+ state.states.set(key, "skipped");
538
+ changed = true;
539
+ continue;
540
+ }
541
+ if (budgetBreach.onExceeded === "warn") {
542
+ options.onAspectBudgetWarn?.(task, budgetBreach);
543
+ }
544
+ else {
545
+ return {
546
+ _tag: "Failed",
547
+ error: new SmithersError("ASPECT_BUDGET_EXCEEDED", `Aspects ${budgetBreach.kind} budget exceeded for task "${task.nodeId}": ${budgetBreach.current} >= ${budgetBreach.limit}`, {
548
+ kind: budgetBreach.kind,
549
+ limit: budgetBreach.limit,
550
+ current: budgetBreach.current,
551
+ }),
552
+ };
553
+ }
554
+ }
533
555
  state.states.set(key, "in-progress");
534
556
  executable.push(task);
535
557
  changed = true;