-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathstate-machine.zod.ts
More file actions
135 lines (113 loc) · 4.5 KB
/
Copy pathstate-machine.zod.ts
File metadata and controls
135 lines (113 loc) · 4.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { z } from 'zod';
import { SnakeCaseIdentifierSchema } from '../shared/identifiers.zod';
/**
* XState-inspired State Machine Protocol
* Used to define strict business logic constraints and lifecycle management.
* Prevent AI "hallucinations" by enforcing valid valid transitions.
*/
// --- Primitives ---
/**
* References a named action (side effect)
* Can be a script, a webhook, or a field update.
*/
import { lazySchema } from '../shared/lazy-schema';
export const ActionRefSchema = lazySchema(() => z.union([
z.string().describe('Action Name'),
z.object({
type: z.string(), // e.g., 'xstate.assign', 'log', 'email'
params: z.record(z.string(), z.unknown()).optional()
})
]));
/**
* References a named condition (guard)
* Must evaluate to true for the transition to occur.
*/
export const GuardRefSchema = lazySchema(() => z.union([
z.string().describe('Guard Name (e.g., "isManager", "amountGT1000")'),
z.object({
type: z.string(),
params: z.record(z.string(), z.unknown()).optional()
})
]));
// --- Core Structure ---
/**
* State Transition Definition
* "When EVENT happens, if GUARD is true, go to TARGET and run ACTIONS"
*/
export const TransitionSchema = lazySchema(() => z.object({
target: z.string().optional().describe('Target State ID'),
cond: GuardRefSchema.optional().describe('Condition (Guard) required to take this path'),
actions: z.array(ActionRefSchema).optional().describe('Actions to execute during transition'),
description: z.string().optional().describe('Human readable description of this rule'),
}));
/**
* Event Definition (Signals)
*/
export const EventSchema = lazySchema(() => z.object({
type: z.string().describe('Event Type (e.g. "APPROVE", "REJECT", "Submit")'),
// Payload validation schema could go here if we want deep validation
schema: z.record(z.string(), z.unknown()).optional().describe('Expected event payload structure'),
}));
export type ActionRef = z.infer<typeof ActionRefSchema>;
export type Transition = z.infer<typeof TransitionSchema>;
export type StateNodeConfig = {
type?: 'atomic' | 'compound' | 'parallel' | 'final' | 'history';
entry?: ActionRef[];
exit?: ActionRef[];
on?: Record<string, string | Transition | Transition[]>;
always?: Transition[];
initial?: string;
states?: Record<string, StateNodeConfig>;
meta?: {
label?: string;
description?: string;
color?: string;
aiInstructions?: string;
};
};
/**
* State Node Definition
*/
export const StateNodeSchema: z.ZodType<StateNodeConfig> = z.lazy(() => z.object({
/** Type of state */
type: z.enum(['atomic', 'compound', 'parallel', 'final', 'history']).default('atomic'),
/** Entry/Exit Actions */
entry: z.array(ActionRefSchema).optional().describe('Actions to run when entering this state'),
exit: z.array(ActionRefSchema).optional().describe('Actions to run when leaving this state'),
/** Transitions (Events) */
on: z.record(z.string(), z.union([
z.string(), // Shorthand target
TransitionSchema,
z.array(TransitionSchema)
])).optional().describe('Map of Event Type -> Transition Definition'),
/** Always Transitions (Eventless) */
always: z.array(TransitionSchema).optional(),
/** Nesting (Hierarchical States) */
initial: z.string().optional().describe('Initial child state (if compound)'),
states: z.record(z.string(), StateNodeSchema).optional(),
/** Metadata for UI/AI */
meta: z.object({
label: z.string().optional(),
description: z.string().optional(),
color: z.string().optional(), // For UI diagrams
// Instructions for AI Agent when in this state
aiInstructions: z.string().optional().describe('Specific instructions for AI when in this state'),
}).optional(),
}));
/**
* Top-Level State Machine Definition
*/
export const StateMachineSchema = lazySchema(() => z.object({
id: SnakeCaseIdentifierSchema.describe('Unique Machine ID'),
description: z.string().optional(),
/** Context (Memory) Schema */
contextSchema: z.record(z.string(), z.unknown()).optional().describe('Zod Schema for the machine context/memory'),
/** Initial State */
initial: z.string().describe('Initial State ID'),
/** State Definitions */
states: z.record(z.string(), StateNodeSchema).describe('State Nodes'),
/** Global Listeners */
on: z.record(z.string(), z.union([z.string(), TransitionSchema, z.array(TransitionSchema)])).optional(),
}));
export type StateMachineConfig = z.infer<typeof StateMachineSchema>;