yaml-flow 2.8.0 → 3.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 (39) hide show
  1. package/README.md +168 -3
  2. package/dist/{constants-BEbO2_OK.d.ts → constants-B_ftYTTE.d.ts} +36 -6
  3. package/dist/{constants-BNjeIlZ8.d.cts → constants-CiyHX8L-.d.cts} +36 -6
  4. package/dist/continuous-event-graph/index.cjs +399 -42
  5. package/dist/continuous-event-graph/index.cjs.map +1 -1
  6. package/dist/continuous-event-graph/index.d.cts +124 -5
  7. package/dist/continuous-event-graph/index.d.ts +124 -5
  8. package/dist/continuous-event-graph/index.js +396 -43
  9. package/dist/continuous-event-graph/index.js.map +1 -1
  10. package/dist/event-graph/index.cjs +6784 -44
  11. package/dist/event-graph/index.cjs.map +1 -1
  12. package/dist/event-graph/index.d.cts +5 -5
  13. package/dist/event-graph/index.d.ts +5 -5
  14. package/dist/event-graph/index.js +6777 -43
  15. package/dist/event-graph/index.js.map +1 -1
  16. package/dist/index.cjs +946 -91
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +5 -5
  19. package/dist/index.d.ts +5 -5
  20. package/dist/index.js +938 -91
  21. package/dist/index.js.map +1 -1
  22. package/dist/inference/index.cjs +17 -8
  23. package/dist/inference/index.cjs.map +1 -1
  24. package/dist/inference/index.d.cts +2 -2
  25. package/dist/inference/index.d.ts +2 -2
  26. package/dist/inference/index.js +17 -8
  27. package/dist/inference/index.js.map +1 -1
  28. package/dist/step-machine/index.cjs +6600 -0
  29. package/dist/step-machine/index.cjs.map +1 -1
  30. package/dist/step-machine/index.d.cts +26 -1
  31. package/dist/step-machine/index.d.ts +26 -1
  32. package/dist/step-machine/index.js +6596 -1
  33. package/dist/step-machine/index.js.map +1 -1
  34. package/dist/{types-DAI_a2as.d.ts → types-BpWrH1sf.d.cts} +16 -7
  35. package/dist/{types-DAI_a2as.d.cts → types-BpWrH1sf.d.ts} +16 -7
  36. package/dist/{types-mS_pPftm.d.ts → types-BuEo3wVG.d.ts} +1 -1
  37. package/dist/{types-C2lOwquM.d.cts → types-CxJg9Jrt.d.cts} +1 -1
  38. package/package.json +1 -1
  39. package/schema/event-graph.schema.json +254 -0
package/README.md CHANGED
@@ -280,7 +280,9 @@ That's the entire integration. ~30 lines. The engine is pure; your loop owns the
280
280
  | Conditional routing | `on: { positive: [pos-result], negative: [neg-result] }` | Different outputs based on task result |
281
281
  | Failure tokens | `on_failure: [data-unavailable]` | Inject tokens on failure so downstream alternatives can activate |
282
282
  | Retry | `retry: { max_attempts: 3 }` | Auto-retry on failure (task resets to not-started) |
283
- | Repeatable tasks | `repeatable: true` or `repeatable: { max: 5 }` | Task can re-execute when its inputs refresh |
283
+ | Refresh strategy | `refreshStrategy: 'data-changed'` (default) | When a completed task should re-run: `data-changed`, `epoch-changed`, `time-based`, `manual`, `once` |
284
+ | Max executions | `maxExecutions: 5` | Cap how many times a task can execute |
285
+ | Refresh interval | `refreshInterval: 300` | Seconds between re-runs (for `time-based` strategy) |
284
286
  | Circuit breaker | `circuit_breaker: { max_executions: 10, on_break: [stop-token] }` | Inject tokens after N executions |
285
287
  | External events | `apply(state, { type: 'inject-tokens', tokens: ['user-approved'] })` | Unblock tasks waiting on external input |
286
288
  | Dynamic tasks | `apply(state, { type: 'task-creation', taskName: 'new', taskConfig: {...} })` | Add tasks at runtime |
@@ -352,11 +354,55 @@ tasks:
352
354
  revise:
353
355
  requires: [needs-revision]
354
356
  provides: [draft-answer]
355
- repeatable: { max: 3 }
357
+ refreshStrategy: epoch-changed
358
+ maxExecutions: 3
356
359
  ```
357
360
 
358
361
  The three searches run in parallel. `synthesize` waits for all three. `verify` can produce different token sets depending on its result. If rejected, `revise` picks up and feeds back into `verify` (up to 3 times). If verify itself fails, `verification-skipped` unblocks any downstream task waiting on it.
359
362
 
363
+ ### Refresh Strategies
364
+
365
+ | Strategy | Behavior |
366
+ |---|---|
367
+ | `data-changed` (default) | Re-run when upstream output content changes (tracked via `dataHash`) |
368
+ | `epoch-changed` | Re-run when upstream task execution count increases (classic "inputs refreshed") |
369
+ | `time-based` | Re-run after `refreshInterval` seconds since last completion |
370
+ | `manual` | Never auto-eligible; only via external `inject-tokens` or explicit push |
371
+ | `once` | Run once, never re-run (classic one-shot task) |
372
+
373
+ Set a board-level default in `settings.refreshStrategy`, then override per-task:
374
+
375
+ ```yaml
376
+ settings:
377
+ completion: manual
378
+ refreshStrategy: epoch-changed # board default
379
+ tasks:
380
+ fetch_prices:
381
+ provides: [price-data]
382
+ refreshStrategy: time-based # override: poll every 60s
383
+ refreshInterval: 60
384
+ compute:
385
+ requires: [price-data]
386
+ provides: [indicators]
387
+ # inherits epoch-changed from settings
388
+ alert:
389
+ requires: [indicators]
390
+ provides: [alert-sent]
391
+ refreshStrategy: data-changed # override: only if indicators actually changed
392
+ maxExecutions: 10 # safety cap
393
+ ```
394
+
395
+ Handlers can return a `dataHash` with completion events to enable content-aware freshness:
396
+
397
+ ```typescript
398
+ apply(state, {
399
+ type: 'task-completed',
400
+ taskName: 'fetch_prices',
401
+ dataHash: crypto.createHash('md5').update(JSON.stringify(data)).digest('hex'),
402
+ timestamp: new Date().toISOString(),
403
+ }, graph);
404
+ ```
405
+
360
406
  ### Pattern: Order Processing Pipeline (Step Machine)
361
407
 
362
408
  ```yaml
@@ -839,6 +885,37 @@ Use `validateGraphConfig()` for structural checks (JSON shape) and `validateGrap
839
885
 
840
886
  ---
841
887
 
888
+ ## JSON Schema Validation
889
+
890
+ Full structural validation using AJV against the JSON Schema definitions. Catches malformed configs before they reach the engine.
891
+
892
+ ```typescript
893
+ import { validateGraphSchema } from 'yaml-flow/event-graph';
894
+ import { validateFlowSchema } from 'yaml-flow/step-machine';
895
+ import { validateLiveCardSchema } from 'yaml-flow/card-compute';
896
+
897
+ // Event graph
898
+ const r1 = validateGraphSchema(config);
899
+ r1.ok; // true | false
900
+ r1.errors; // AJV error objects (when invalid)
901
+
902
+ // Step machine
903
+ const r2 = validateFlowSchema(config);
904
+
905
+ // Live cards
906
+ const r3 = validateLiveCardSchema(config);
907
+ ```
908
+
909
+ | Validator | Schema file | What it checks |
910
+ |---|---|---|
911
+ | `validateGraphSchema` | `schema/event-graph.schema.json` | Tasks, settings, refreshStrategy, retry, circuit_breaker, inference hints |
912
+ | `validateFlowSchema` | `schema/flow.schema.json` | Steps, transitions, retry, terminal states |
913
+ | `validateLiveCardSchema` | `schema/live-cards.schema.json` | Cards, sources, elements, compute, data bindings |
914
+
915
+ All validators are synchronous, pure functions. They return `{ ok: boolean, errors?: ErrorObject[] }`.
916
+
917
+ ---
918
+
842
919
  ## Continuous Event Graph
843
920
 
844
921
  A **long-lived, evolving** event-graph where both the graph config and execution state mutate over time. Ideal for dashboards, monitoring systems, and any scenario where the workflow has no fixed endpoint.
@@ -980,6 +1057,80 @@ const restored = restore(data); // → LiveGraph (validates shape)
980
1057
  | `getUnreachableNodes(live)` | Nodes that can never become eligible |
981
1058
  | `snapshot(live)` | Serialize to a JSON-safe snapshot |
982
1059
  | `restore(data)` | Restore a LiveGraph from a snapshot |
1060
+ | `applyEvents(live, events)` | Apply multiple events atomically (batch reduce) |
1061
+
1062
+ ### Reactive Graph (Push-based Execution)
1063
+
1064
+ The reactive layer adds **self-sustaining execution** on top of the pure LiveGraph. Register handlers, push one event, and the graph drives itself to completion. No daemon, no polling — each handler callback triggers the next wave.
1065
+
1066
+ ```typescript
1067
+ import { createReactiveGraph, MemoryJournal } from 'yaml-flow/continuous-event-graph';
1068
+
1069
+ // 1. Create with handlers
1070
+ const rg = createReactiveGraph(config, {
1071
+ handlers: {
1072
+ fetch: async (ctx) => { const data = await fetchAPI(); return { dataHash: hash(data) }; },
1073
+ transform: async (ctx) => { return { result: 'success' }; },
1074
+ notify: async (ctx) => { await sendSlack('done'); return {}; },
1075
+ },
1076
+ defaultTimeoutMs: 30_000,
1077
+ onDrain: (events, live, schedule) => console.log(`${events.length} events, ${schedule.eligible.length} eligible`),
1078
+ });
1079
+
1080
+ // 2. Push one event — the chain sustains itself
1081
+ rg.push({ type: 'inject-tokens', tokens: [], timestamp: new Date().toISOString() });
1082
+ // fetch runs -> completes -> transform becomes eligible -> runs -> notify -> done
1083
+
1084
+ // 3. Add nodes at runtime (with handler)
1085
+ rg.addNode('alert', { requires: ['anomaly'], provides: ['alerted'] }, async (ctx) => {
1086
+ await pageOncall(ctx.taskName);
1087
+ return {};
1088
+ });
1089
+
1090
+ // 4. Read state
1091
+ rg.getState(); // LiveGraph snapshot
1092
+ rg.getSchedule(); // current ScheduleResult
1093
+ rg.getDispatchState(); // Map<taskName, DispatchEntry>
1094
+
1095
+ // 5. Cleanup
1096
+ rg.dispose();
1097
+ ```
1098
+
1099
+ **How it works internally:**
1100
+
1101
+ ```
1102
+ push(event)
1103
+ -> applyEvent (pure state change)
1104
+ -> schedule (what's eligible?)
1105
+ -> dispatch handlers (fire-and-forget)
1106
+ -> handler completes -> appends to journal
1107
+ -> drain journal -> applyEvents (batch) -> schedule -> dispatch
1108
+ -> repeat until nothing is eligible
1109
+ ```
1110
+
1111
+ The journal serializes concurrent callbacks — multiple handlers complete simultaneously, their events batch into a single `applyEvents()` call. No race conditions.
1112
+
1113
+ **Dispatch lifecycle (reactive-layer internal, NOT in core types):**
1114
+
1115
+ | Status | Meaning |
1116
+ |---|---|
1117
+ | `initiated` | Handler callback fired, awaiting response |
1118
+ | `dispatch-failed` | Handler threw synchronously |
1119
+ | `timed-out` | No callback within deadline |
1120
+ | `retry-queued` | Will retry on next drain cycle |
1121
+ | `abandoned` | Max dispatch retries exceeded -> pushes `task-failed` to core |
1122
+
1123
+ **Options:**
1124
+
1125
+ | Option | Default | Description |
1126
+ |---|---|---|
1127
+ | `handlers` | (required) | `Record<string, TaskHandler>` |
1128
+ | `maxDispatchRetries` | `3` | Times to retry invoking a handler |
1129
+ | `defaultTimeoutMs` | `30000` | Handler callback deadline (0 = no timeout) |
1130
+ | `journal` | `MemoryJournal` | Event log adapter (`MemoryJournal` or `FileJournal`) |
1131
+ | `onDrain` | — | Called after each drain cycle (observability) |
1132
+ | `onDispatchFailed` | — | Called when handler invocation fails |
1133
+ | `onAbandoned` | — | Called when task dispatch is abandoned |
983
1134
 
984
1135
  ---
985
1136
 
@@ -1234,15 +1385,25 @@ import { resolveVariables, resolveConfigTemplates } from 'yaml-flow/config';
1234
1385
 
1235
1386
  // Continuous Event Graph (long-lived evolving workflows)
1236
1387
  import {
1237
- createLiveGraph, applyEvent, addNode, removeNode,
1388
+ createLiveGraph, applyEvent, applyEvents, addNode, removeNode,
1238
1389
  addRequires, removeRequires, addProvides, removeProvides,
1239
1390
  injectTokens, drainTokens, schedule, inspect,
1240
1391
  resetNode, disableNode, enableNode, getNode,
1241
1392
  snapshot, restore,
1242
1393
  getUnreachableTokens, getUnreachableNodes,
1243
1394
  getUpstream, getDownstream,
1395
+ createReactiveGraph, MemoryJournal, FileJournal,
1396
+ } from 'yaml-flow/continuous-event-graph';
1397
+ import type {
1398
+ ReactiveGraph, TaskHandler, TaskHandlerContext, TaskHandlerResult,
1399
+ DispatchEntry, Journal,
1244
1400
  } from 'yaml-flow/continuous-event-graph';
1245
1401
 
1402
+ // JSON Schema Validators
1403
+ import { validateGraphSchema } from 'yaml-flow/event-graph';
1404
+ import { validateFlowSchema } from 'yaml-flow/step-machine';
1405
+ import { validateLiveCardSchema } from 'yaml-flow/card-compute';
1406
+
1246
1407
  // LLM Inference (AI-assisted completion detection)
1247
1408
  import {
1248
1409
  buildInferencePrompt, inferCompletions, applyInferences, inferAndApply,
@@ -1321,6 +1482,10 @@ See the [examples/](./examples) directory:
1321
1482
  | [URL Pipeline](./examples/graph-of-graphs/url-processing-pipeline.ts) | Graph-of-Graphs | Outer event-graph → batch × inner event-graph per item |
1322
1483
  | [Multi-Stage ETL](./examples/graph-of-graphs/multi-stage-etl.ts) | Graph-of-Graphs | Mixed modes: event-graph outer → step-machine + event-graph subs |
1323
1484
  | [Stock Dashboard](./examples/continuous-event-graph/stock-dashboard.ts) | Continuous Event Graph | Runtime mutations, token drain, upstream/downstream, snapshot |
1485
+ | [Reactive Pipeline](./examples/continuous-event-graph/reactive-pipeline.ts) | Reactive Graph | Self-driving ETL — push once, 4 tasks complete automatically |
1486
+ | [Reactive Monitoring](./examples/continuous-event-graph/reactive-monitoring.ts) | Reactive Graph | Conditional routing, on_failure escalation, runtime addNode |
1487
+ | [Executor Pipeline](./examples/event-graph/executor-pipeline.ts) | Event Graph (library) | You-drive-the-loop ETL with random async delays |
1488
+ | [Executor Diamond](./examples/event-graph/executor-diamond.ts) | Event Graph (library) | Parallel fan-out/fan-in diamond DAG with async executors |
1324
1489
  | [Azure Deployment](./examples/inference/azure-deployment.ts) | Inference | LLM analyzes deployment logs, auto-completes checkpoints |
1325
1490
  | [Data Pipeline](./examples/inference/data-pipeline.ts) | Inference | Iterative inference — evidence arrives in waves |
1326
1491
  | [Pluggable Adapters](./examples/inference/pluggable-adapters.ts) | Inference | OpenAI, Anthropic, Azure, CLI, HTTP adapter factories |
@@ -1,4 +1,4 @@
1
- import { G as GraphConfig, c as ExecutionState, S as SchedulerResult, e as GraphEvent, T as TaskConfig, l as TaskState, g as StuckDetection, C as CompletionStrategy, a as ConflictStrategy, b as ExecutionMode, d as ExecutionStatus, m as TaskStatus } from './types-DAI_a2as.js';
1
+ import { G as GraphConfig, c as ExecutionState, S as SchedulerResult, e as GraphEvent, T as TaskConfig, l as TaskState, R as RefreshStrategy, g as StuckDetection, C as CompletionStrategy, a as ConflictStrategy, b as ExecutionMode, d as ExecutionStatus, m as TaskStatus } from './types-BpWrH1sf.js';
2
2
  import { e as StepFlowConfig } from './types-FZ_eyErS.js';
3
3
 
4
4
  /**
@@ -15,7 +15,7 @@ import { e as StepFlowConfig } from './types-FZ_eyErS.js';
15
15
  declare function next(graph: GraphConfig, state: ExecutionState): SchedulerResult;
16
16
  /**
17
17
  * Get candidate tasks whose dependencies are all met.
18
- * Handles repeatable tasks and circuit breakers.
18
+ * Uses refreshStrategy to determine re-execution eligibility.
19
19
  * Pure function.
20
20
  */
21
21
  declare function getCandidateTasks(graph: GraphConfig, state: ExecutionState): string[];
@@ -57,11 +57,16 @@ declare function hasTask(graph: GraphConfig, taskName: string): boolean;
57
57
  declare function isNonActiveTask(taskState: TaskState | undefined): boolean;
58
58
  declare function isTaskCompleted(taskState: TaskState | undefined): boolean;
59
59
  declare function isTaskRunning(taskState: TaskState | undefined): boolean;
60
- declare function isRepeatableTask(taskConfig: TaskConfig): boolean;
61
- declare function getRepeatableMax(taskConfig: TaskConfig): number | undefined;
60
+ declare function getRefreshStrategy(taskConfig: TaskConfig, graphSettings?: {
61
+ refreshStrategy?: RefreshStrategy;
62
+ }): RefreshStrategy;
63
+ declare function isRerunnable(taskConfig: TaskConfig, graphSettings?: {
64
+ refreshStrategy?: RefreshStrategy;
65
+ }): boolean;
66
+ declare function getMaxExecutions(taskConfig: TaskConfig): number | undefined;
62
67
  /**
63
68
  * Dynamically compute available outputs from all completed tasks.
64
- * For repeatable tasks, outputs are versioned by epoch.
69
+ * Tasks with strategies other than 'once' may have completed and reset.
65
70
  * Pure function.
66
71
  */
67
72
  declare function computeAvailableOutputs(graph: GraphConfig, taskStates: Record<string, TaskState>): string[];
@@ -303,6 +308,31 @@ interface GraphValidationResult {
303
308
  */
304
309
  declare function validateGraph(graph: GraphConfig): GraphValidationResult;
305
310
 
311
+ /**
312
+ * schema-validator — Full JSON Schema validation for EventGraph configs.
313
+ *
314
+ * Uses AJV to validate against the published event-graph.schema.json.
315
+ * For a lightweight sync check without AJV, use `validateGraphConfig()` instead.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * import { validateGraphSchema } from 'yaml-flow/event-graph';
320
+ *
321
+ * const result = validateGraphSchema(config);
322
+ * if (!result.ok) console.error(result.errors);
323
+ * ```
324
+ */
325
+ interface SchemaValidationResult {
326
+ ok: boolean;
327
+ errors: string[];
328
+ }
329
+ /**
330
+ * Validate an event-graph config against the full event-graph.schema.json (draft-07).
331
+ *
332
+ * Requires `ajv` and `ajv-formats` to be installed.
333
+ */
334
+ declare function validateGraphSchema(config: unknown): SchemaValidationResult;
335
+
306
336
  /**
307
337
  * Event Graph — Constants
308
338
  */
@@ -319,4 +349,4 @@ declare const DEFAULTS: {
319
349
  readonly MAX_ITERATIONS: 1000;
320
350
  };
321
351
 
322
- export { isTaskCompleted as A, isTaskRunning as B, COMPLETION_STRATEGIES as C, DEFAULTS as D, EXECUTION_MODES as E, loadGraphConfig as F, type GraphIssue as G, next as H, type IssueSeverity as I, planExecution as J, validateGraph as K, validateGraphConfig as L, type MermaidOptions as M, addKeyToProvides as N, addKeyToRequires as O, getRepeatableMax as P, groupTasksByProvides as Q, hasOutputConflict as R, removeKeyFromProvides as S, TASK_STATUS as T, removeKeyFromRequires as U, CONFLICT_STRATEGIES as a, type CompletionResult as b, EXECUTION_STATUS as c, type ExecutionPlan as d, type ExportOptions as e, type GraphValidationResult as f, addDynamicTask as g, apply as h, applyAll as i, computeAvailableOutputs as j, createDefaultTaskState as k, createInitialExecutionState as l, detectStuckState as m, exportGraphConfig as n, exportGraphConfigToFile as o, flowToMermaid as p, getAllTasks as q, getCandidateTasks as r, getProvides as s, getRequires as t, getTask as u, graphToMermaid as v, hasTask as w, isExecutionComplete as x, isNonActiveTask as y, isRepeatableTask as z };
352
+ export { isNonActiveTask as A, isRerunnable as B, COMPLETION_STRATEGIES as C, DEFAULTS as D, EXECUTION_MODES as E, isTaskCompleted as F, type GraphIssue as G, isTaskRunning as H, type IssueSeverity as I, loadGraphConfig as J, next as K, planExecution as L, type MermaidOptions as M, validateGraph as N, validateGraphConfig as O, validateGraphSchema as P, addKeyToProvides as Q, addKeyToRequires as R, groupTasksByProvides as S, TASK_STATUS as T, hasOutputConflict as U, removeKeyFromProvides as V, removeKeyFromRequires as W, CONFLICT_STRATEGIES as a, type CompletionResult as b, EXECUTION_STATUS as c, type ExecutionPlan as d, type ExportOptions as e, type GraphValidationResult as f, addDynamicTask as g, apply as h, applyAll as i, computeAvailableOutputs as j, createDefaultTaskState as k, createInitialExecutionState as l, detectStuckState as m, exportGraphConfig as n, exportGraphConfigToFile as o, flowToMermaid as p, getAllTasks as q, getCandidateTasks as r, getMaxExecutions as s, getProvides as t, getRefreshStrategy as u, getRequires as v, getTask as w, graphToMermaid as x, hasTask as y, isExecutionComplete as z };
@@ -1,4 +1,4 @@
1
- import { G as GraphConfig, c as ExecutionState, S as SchedulerResult, e as GraphEvent, T as TaskConfig, l as TaskState, g as StuckDetection, C as CompletionStrategy, a as ConflictStrategy, b as ExecutionMode, d as ExecutionStatus, m as TaskStatus } from './types-DAI_a2as.cjs';
1
+ import { G as GraphConfig, c as ExecutionState, S as SchedulerResult, e as GraphEvent, T as TaskConfig, l as TaskState, R as RefreshStrategy, g as StuckDetection, C as CompletionStrategy, a as ConflictStrategy, b as ExecutionMode, d as ExecutionStatus, m as TaskStatus } from './types-BpWrH1sf.cjs';
2
2
  import { e as StepFlowConfig } from './types-FZ_eyErS.cjs';
3
3
 
4
4
  /**
@@ -15,7 +15,7 @@ import { e as StepFlowConfig } from './types-FZ_eyErS.cjs';
15
15
  declare function next(graph: GraphConfig, state: ExecutionState): SchedulerResult;
16
16
  /**
17
17
  * Get candidate tasks whose dependencies are all met.
18
- * Handles repeatable tasks and circuit breakers.
18
+ * Uses refreshStrategy to determine re-execution eligibility.
19
19
  * Pure function.
20
20
  */
21
21
  declare function getCandidateTasks(graph: GraphConfig, state: ExecutionState): string[];
@@ -57,11 +57,16 @@ declare function hasTask(graph: GraphConfig, taskName: string): boolean;
57
57
  declare function isNonActiveTask(taskState: TaskState | undefined): boolean;
58
58
  declare function isTaskCompleted(taskState: TaskState | undefined): boolean;
59
59
  declare function isTaskRunning(taskState: TaskState | undefined): boolean;
60
- declare function isRepeatableTask(taskConfig: TaskConfig): boolean;
61
- declare function getRepeatableMax(taskConfig: TaskConfig): number | undefined;
60
+ declare function getRefreshStrategy(taskConfig: TaskConfig, graphSettings?: {
61
+ refreshStrategy?: RefreshStrategy;
62
+ }): RefreshStrategy;
63
+ declare function isRerunnable(taskConfig: TaskConfig, graphSettings?: {
64
+ refreshStrategy?: RefreshStrategy;
65
+ }): boolean;
66
+ declare function getMaxExecutions(taskConfig: TaskConfig): number | undefined;
62
67
  /**
63
68
  * Dynamically compute available outputs from all completed tasks.
64
- * For repeatable tasks, outputs are versioned by epoch.
69
+ * Tasks with strategies other than 'once' may have completed and reset.
65
70
  * Pure function.
66
71
  */
67
72
  declare function computeAvailableOutputs(graph: GraphConfig, taskStates: Record<string, TaskState>): string[];
@@ -303,6 +308,31 @@ interface GraphValidationResult {
303
308
  */
304
309
  declare function validateGraph(graph: GraphConfig): GraphValidationResult;
305
310
 
311
+ /**
312
+ * schema-validator — Full JSON Schema validation for EventGraph configs.
313
+ *
314
+ * Uses AJV to validate against the published event-graph.schema.json.
315
+ * For a lightweight sync check without AJV, use `validateGraphConfig()` instead.
316
+ *
317
+ * @example
318
+ * ```typescript
319
+ * import { validateGraphSchema } from 'yaml-flow/event-graph';
320
+ *
321
+ * const result = validateGraphSchema(config);
322
+ * if (!result.ok) console.error(result.errors);
323
+ * ```
324
+ */
325
+ interface SchemaValidationResult {
326
+ ok: boolean;
327
+ errors: string[];
328
+ }
329
+ /**
330
+ * Validate an event-graph config against the full event-graph.schema.json (draft-07).
331
+ *
332
+ * Requires `ajv` and `ajv-formats` to be installed.
333
+ */
334
+ declare function validateGraphSchema(config: unknown): SchemaValidationResult;
335
+
306
336
  /**
307
337
  * Event Graph — Constants
308
338
  */
@@ -319,4 +349,4 @@ declare const DEFAULTS: {
319
349
  readonly MAX_ITERATIONS: 1000;
320
350
  };
321
351
 
322
- export { isTaskCompleted as A, isTaskRunning as B, COMPLETION_STRATEGIES as C, DEFAULTS as D, EXECUTION_MODES as E, loadGraphConfig as F, type GraphIssue as G, next as H, type IssueSeverity as I, planExecution as J, validateGraph as K, validateGraphConfig as L, type MermaidOptions as M, addKeyToProvides as N, addKeyToRequires as O, getRepeatableMax as P, groupTasksByProvides as Q, hasOutputConflict as R, removeKeyFromProvides as S, TASK_STATUS as T, removeKeyFromRequires as U, CONFLICT_STRATEGIES as a, type CompletionResult as b, EXECUTION_STATUS as c, type ExecutionPlan as d, type ExportOptions as e, type GraphValidationResult as f, addDynamicTask as g, apply as h, applyAll as i, computeAvailableOutputs as j, createDefaultTaskState as k, createInitialExecutionState as l, detectStuckState as m, exportGraphConfig as n, exportGraphConfigToFile as o, flowToMermaid as p, getAllTasks as q, getCandidateTasks as r, getProvides as s, getRequires as t, getTask as u, graphToMermaid as v, hasTask as w, isExecutionComplete as x, isNonActiveTask as y, isRepeatableTask as z };
352
+ export { isNonActiveTask as A, isRerunnable as B, COMPLETION_STRATEGIES as C, DEFAULTS as D, EXECUTION_MODES as E, isTaskCompleted as F, type GraphIssue as G, isTaskRunning as H, type IssueSeverity as I, loadGraphConfig as J, next as K, planExecution as L, type MermaidOptions as M, validateGraph as N, validateGraphConfig as O, validateGraphSchema as P, addKeyToProvides as Q, addKeyToRequires as R, groupTasksByProvides as S, TASK_STATUS as T, hasOutputConflict as U, removeKeyFromProvides as V, removeKeyFromRequires as W, CONFLICT_STRATEGIES as a, type CompletionResult as b, EXECUTION_STATUS as c, type ExecutionPlan as d, type ExportOptions as e, type GraphValidationResult as f, addDynamicTask as g, apply as h, applyAll as i, computeAvailableOutputs as j, createDefaultTaskState as k, createInitialExecutionState as l, detectStuckState as m, exportGraphConfig as n, exportGraphConfigToFile as o, flowToMermaid as p, getAllTasks as q, getCandidateTasks as r, getMaxExecutions as s, getProvides as t, getRefreshStrategy as u, getRequires as v, getTask as w, graphToMermaid as x, hasTask as y, isExecutionComplete as z };