@smithers-orchestrator/scheduler 0.16.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +190 -0
  3. package/src/ApprovalResolution.ts +7 -0
  4. package/src/CachePolicy.ts +8 -0
  5. package/src/ContinuationRequest.ts +3 -0
  6. package/src/ContinueAsNewTransition.ts +9 -0
  7. package/src/EngineDecision.ts +19 -0
  8. package/src/PlanNode.ts +32 -0
  9. package/src/RalphMeta.ts +7 -0
  10. package/src/RalphState.ts +4 -0
  11. package/src/RalphStateMap.ts +3 -0
  12. package/src/ReadonlyTaskStateMap.ts +3 -0
  13. package/src/RenderContext.ts +14 -0
  14. package/src/RetryPolicy.ts +6 -0
  15. package/src/RetryWaitMap.ts +1 -0
  16. package/src/RunResult.ts +15 -0
  17. package/src/ScheduleResult.ts +15 -0
  18. package/src/ScheduleSnapshot.ts +8 -0
  19. package/src/Scheduler.js +28 -0
  20. package/src/SchedulerLive.js +8 -0
  21. package/src/SmithersWorkflowOptions.ts +43 -0
  22. package/src/TaskFailure.ts +5 -0
  23. package/src/TaskOutput.ts +9 -0
  24. package/src/TaskRecord.ts +10 -0
  25. package/src/TaskState.ts +10 -0
  26. package/src/TaskStateMap.ts +3 -0
  27. package/src/TokenUsage.ts +9 -0
  28. package/src/WaitReason.ts +8 -0
  29. package/src/WorkflowSession.js +10 -0
  30. package/src/WorkflowSessionLive.js +6 -0
  31. package/src/WorkflowSessionOptions.ts +10 -0
  32. package/src/WorkflowSessionService.ts +52 -0
  33. package/src/buildPlanTree.js +273 -0
  34. package/src/buildStateKey.js +8 -0
  35. package/src/cloneTaskStateMap.js +10 -0
  36. package/src/computeRetryDelayMs.js +14 -0
  37. package/src/index.d.ts +437 -0
  38. package/src/index.js +53 -0
  39. package/src/isTerminalState.js +15 -0
  40. package/src/makeWorkflowSession.js +723 -0
  41. package/src/nowMs.js +6 -0
  42. package/src/parseStateKey.js +15 -0
  43. package/src/retryPolicyToSchedule.js +26 -0
  44. package/src/retryScheduleDelayMs.js +23 -0
  45. package/src/scheduleTasks.js +330 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 William Cory
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,190 @@
1
+ {
2
+ "name": "@smithers-orchestrator/scheduler",
3
+ "version": "0.16.0",
4
+ "description": "Pure decision engine: session, scheduler, and task state management for Smithers workflows",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.d.ts",
10
+ "import": "./src/index.js",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./TaskState": {
14
+ "types": "./src/index.d.ts"
15
+ },
16
+ "./TaskStateMap": {
17
+ "types": "./src/index.d.ts"
18
+ },
19
+ "./ReadonlyTaskStateMap": {
20
+ "types": "./src/index.d.ts"
21
+ },
22
+ "./TaskRecord": {
23
+ "types": "./src/index.d.ts"
24
+ },
25
+ "./buildStateKey": {
26
+ "types": "./src/index.d.ts",
27
+ "import": "./src/buildStateKey.js",
28
+ "default": "./src/buildStateKey.js"
29
+ },
30
+ "./parseStateKey": {
31
+ "types": "./src/index.d.ts",
32
+ "import": "./src/parseStateKey.js",
33
+ "default": "./src/parseStateKey.js"
34
+ },
35
+ "./cloneTaskStateMap": {
36
+ "types": "./src/index.d.ts",
37
+ "import": "./src/cloneTaskStateMap.js",
38
+ "default": "./src/cloneTaskStateMap.js"
39
+ },
40
+ "./isTerminalState": {
41
+ "types": "./src/index.d.ts",
42
+ "import": "./src/isTerminalState.js",
43
+ "default": "./src/isTerminalState.js"
44
+ },
45
+ "./PlanNode": {
46
+ "types": "./src/index.d.ts"
47
+ },
48
+ "./ScheduleResult": {
49
+ "types": "./src/index.d.ts"
50
+ },
51
+ "./RalphMeta": {
52
+ "types": "./src/index.d.ts"
53
+ },
54
+ "./ContinuationRequest": {
55
+ "types": "./src/index.d.ts"
56
+ },
57
+ "./RalphState": {
58
+ "types": "./src/index.d.ts"
59
+ },
60
+ "./RalphStateMap": {
61
+ "types": "./src/index.d.ts"
62
+ },
63
+ "./RetryWaitMap": {
64
+ "types": "./src/index.d.ts"
65
+ },
66
+ "./ScheduleSnapshot": {
67
+ "types": "./src/index.d.ts"
68
+ },
69
+ "./Scheduler": {
70
+ "types": "./src/index.d.ts",
71
+ "import": "./src/Scheduler.js",
72
+ "default": "./src/Scheduler.js"
73
+ },
74
+ "./SchedulerLive": {
75
+ "types": "./src/index.d.ts",
76
+ "import": "./src/SchedulerLive.js",
77
+ "default": "./src/SchedulerLive.js"
78
+ },
79
+ "./buildPlanTree": {
80
+ "types": "./src/index.d.ts",
81
+ "import": "./src/buildPlanTree.js",
82
+ "default": "./src/buildPlanTree.js"
83
+ },
84
+ "./scheduleTasks": {
85
+ "types": "./src/index.d.ts",
86
+ "import": "./src/scheduleTasks.js",
87
+ "default": "./src/scheduleTasks.js"
88
+ },
89
+ "./WorkflowSession": {
90
+ "types": "./src/index.d.ts",
91
+ "import": "./src/WorkflowSession.js",
92
+ "default": "./src/WorkflowSession.js"
93
+ },
94
+ "./WorkflowSessionService": {
95
+ "types": "./src/index.d.ts",
96
+ "import": "./src/WorkflowSessionService.js",
97
+ "default": "./src/WorkflowSessionService.js"
98
+ },
99
+ "./WorkflowSessionOptions": {
100
+ "types": "./src/index.d.ts"
101
+ },
102
+ "./makeWorkflowSession": {
103
+ "types": "./src/index.d.ts",
104
+ "import": "./src/makeWorkflowSession.js",
105
+ "default": "./src/makeWorkflowSession.js"
106
+ },
107
+ "./WorkflowSessionLive": {
108
+ "types": "./src/index.d.ts",
109
+ "import": "./src/WorkflowSessionLive.js",
110
+ "default": "./src/WorkflowSessionLive.js"
111
+ },
112
+ "./EngineDecision": {
113
+ "types": "./src/index.d.ts"
114
+ },
115
+ "./RenderContext": {
116
+ "types": "./src/index.d.ts"
117
+ },
118
+ "./WaitReason": {
119
+ "types": "./src/index.d.ts"
120
+ },
121
+ "./RunResult": {
122
+ "types": "./src/index.d.ts"
123
+ },
124
+ "./TaskOutput": {
125
+ "types": "./src/index.d.ts"
126
+ },
127
+ "./TaskFailure": {
128
+ "types": "./src/index.d.ts"
129
+ },
130
+ "./TokenUsage": {
131
+ "types": "./src/index.d.ts"
132
+ },
133
+ "./RetryPolicy": {
134
+ "types": "./src/index.d.ts"
135
+ },
136
+ "./CachePolicy": {
137
+ "types": "./src/index.d.ts"
138
+ },
139
+ "./SmithersWorkflowOptions": {
140
+ "types": "./src/index.d.ts"
141
+ },
142
+ "./ApprovalResolution": {
143
+ "types": "./src/index.d.ts"
144
+ },
145
+ "./ContinueAsNewTransition": {
146
+ "types": "./src/index.d.ts"
147
+ },
148
+ "./retryPolicyToSchedule": {
149
+ "types": "./src/index.d.ts",
150
+ "import": "./src/retryPolicyToSchedule.js",
151
+ "default": "./src/retryPolicyToSchedule.js"
152
+ },
153
+ "./retryScheduleDelayMs": {
154
+ "types": "./src/index.d.ts",
155
+ "import": "./src/retryScheduleDelayMs.js",
156
+ "default": "./src/retryScheduleDelayMs.js"
157
+ },
158
+ "./computeRetryDelayMs": {
159
+ "types": "./src/index.d.ts",
160
+ "import": "./src/computeRetryDelayMs.js",
161
+ "default": "./src/computeRetryDelayMs.js"
162
+ },
163
+ "./nowMs": {
164
+ "types": "./src/index.d.ts",
165
+ "import": "./src/nowMs.js",
166
+ "default": "./src/nowMs.js"
167
+ },
168
+ "./*": {
169
+ "types": "./src/index.d.ts",
170
+ "import": "./src/*.js",
171
+ "default": "./src/*.js"
172
+ }
173
+ },
174
+ "files": [
175
+ "src/"
176
+ ],
177
+ "dependencies": {
178
+ "effect": "^3.21.0",
179
+ "@smithers-orchestrator/errors": "0.16.0",
180
+ "@smithers-orchestrator/graph": "0.16.0"
181
+ },
182
+ "devDependencies": {
183
+ "@types/bun": "latest",
184
+ "typescript": "~5.9.3"
185
+ },
186
+ "scripts": {
187
+ "build": "tsup --dts-only",
188
+ "typecheck": "tsc -p tsconfig.json --noEmit"
189
+ }
190
+ }
@@ -0,0 +1,7 @@
1
+ export type ApprovalResolution = {
2
+ readonly approved: boolean;
3
+ readonly note?: string;
4
+ readonly decidedBy?: string;
5
+ readonly optionKey?: string;
6
+ readonly payload?: unknown;
7
+ };
@@ -0,0 +1,8 @@
1
+ export type CachePolicy<Ctx = any> = {
2
+ by?: (ctx: Ctx) => unknown;
3
+ version?: string;
4
+ key?: string;
5
+ ttlMs?: number;
6
+ scope?: "run" | "workflow" | "global";
7
+ [key: string]: unknown;
8
+ };
@@ -0,0 +1,3 @@
1
+ export type ContinuationRequest = {
2
+ readonly stateJson?: string;
3
+ };
@@ -0,0 +1,9 @@
1
+ export type ContinueAsNewTransition = {
2
+ readonly reason: "explicit" | "loop-threshold" | "driver";
3
+ readonly iteration?: number;
4
+ readonly statePayload?: unknown;
5
+ readonly stateJson?: string;
6
+ readonly newRunId?: string;
7
+ readonly carriedStateBytes?: number;
8
+ readonly ancestryDepth?: number;
9
+ };
@@ -0,0 +1,19 @@
1
+ import type { TaskDescriptor } from "@smithers-orchestrator/graph";
2
+ import type { ContinueAsNewTransition } from "./ContinueAsNewTransition.ts";
3
+ import type { RenderContext } from "./RenderContext.ts";
4
+ import type { RunResult } from "./RunResult.ts";
5
+ import type { WaitReason } from "./WaitReason.ts";
6
+
7
+ export type EngineDecision =
8
+ | { readonly _tag: "Execute"; readonly tasks: readonly TaskDescriptor[] }
9
+ | { readonly _tag: "ReRender"; readonly context: RenderContext }
10
+ | {
11
+ readonly _tag: "Wait";
12
+ readonly reason: WaitReason;
13
+ }
14
+ | {
15
+ readonly _tag: "ContinueAsNew";
16
+ readonly transition: ContinueAsNewTransition;
17
+ }
18
+ | { readonly _tag: "Finished"; readonly result: RunResult }
19
+ | { readonly _tag: "Failed"; readonly error: unknown };
@@ -0,0 +1,32 @@
1
+ export type PlanNode =
2
+ | { readonly kind: "task"; readonly nodeId: string }
3
+ | { readonly kind: "sequence"; readonly children: readonly PlanNode[] }
4
+ | { readonly kind: "parallel"; readonly children: readonly PlanNode[] }
5
+ | {
6
+ readonly kind: "ralph";
7
+ readonly id: string;
8
+ readonly children: readonly PlanNode[];
9
+ readonly until: boolean;
10
+ readonly maxIterations: number;
11
+ readonly onMaxReached: "fail" | "return-last";
12
+ readonly continueAsNewEvery?: number;
13
+ }
14
+ | {
15
+ readonly kind: "continue-as-new";
16
+ readonly stateJson?: string;
17
+ }
18
+ | { readonly kind: "group"; readonly children: readonly PlanNode[] }
19
+ | {
20
+ readonly kind: "saga";
21
+ readonly id: string;
22
+ readonly actionChildren: readonly PlanNode[];
23
+ readonly compensationChildren: readonly PlanNode[];
24
+ readonly onFailure: "compensate" | "compensate-and-fail" | "fail";
25
+ }
26
+ | {
27
+ readonly kind: "try-catch-finally";
28
+ readonly id: string;
29
+ readonly tryChildren: readonly PlanNode[];
30
+ readonly catchChildren: readonly PlanNode[];
31
+ readonly finallyChildren: readonly PlanNode[];
32
+ };
@@ -0,0 +1,7 @@
1
+ export type RalphMeta = {
2
+ readonly id: string;
3
+ readonly until: boolean;
4
+ readonly maxIterations: number;
5
+ readonly onMaxReached: "fail" | "return-last";
6
+ readonly continueAsNewEvery?: number;
7
+ };
@@ -0,0 +1,4 @@
1
+ export type RalphState = {
2
+ readonly iteration: number;
3
+ readonly done: boolean;
4
+ };
@@ -0,0 +1,3 @@
1
+ import type { RalphState } from "./RalphState.ts";
2
+
3
+ export type RalphStateMap = Map<string, RalphState>;
@@ -0,0 +1,3 @@
1
+ import type { TaskState } from "./TaskState.ts";
2
+
3
+ export type ReadonlyTaskStateMap = ReadonlyMap<string, TaskState>;
@@ -0,0 +1,14 @@
1
+ import type { WorkflowGraph } from "@smithers-orchestrator/graph";
2
+ import type { TaskOutput } from "./TaskOutput.ts";
3
+
4
+ export type RenderContext = {
5
+ readonly runId: string;
6
+ readonly graph?: WorkflowGraph | null;
7
+ readonly iteration?: number;
8
+ readonly iterations?: Record<string, number> | ReadonlyMap<string, number>;
9
+ readonly input?: unknown;
10
+ readonly outputs?: Record<string, unknown[]> | ReadonlyMap<string, TaskOutput>;
11
+ readonly auth?: unknown;
12
+ readonly taskStates?: unknown;
13
+ readonly ralphIterations?: ReadonlyMap<string, number>;
14
+ };
@@ -0,0 +1,6 @@
1
+ export type RetryBackoff = "fixed" | "linear" | "exponential";
2
+
3
+ export type RetryPolicy = {
4
+ backoff?: RetryBackoff;
5
+ initialDelayMs?: number;
6
+ };
@@ -0,0 +1 @@
1
+ export type RetryWaitMap = Map<string, number>;
@@ -0,0 +1,15 @@
1
+ export type RunResult = {
2
+ readonly runId: string;
3
+ readonly status:
4
+ | "running"
5
+ | "finished"
6
+ | "failed"
7
+ | "cancelled"
8
+ | "continued"
9
+ | "waiting-approval"
10
+ | "waiting-event"
11
+ | "waiting-timer";
12
+ readonly output?: unknown;
13
+ readonly error?: unknown;
14
+ readonly nextRunId?: string;
15
+ };
@@ -0,0 +1,15 @@
1
+ import type { TaskDescriptor } from "@smithers-orchestrator/graph";
2
+ import type { ContinuationRequest } from "./ContinuationRequest.ts";
3
+ import type { RalphMeta } from "./RalphMeta.ts";
4
+
5
+ export type ScheduleResult = {
6
+ readonly runnable: readonly TaskDescriptor[];
7
+ readonly pendingExists: boolean;
8
+ readonly waitingApprovalExists: boolean;
9
+ readonly waitingEventExists: boolean;
10
+ readonly waitingTimerExists: boolean;
11
+ readonly readyRalphs: readonly RalphMeta[];
12
+ readonly continuation?: ContinuationRequest;
13
+ readonly nextRetryAtMs?: number;
14
+ readonly fatalError?: string;
15
+ };
@@ -0,0 +1,8 @@
1
+ import type { PlanNode } from "./PlanNode.ts";
2
+ import type { ScheduleResult } from "./ScheduleResult.ts";
3
+
4
+ export type ScheduleSnapshot = {
5
+ readonly plan: PlanNode | null;
6
+ readonly result: ScheduleResult;
7
+ readonly computedAtMs: number;
8
+ };
@@ -0,0 +1,28 @@
1
+ import { Context } from "effect";
2
+ /** @typedef {import("@smithers-orchestrator/graph").TaskDescriptor} TaskDescriptor */
3
+ /** @typedef {import("./TaskStateMap.ts").TaskStateMap} TaskStateMap */
4
+ /** @typedef {import("./PlanNode.ts").PlanNode} PlanNode */
5
+ /** @typedef {import("./RalphStateMap.ts").RalphStateMap} RalphStateMap */
6
+ /** @typedef {import("./RetryWaitMap.ts").RetryWaitMap} RetryWaitMap */
7
+ /** @typedef {import("./ScheduleResult.ts").ScheduleResult} ScheduleResult */
8
+
9
+ /**
10
+ * @typedef {{
11
+ * readonly schedule: (
12
+ * plan: PlanNode | null,
13
+ * states: TaskStateMap,
14
+ * descriptors: Map<string, TaskDescriptor>,
15
+ * ralphState: RalphStateMap,
16
+ * retryWait: RetryWaitMap,
17
+ * nowMs: number,
18
+ * ) => import("effect").Effect.Effect<ScheduleResult>
19
+ * }} SchedulerService
20
+ */
21
+
22
+ const SchedulerBase =
23
+ /** @type {Context.TagClass<Scheduler, "Scheduler", SchedulerService>} */ (
24
+ /** @type {unknown} */ (Context.Tag("Scheduler")())
25
+ );
26
+
27
+ export class Scheduler extends SchedulerBase {
28
+ }
@@ -0,0 +1,8 @@
1
+ import { Effect, Layer } from "effect";
2
+ import { Scheduler } from "./Scheduler.js";
3
+ import { scheduleTasks } from "./scheduleTasks.js";
4
+
5
+ /** @type {Layer.Layer<Scheduler, never, never>} */
6
+ export const SchedulerLive = Layer.succeed(Scheduler, {
7
+ schedule: (plan, states, descriptors, ralphState, retryWait, nowMs) => Effect.sync(() => scheduleTasks(plan, states, descriptors, ralphState, retryWait, nowMs)),
8
+ });
@@ -0,0 +1,43 @@
1
+ export type SmithersAlertSeverity = "info" | "warning" | "critical";
2
+
3
+ export type SmithersAlertLabels = Record<string, string>;
4
+
5
+ export type SmithersAlertReactionKind =
6
+ | "emit-only"
7
+ | "pause"
8
+ | "cancel"
9
+ | "open-approval"
10
+ | "deliver";
11
+
12
+ export type SmithersAlertReaction =
13
+ | { kind: "emit-only" }
14
+ | { kind: "pause" }
15
+ | { kind: "cancel" }
16
+ | { kind: "open-approval" }
17
+ | { kind: "deliver"; destination: string };
18
+
19
+ export type SmithersAlertReactionRef = string | SmithersAlertReaction;
20
+
21
+ export type SmithersAlertPolicyDefaults = {
22
+ owner?: string;
23
+ severity?: SmithersAlertSeverity;
24
+ runbook?: string;
25
+ labels?: SmithersAlertLabels;
26
+ };
27
+
28
+ export type SmithersAlertPolicyRule = SmithersAlertPolicyDefaults & {
29
+ afterMs?: number;
30
+ reaction?: SmithersAlertReactionRef;
31
+ };
32
+
33
+ export type SmithersAlertPolicy = {
34
+ defaults?: SmithersAlertPolicyDefaults;
35
+ rules?: Record<string, SmithersAlertPolicyRule>;
36
+ reactions?: Record<string, SmithersAlertReaction>;
37
+ };
38
+
39
+ export type SmithersWorkflowOptions = {
40
+ alertPolicy?: SmithersAlertPolicy;
41
+ cache?: boolean;
42
+ workflowHash?: string;
43
+ };
@@ -0,0 +1,5 @@
1
+ export type TaskFailure = {
2
+ readonly nodeId: string;
3
+ readonly iteration: number;
4
+ readonly error: unknown;
5
+ };
@@ -0,0 +1,9 @@
1
+ import type { TokenUsage } from "./TokenUsage.ts";
2
+
3
+ export type TaskOutput = {
4
+ readonly nodeId: string;
5
+ readonly iteration: number;
6
+ readonly output: unknown;
7
+ readonly text?: string | null;
8
+ readonly usage?: TokenUsage | null;
9
+ };
@@ -0,0 +1,10 @@
1
+ import type { TaskDescriptor } from "@smithers-orchestrator/graph";
2
+ import type { TaskState } from "./TaskState.ts";
3
+
4
+ export type TaskRecord = {
5
+ readonly descriptor: TaskDescriptor;
6
+ readonly state: TaskState;
7
+ readonly output?: unknown;
8
+ readonly error?: unknown;
9
+ readonly updatedAtMs: number;
10
+ };
@@ -0,0 +1,10 @@
1
+ export type TaskState =
2
+ | "pending"
3
+ | "waiting-approval"
4
+ | "waiting-event"
5
+ | "waiting-timer"
6
+ | "in-progress"
7
+ | "finished"
8
+ | "failed"
9
+ | "cancelled"
10
+ | "skipped";
@@ -0,0 +1,3 @@
1
+ import type { TaskState } from "./TaskState.ts";
2
+
3
+ export type TaskStateMap = Map<string, TaskState>;
@@ -0,0 +1,9 @@
1
+ export type TokenUsage = {
2
+ readonly inputTokens?: number;
3
+ readonly outputTokens?: number;
4
+ readonly cacheReadTokens?: number;
5
+ readonly cacheWriteTokens?: number;
6
+ readonly reasoningTokens?: number;
7
+ readonly totalTokens?: number;
8
+ readonly [key: string]: unknown;
9
+ };
@@ -0,0 +1,8 @@
1
+ export type WaitReason =
2
+ | { readonly _tag: "Approval"; readonly nodeId: string }
3
+ | { readonly _tag: "Event"; readonly eventName: string }
4
+ | { readonly _tag: "Timer"; readonly resumeAtMs: number }
5
+ | { readonly _tag: "RetryBackoff"; readonly waitMs: number }
6
+ | { readonly _tag: "HotReload" }
7
+ | { readonly _tag: "OrphanRecovery"; readonly count: number }
8
+ | { readonly _tag: "ExternalTrigger" };
@@ -0,0 +1,10 @@
1
+ import { Context } from "effect";
2
+ /** @typedef {import("./WorkflowSessionService.ts").WorkflowSessionService} WorkflowSessionService */
3
+
4
+ const WorkflowSessionBase =
5
+ /** @type {Context.TagClass<WorkflowSession, "WorkflowSession", WorkflowSessionService>} */ (
6
+ /** @type {unknown} */ (Context.Tag("WorkflowSession")())
7
+ );
8
+
9
+ export class WorkflowSession extends WorkflowSessionBase {
10
+ }
@@ -0,0 +1,6 @@
1
+ import { Layer } from "effect";
2
+ import { WorkflowSession } from "./WorkflowSession.js";
3
+ import { makeWorkflowSession } from "./makeWorkflowSession.js";
4
+
5
+ /** @type {Layer.Layer<WorkflowSession, never, never>} */
6
+ export const WorkflowSessionLive = Layer.sync(WorkflowSession, makeWorkflowSession);
@@ -0,0 +1,10 @@
1
+ export type WorkflowSessionOptions = {
2
+ readonly runId?: string;
3
+ readonly nowMs?: () => number;
4
+ readonly requireStableFinish?: boolean;
5
+ readonly requireRerenderOnOutputChange?: boolean;
6
+ readonly initialRalphState?: ReadonlyMap<string, {
7
+ readonly iteration: number;
8
+ readonly done: boolean;
9
+ }>;
10
+ };
@@ -0,0 +1,52 @@
1
+ import type { Effect } from "effect";
2
+ import type { WorkflowGraph } from "@smithers-orchestrator/graph";
3
+ import type { TaskStateMap } from "./TaskStateMap.ts";
4
+ import type { ApprovalResolution } from "./ApprovalResolution.ts";
5
+ import type { ScheduleSnapshot } from "./ScheduleSnapshot.ts";
6
+ import type { EngineDecision } from "./EngineDecision.ts";
7
+ import type { TaskFailure } from "./TaskFailure.ts";
8
+ import type { TaskOutput } from "./TaskOutput.ts";
9
+
10
+ export type WorkflowSessionService = {
11
+ readonly submitGraph: (graph: WorkflowGraph) => Effect.Effect<EngineDecision>;
12
+ readonly taskCompleted: (output: TaskOutput) => Effect.Effect<EngineDecision>;
13
+ readonly taskFailed: (failure: TaskFailure) => Effect.Effect<EngineDecision>;
14
+ readonly approvalResolved: (
15
+ nodeId: string,
16
+ resolution: ApprovalResolution,
17
+ ) => Effect.Effect<EngineDecision>;
18
+ readonly approvalTimedOut: (nodeId: string) => Effect.Effect<EngineDecision>;
19
+ readonly eventReceived: (
20
+ eventName: string,
21
+ payload: unknown,
22
+ correlationId?: string | null,
23
+ ) => Effect.Effect<EngineDecision>;
24
+ readonly signalReceived: (
25
+ signalName: string,
26
+ payload: unknown,
27
+ correlationId?: string | null,
28
+ ) => Effect.Effect<EngineDecision>;
29
+ readonly timerFired: (
30
+ nodeId: string,
31
+ firedAtMs?: number,
32
+ ) => Effect.Effect<EngineDecision>;
33
+ readonly hotReloaded: (graph: WorkflowGraph) => Effect.Effect<EngineDecision>;
34
+ readonly heartbeatTimedOut: (
35
+ nodeId: string,
36
+ iteration?: number,
37
+ details?: Record<string, unknown>,
38
+ ) => Effect.Effect<EngineDecision>;
39
+ readonly cacheResolved: (
40
+ output: TaskOutput,
41
+ cached: boolean,
42
+ ) => Effect.Effect<EngineDecision>;
43
+ readonly cacheMissed: (
44
+ nodeId: string,
45
+ iteration?: number,
46
+ ) => Effect.Effect<EngineDecision>;
47
+ readonly recoverOrphanedTasks: () => Effect.Effect<EngineDecision>;
48
+ readonly cancelRequested: () => Effect.Effect<EngineDecision>;
49
+ readonly getTaskStates: () => Effect.Effect<TaskStateMap>;
50
+ readonly getSchedule: () => Effect.Effect<ScheduleSnapshot | null>;
51
+ readonly getCurrentGraph: () => Effect.Effect<WorkflowGraph | null>;
52
+ };