@protoyard/flows 0.0.0-stage → 1.1.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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -2
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +2 -0
  5. package/dist/model/conditions.d.ts +75 -0
  6. package/dist/model/conditions.js +297 -0
  7. package/dist/model/data.d.ts +14 -0
  8. package/dist/model/data.js +73 -0
  9. package/dist/model/flow.d.ts +53 -0
  10. package/dist/model/flow.js +93 -0
  11. package/dist/model/index.d.ts +17 -0
  12. package/dist/model/index.js +17 -0
  13. package/dist/model/limits.d.ts +32 -0
  14. package/dist/model/limits.js +32 -0
  15. package/dist/model/ops.d.ts +122 -0
  16. package/dist/model/ops.js +311 -0
  17. package/dist/model/recipes.d.ts +12 -0
  18. package/dist/model/recipes.js +209 -0
  19. package/dist/model/refs.d.ts +30 -0
  20. package/dist/model/refs.js +48 -0
  21. package/dist/model/steps.d.ts +276 -0
  22. package/dist/model/steps.js +206 -0
  23. package/dist/model/text.d.ts +30 -0
  24. package/dist/model/text.js +33 -0
  25. package/dist/model/triggers.d.ts +42 -0
  26. package/dist/model/triggers.js +68 -0
  27. package/dist/model/validate.d.ts +19 -0
  28. package/dist/model/validate.js +207 -0
  29. package/dist/model/walk.d.ts +40 -0
  30. package/dist/model/walk.js +68 -0
  31. package/dist/runtime/clock.d.ts +12 -0
  32. package/dist/runtime/clock.js +40 -0
  33. package/dist/runtime/engine.d.ts +15 -0
  34. package/dist/runtime/engine.js +578 -0
  35. package/dist/runtime/index.d.ts +11 -0
  36. package/dist/runtime/index.js +10 -0
  37. package/dist/runtime/keeps.d.ts +8 -0
  38. package/dist/runtime/keeps.js +33 -0
  39. package/dist/runtime/outbox.d.ts +8 -0
  40. package/dist/runtime/outbox.js +27 -0
  41. package/dist/runtime/seed.d.ts +7 -0
  42. package/dist/runtime/seed.js +45 -0
  43. package/dist/runtime/signals.d.ts +4 -0
  44. package/dist/runtime/signals.js +13 -0
  45. package/dist/runtime/types.d.ts +127 -0
  46. package/dist/runtime/types.js +5 -0
  47. package/package.json +62 -3
@@ -0,0 +1,209 @@
1
+ const team = [{ type: 'team', id: 'team', name: 'The team' }];
2
+ const leads = [{ type: 'role', id: 'lead', name: 'Team leads' }];
3
+ function recipe(id, title, summary, flow) {
4
+ return {
5
+ id,
6
+ title,
7
+ summary,
8
+ build: (overrides = {}) => ({
9
+ formatVersion: 1,
10
+ id: overrides.id ?? id,
11
+ name: overrides.name ?? title,
12
+ status: 'draft',
13
+ ...structuredClone(flow),
14
+ }),
15
+ };
16
+ }
17
+ export const FLOW_RECIPES = [
18
+ recipe('daily-check-in', 'Daily check-in', 'Ask the team three questions each workday and share a summary.', {
19
+ triggers: [
20
+ {
21
+ id: 'weekday-morning',
22
+ type: 'schedule',
23
+ days: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
24
+ time: '09:30',
25
+ timezone: 'local',
26
+ },
27
+ ],
28
+ steps: [
29
+ {
30
+ id: 'ask-team',
31
+ type: 'ask',
32
+ audience: team,
33
+ questions: [
34
+ { id: 'done', kind: 'text', prompt: 'What did you finish yesterday?', required: true },
35
+ { id: 'next', kind: 'text', prompt: 'What are you working on today?', required: true },
36
+ { id: 'blocked', kind: 'boolean', prompt: 'Is anything blocking you?' },
37
+ ],
38
+ deadlineMinutes: 240,
39
+ },
40
+ {
41
+ id: 'summarize',
42
+ type: 'assistant',
43
+ task: 'summarize',
44
+ input: { from: 'step', stepId: 'ask-team' },
45
+ },
46
+ {
47
+ id: 'share-summary',
48
+ type: 'message',
49
+ channel: 'chat',
50
+ to: team,
51
+ text: ['Today’s check-in: ', { ref: { from: 'step', stepId: 'summarize' } }],
52
+ },
53
+ ],
54
+ }),
55
+ recipe('approval-request', 'Approval request', 'Collect a request, route it to an approver and tell the requester.', {
56
+ triggers: [
57
+ { id: 'request', type: 'manual', label: 'New request', surfaces: ['app', 'link'] },
58
+ ],
59
+ collections: [
60
+ {
61
+ id: 'requests',
62
+ noun: 'request',
63
+ plural: 'requests',
64
+ states: [
65
+ { id: 'pending', label: 'Pending' },
66
+ { id: 'approved', label: 'Approved', final: true },
67
+ { id: 'declined', label: 'Declined', final: true },
68
+ ],
69
+ },
70
+ ],
71
+ steps: [
72
+ {
73
+ id: 'save-request',
74
+ type: 'save_record',
75
+ collection: 'requests',
76
+ title: [{ ref: { from: 'trigger', path: ['summary'] } }],
77
+ state: 'pending',
78
+ },
79
+ {
80
+ id: 'approve',
81
+ type: 'approval',
82
+ approvers: leads,
83
+ prompt: ['Approve this request? ', { ref: { from: 'trigger', path: ['summary'] } }],
84
+ },
85
+ {
86
+ id: 'decide',
87
+ type: 'branch',
88
+ when: {
89
+ type: 'compare',
90
+ left: { ref: { from: 'step', stepId: 'approve', path: ['decision'] } },
91
+ op: 'eq',
92
+ right: { value: 'approved' },
93
+ },
94
+ // biome-ignore lint/suspicious/noThenProperty: a branch arm, not a thenable
95
+ then: [
96
+ { id: 'mark-approved', type: 'set_state', state: 'approved' },
97
+ { id: 'tell-approved', type: 'reply', text: 'Your request was approved.' },
98
+ ],
99
+ otherwise: [
100
+ { id: 'mark-declined', type: 'set_state', state: 'declined' },
101
+ { id: 'tell-declined', type: 'reply', text: 'Your request was declined.' },
102
+ ],
103
+ },
104
+ ],
105
+ }),
106
+ recipe('incoming-triage', 'Incoming triage', 'Classify each new record and alert the team about urgent ones.', {
107
+ triggers: [{ id: 'new-record', type: 'event', event: 'record.created', source: 'records' }],
108
+ collections: [
109
+ {
110
+ id: 'tickets',
111
+ noun: 'ticket',
112
+ plural: 'tickets',
113
+ states: [
114
+ { id: 'new', label: 'New' },
115
+ { id: 'urgent', label: 'Urgent' },
116
+ { id: 'closed', label: 'Closed', final: true },
117
+ ],
118
+ },
119
+ ],
120
+ steps: [
121
+ {
122
+ id: 'classify',
123
+ type: 'assistant',
124
+ task: 'classify',
125
+ input: { from: 'record', path: ['body'] },
126
+ labels: ['urgent', 'normal', 'spam'],
127
+ },
128
+ {
129
+ id: 'urgent',
130
+ type: 'branch',
131
+ when: {
132
+ type: 'any',
133
+ conditions: [
134
+ {
135
+ type: 'compare',
136
+ left: { ref: { from: 'step', stepId: 'classify', path: ['label'] } },
137
+ op: 'eq',
138
+ right: { value: 'urgent' },
139
+ },
140
+ { type: 'judge', phrase: 'the customer cannot work at all' },
141
+ ],
142
+ },
143
+ // biome-ignore lint/suspicious/noThenProperty: a branch arm, not a thenable
144
+ then: [
145
+ { id: 'mark-urgent', type: 'set_state', state: 'urgent' },
146
+ {
147
+ id: 'alert-team',
148
+ type: 'message',
149
+ channel: 'notification',
150
+ to: team,
151
+ text: ['Urgent: ', { ref: { from: 'record', path: ['title'] } }],
152
+ },
153
+ ],
154
+ otherwise: [],
155
+ },
156
+ ],
157
+ }),
158
+ recipe('weekly-digest', 'Weekly digest', 'Every Friday, gather the week’s records and send a digest by email.', {
159
+ triggers: [
160
+ { id: 'friday', type: 'schedule', days: ['friday'], time: '16:00', timezone: 'local' },
161
+ ],
162
+ collections: [{ id: 'updates', noun: 'update', states: [] }],
163
+ steps: [
164
+ { id: 'gather', type: 'get_records', collection: 'updates', sinceDays: 7, limit: 100 },
165
+ {
166
+ id: 'any-updates',
167
+ type: 'branch',
168
+ when: { type: 'test', subject: { from: 'step', stepId: 'gather' }, test: 'not_empty' },
169
+ // biome-ignore lint/suspicious/noThenProperty: a branch arm, not a thenable
170
+ then: [
171
+ {
172
+ id: 'digest',
173
+ type: 'assistant',
174
+ task: 'summarize',
175
+ input: { from: 'step', stepId: 'gather' },
176
+ },
177
+ {
178
+ id: 'send-digest',
179
+ type: 'message',
180
+ channel: 'email',
181
+ to: leads,
182
+ subject: 'Weekly digest',
183
+ text: [{ ref: { from: 'step', stepId: 'digest' } }],
184
+ },
185
+ ],
186
+ otherwise: [],
187
+ },
188
+ ],
189
+ }),
190
+ recipe('rotating-host', 'Rotating host', 'Each Monday, pick the next host from a list and announce it.', {
191
+ triggers: [
192
+ { id: 'monday', type: 'schedule', days: ['monday'], time: '10:00', timezone: 'local' },
193
+ ],
194
+ lists: [{ id: 'hosts', name: 'Hosts', items: ['Ada Example', 'Lin Example', 'Sam Example'] }],
195
+ steps: [
196
+ { id: 'pick-host', type: 'pick_from_list', list: 'hosts', order: 'next_unused' },
197
+ {
198
+ id: 'announce',
199
+ type: 'message',
200
+ channel: 'chat',
201
+ to: team,
202
+ text: ['This week’s host is ', { ref: { from: 'step', stepId: 'pick-host' } }, '.'],
203
+ },
204
+ ],
205
+ }),
206
+ ];
207
+ export function findRecipe(id) {
208
+ return FLOW_RECIPES.find((r) => r.id === id);
209
+ }
@@ -0,0 +1,30 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * A reference to data a run has at hand: a field of what started it
4
+ * (`trigger`), the output of an earlier step (`step`), or the record the run
5
+ * is about (`record`). The path is a list of plain segments, never an
6
+ * expression, and only own properties are read.
7
+ */
8
+ export type DataRef = {
9
+ from: 'trigger';
10
+ path?: string[];
11
+ } | {
12
+ from: 'step';
13
+ stepId: string;
14
+ path?: string[];
15
+ } | {
16
+ from: 'record';
17
+ path?: string[];
18
+ };
19
+ export declare const FlowIdSchema: z.ZodString;
20
+ export declare const DataRefSchema: z.ZodType<DataRef>;
21
+ /** What a run offers references: its trigger payload, step outputs and record. */
22
+ export interface RefScope {
23
+ trigger?: unknown;
24
+ outputs?: Readonly<Record<string, unknown>>;
25
+ record?: unknown;
26
+ }
27
+ /** Reads a reference from a run scope; anything missing is `undefined`. */
28
+ export declare function resolveRef(ref: DataRef, scope?: RefScope): unknown;
29
+ /** A short human label: `trigger.payload.status`, `step check.result`, `record.state`. */
30
+ export declare function describeRef(ref: DataRef): string;
@@ -0,0 +1,48 @@
1
+ import { z } from 'zod';
2
+ import { FORBIDDEN_KEYS } from "./data.js";
3
+ import { FLOW_ID_PATTERN, FLOW_LIMITS } from "./limits.js";
4
+ export const FlowIdSchema = z.string().regex(FLOW_ID_PATTERN, 'Use a lowercase slug id');
5
+ const SegmentSchema = z
6
+ .string()
7
+ .regex(/^[A-Za-z0-9_-]{1,64}$/, 'Use letters, digits, - or _ in a path segment')
8
+ .refine((segment) => !FORBIDDEN_KEYS.has(segment), 'This path segment is not allowed');
9
+ const PathSchema = z.array(SegmentSchema).min(1).max(FLOW_LIMITS.maxPathSegments).optional();
10
+ export const DataRefSchema = z.discriminatedUnion('from', [
11
+ z.strictObject({ from: z.literal('trigger'), path: PathSchema }),
12
+ z.strictObject({ from: z.literal('step'), stepId: FlowIdSchema, path: PathSchema }),
13
+ z.strictObject({ from: z.literal('record'), path: PathSchema }),
14
+ ]);
15
+ function readPath(root, path) {
16
+ let value = root;
17
+ for (const segment of path ?? []) {
18
+ if (value === null || typeof value !== 'object' || FORBIDDEN_KEYS.has(segment)) {
19
+ return undefined;
20
+ }
21
+ if (!Object.hasOwn(value, segment))
22
+ return undefined;
23
+ value = value[segment];
24
+ }
25
+ return value;
26
+ }
27
+ /** Reads a reference from a run scope; anything missing is `undefined`. */
28
+ export function resolveRef(ref, scope = {}) {
29
+ switch (ref.from) {
30
+ case 'trigger':
31
+ return readPath(scope.trigger, ref.path);
32
+ case 'record':
33
+ return readPath(scope.record, ref.path);
34
+ case 'step': {
35
+ const outputs = scope.outputs ?? {};
36
+ if (!Object.hasOwn(outputs, ref.stepId))
37
+ return undefined;
38
+ return readPath(outputs[ref.stepId], ref.path);
39
+ }
40
+ }
41
+ }
42
+ /** A short human label: `trigger.payload.status`, `step check.result`, `record.state`. */
43
+ export function describeRef(ref) {
44
+ const tail = ref.path?.length ? `.${ref.path.join('.')}` : '';
45
+ if (ref.from === 'step')
46
+ return `step ${ref.stepId}${tail}`;
47
+ return `${ref.from}${tail}`;
48
+ }
@@ -0,0 +1,276 @@
1
+ import { z } from 'zod';
2
+ import { type Condition } from './conditions.ts';
3
+ export declare const QUESTION_KINDS: readonly ["text", "choice", "boolean", "number"];
4
+ export declare const QuestionSchema: z.ZodObject<{
5
+ id: z.ZodString;
6
+ kind: z.ZodEnum<{
7
+ number: "number";
8
+ boolean: "boolean";
9
+ text: "text";
10
+ choice: "choice";
11
+ }>;
12
+ prompt: z.ZodString;
13
+ required: z.ZodOptional<z.ZodBoolean>;
14
+ choices: z.ZodOptional<z.ZodArray<z.ZodString>>;
15
+ }, z.core.$strict>;
16
+ export type Question = z.infer<typeof QuestionSchema>;
17
+ declare const LEAF_STEP_SCHEMAS: readonly [z.ZodObject<{
18
+ type: z.ZodLiteral<"ask">;
19
+ audience: z.ZodArray<z.ZodObject<{
20
+ type: z.ZodEnum<{
21
+ person: "person";
22
+ team: "team";
23
+ role: "role";
24
+ channel: "channel";
25
+ list: "list";
26
+ everyone: "everyone";
27
+ }>;
28
+ id: z.ZodString;
29
+ name: z.ZodString;
30
+ }, z.core.$strict>>;
31
+ questions: z.ZodArray<z.ZodObject<{
32
+ id: z.ZodString;
33
+ kind: z.ZodEnum<{
34
+ number: "number";
35
+ boolean: "boolean";
36
+ text: "text";
37
+ choice: "choice";
38
+ }>;
39
+ prompt: z.ZodString;
40
+ required: z.ZodOptional<z.ZodBoolean>;
41
+ choices: z.ZodOptional<z.ZodArray<z.ZodString>>;
42
+ }, z.core.$strict>>;
43
+ deadlineMinutes: z.ZodOptional<z.ZodNumber>;
44
+ id: z.ZodString;
45
+ label: z.ZodOptional<z.ZodString>;
46
+ enabled: z.ZodOptional<z.ZodBoolean>;
47
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
48
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
49
+ }, z.core.$strict>, z.ZodObject<{
50
+ type: z.ZodLiteral<"approval">;
51
+ approvers: z.ZodArray<z.ZodObject<{
52
+ type: z.ZodEnum<{
53
+ person: "person";
54
+ team: "team";
55
+ role: "role";
56
+ channel: "channel";
57
+ list: "list";
58
+ everyone: "everyone";
59
+ }>;
60
+ id: z.ZodString;
61
+ name: z.ZodString;
62
+ }, z.core.$strict>>;
63
+ prompt: z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>;
64
+ id: z.ZodString;
65
+ label: z.ZodOptional<z.ZodString>;
66
+ enabled: z.ZodOptional<z.ZodBoolean>;
67
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
68
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
69
+ }, z.core.$strict>, z.ZodObject<{
70
+ type: z.ZodLiteral<"wait">;
71
+ minutes: z.ZodNumber;
72
+ id: z.ZodString;
73
+ label: z.ZodOptional<z.ZodString>;
74
+ enabled: z.ZodOptional<z.ZodBoolean>;
75
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
76
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
77
+ }, z.core.$strict>, z.ZodObject<{
78
+ type: z.ZodLiteral<"wait_until">;
79
+ until: z.ZodEnum<{
80
+ time: "time";
81
+ event: "event";
82
+ approval: "approval";
83
+ replies: "replies";
84
+ }>;
85
+ time: z.ZodOptional<z.ZodString>;
86
+ event: z.ZodOptional<z.ZodString>;
87
+ id: z.ZodString;
88
+ label: z.ZodOptional<z.ZodString>;
89
+ enabled: z.ZodOptional<z.ZodBoolean>;
90
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
91
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
92
+ }, z.core.$strict>, z.ZodObject<{
93
+ type: z.ZodLiteral<"message">;
94
+ channel: z.ZodEnum<{
95
+ email: "email";
96
+ chat: "chat";
97
+ inbox: "inbox";
98
+ notification: "notification";
99
+ }>;
100
+ to: z.ZodArray<z.ZodObject<{
101
+ type: z.ZodEnum<{
102
+ person: "person";
103
+ team: "team";
104
+ role: "role";
105
+ channel: "channel";
106
+ list: "list";
107
+ everyone: "everyone";
108
+ }>;
109
+ id: z.ZodString;
110
+ name: z.ZodString;
111
+ }, z.core.$strict>>;
112
+ subject: z.ZodOptional<z.ZodString>;
113
+ text: z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>;
114
+ id: z.ZodString;
115
+ label: z.ZodOptional<z.ZodString>;
116
+ enabled: z.ZodOptional<z.ZodBoolean>;
117
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
118
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
119
+ }, z.core.$strict>, z.ZodObject<{
120
+ type: z.ZodLiteral<"reply">;
121
+ text: z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>;
122
+ id: z.ZodString;
123
+ label: z.ZodOptional<z.ZodString>;
124
+ enabled: z.ZodOptional<z.ZodBoolean>;
125
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
126
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
127
+ }, z.core.$strict>, z.ZodObject<{
128
+ type: z.ZodLiteral<"assistant">;
129
+ task: z.ZodEnum<{
130
+ check: "check";
131
+ summarize: "summarize";
132
+ classify: "classify";
133
+ extract: "extract";
134
+ draft: "draft";
135
+ }>;
136
+ input: z.ZodOptional<z.ZodType<import("./refs.ts").DataRef, unknown, z.core.$ZodTypeInternals<import("./refs.ts").DataRef, unknown>>>;
137
+ instructions: z.ZodOptional<z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>>;
138
+ labels: z.ZodOptional<z.ZodArray<z.ZodString>>;
139
+ id: z.ZodString;
140
+ label: z.ZodOptional<z.ZodString>;
141
+ enabled: z.ZodOptional<z.ZodBoolean>;
142
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
143
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
144
+ }, z.core.$strict>, z.ZodObject<{
145
+ type: z.ZodLiteral<"save_record">;
146
+ collection: z.ZodString;
147
+ title: z.ZodOptional<z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>>;
148
+ fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>>>;
149
+ state: z.ZodOptional<z.ZodString>;
150
+ id: z.ZodString;
151
+ label: z.ZodOptional<z.ZodString>;
152
+ enabled: z.ZodOptional<z.ZodBoolean>;
153
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
154
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
155
+ }, z.core.$strict>, z.ZodObject<{
156
+ type: z.ZodLiteral<"get_records">;
157
+ collection: z.ZodString;
158
+ sinceDays: z.ZodNumber;
159
+ limit: z.ZodNumber;
160
+ id: z.ZodString;
161
+ label: z.ZodOptional<z.ZodString>;
162
+ enabled: z.ZodOptional<z.ZodBoolean>;
163
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
164
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
165
+ }, z.core.$strict>, z.ZodObject<{
166
+ type: z.ZodLiteral<"set_state">;
167
+ state: z.ZodString;
168
+ id: z.ZodString;
169
+ label: z.ZodOptional<z.ZodString>;
170
+ enabled: z.ZodOptional<z.ZodBoolean>;
171
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
172
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
173
+ }, z.core.$strict>, z.ZodObject<{
174
+ type: z.ZodLiteral<"pick_from_list">;
175
+ list: z.ZodString;
176
+ order: z.ZodEnum<{
177
+ next_unused: "next_unused";
178
+ seeded: "seeded";
179
+ }>;
180
+ id: z.ZodString;
181
+ label: z.ZodOptional<z.ZodString>;
182
+ enabled: z.ZodOptional<z.ZodBoolean>;
183
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
184
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
185
+ }, z.core.$strict>, z.ZodObject<{
186
+ type: z.ZodLiteral<"update_list">;
187
+ list: z.ZodString;
188
+ action: z.ZodEnum<{
189
+ add: "add";
190
+ remove: "remove";
191
+ }>;
192
+ value: z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>;
193
+ id: z.ZodString;
194
+ label: z.ZodOptional<z.ZodString>;
195
+ enabled: z.ZodOptional<z.ZodBoolean>;
196
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
197
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
198
+ }, z.core.$strict>, z.ZodObject<{
199
+ type: z.ZodLiteral<"group_people">;
200
+ from: z.ZodArray<z.ZodObject<{
201
+ type: z.ZodEnum<{
202
+ person: "person";
203
+ team: "team";
204
+ role: "role";
205
+ channel: "channel";
206
+ list: "list";
207
+ everyone: "everyone";
208
+ }>;
209
+ id: z.ZodString;
210
+ name: z.ZodString;
211
+ }, z.core.$strict>>;
212
+ mode: z.ZodEnum<{
213
+ pairs: "pairs";
214
+ groups: "groups";
215
+ pick_one: "pick_one";
216
+ }>;
217
+ size: z.ZodOptional<z.ZodNumber>;
218
+ id: z.ZodString;
219
+ label: z.ZodOptional<z.ZodString>;
220
+ enabled: z.ZodOptional<z.ZodBoolean>;
221
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
222
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
223
+ }, z.core.$strict>, z.ZodObject<{
224
+ type: z.ZodLiteral<"call_tool">;
225
+ tool: z.ZodString;
226
+ action: z.ZodString;
227
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./text.ts").FlowText, unknown, z.core.$ZodTypeInternals<import("./text.ts").FlowText, unknown>>>>;
228
+ id: z.ZodString;
229
+ label: z.ZodOptional<z.ZodString>;
230
+ enabled: z.ZodOptional<z.ZodBoolean>;
231
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
232
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
233
+ }, z.core.$strict>, z.ZodObject<{
234
+ type: z.ZodLiteral<"start_flow">;
235
+ flowId: z.ZodString;
236
+ id: z.ZodString;
237
+ label: z.ZodOptional<z.ZodString>;
238
+ enabled: z.ZodOptional<z.ZodBoolean>;
239
+ only: z.ZodOptional<z.ZodType<Condition, unknown, z.core.$ZodTypeInternals<Condition, unknown>>>;
240
+ delayMinutes: z.ZodOptional<z.ZodNumber>;
241
+ }, z.core.$strict>];
242
+ type LeafStep = z.infer<(typeof LEAF_STEP_SCHEMAS)[number]>;
243
+ /** A branch: `then` runs when `when` passes, `otherwise` when it fails. */
244
+ export interface BranchStep {
245
+ id: string;
246
+ type: 'branch';
247
+ label?: string;
248
+ enabled?: boolean;
249
+ only?: Condition;
250
+ delayMinutes?: number;
251
+ when: Condition;
252
+ then: Step[];
253
+ otherwise: Step[];
254
+ }
255
+ export type Step = LeafStep | BranchStep;
256
+ export type StepType = Step['type'];
257
+ export type StepOf<T extends StepType> = Extract<Step, {
258
+ type: T;
259
+ }>;
260
+ export declare const StepSchema: z.ZodType<Step>;
261
+ export type StepCategory = 'people' | 'timing' | 'logic' | 'messages' | 'data' | 'tools';
262
+ export interface StepKind {
263
+ type: StepType;
264
+ label: string;
265
+ category: StepCategory;
266
+ /** What the step leaves for later steps to reference. */
267
+ output: 'replies' | 'decision' | 'text' | 'records' | 'item' | 'group' | 'none';
268
+ /** True when a runtime turns it into a proposed effect (a message, a saved record…). */
269
+ effect: boolean;
270
+ }
271
+ /** One entry per step type, in editor order. */
272
+ export declare const STEP_KINDS: readonly StepKind[];
273
+ export declare const STEP_TYPES: readonly StepType[];
274
+ export declare function stepKind(type: StepType): StepKind;
275
+ export declare function isStepEnabled(step: Step): boolean;
276
+ export {};