@voltro/plugin-ai-flows 0.34.0 → 0.36.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.
@@ -0,0 +1,142 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/condition.ts
3
+ var t = e.Literal("truthy", "falsy", "eq", "neq", "contains"), n = e.Struct({
4
+ ref: e.String,
5
+ op: t,
6
+ value: e.optional(e.Unknown)
7
+ }), r = (e) => e == null ? !1 : typeof e == "boolean" ? e : !Array.isArray(e) || e.length > 0, i = (e, t) => {
8
+ if (e === t) return !0;
9
+ if (e === null || t === null || e === void 0 || t === void 0) return !1;
10
+ if (typeof e == "object" || typeof t == "object") try {
11
+ return JSON.stringify(e) === JSON.stringify(t);
12
+ } catch {
13
+ return !1;
14
+ }
15
+ return String(e) === String(t);
16
+ }, a = (e, t) => {
17
+ let n = t[e.ref];
18
+ switch (e.op) {
19
+ case "truthy": return r(n);
20
+ case "falsy": return !r(n);
21
+ case "eq": return i(n, e.value);
22
+ case "neq": return !i(n, e.value);
23
+ case "contains": {
24
+ if (n == null) return !1;
25
+ if (Array.isArray(n)) return n.some((t) => i(t, e.value));
26
+ let t = e.value;
27
+ return typeof t != "string" && typeof t != "number" ? !1 : String(n).includes(String(t));
28
+ }
29
+ }
30
+ }, o = (e) => {
31
+ switch (e.op) {
32
+ case "truthy": return `{{${e.ref}}} is set`;
33
+ case "falsy": return `{{${e.ref}}} is not set`;
34
+ case "eq": return `{{${e.ref}}} equals ${JSON.stringify(e.value)}`;
35
+ case "neq": return `{{${e.ref}}} does not equal ${JSON.stringify(e.value)}`;
36
+ case "contains": return `{{${e.ref}}} contains ${JSON.stringify(e.value)}`;
37
+ }
38
+ }, s = e.Literal("deterministic", "agentic"), c = e.Literal("draft", "active", "archived"), l = e.Literal("private", "organization", "shared"), u = e.Literal("pending", "running", "waiting", "succeeded", "failed", "cancelled"), d = e.Literal("manual", "cron"), f = e.Literal("agent", "generate", "structured", "note", "human"), p = e.Literal("text", "image", "video", "audio"), m = e.Literal("approve", "choice", "text"), h = e.Struct({
39
+ value: e.String,
40
+ label: e.String
41
+ }), g = e.Union(e.Struct({
42
+ from: e.Literal("upload"),
43
+ url: e.String
44
+ }), e.Struct({
45
+ from: e.Literal("step"),
46
+ ref: e.String
47
+ })), _ = e.Record({
48
+ key: e.String,
49
+ value: g
50
+ }), v = e.Record({
51
+ key: e.String,
52
+ value: e.Unknown
53
+ }), y = e.Struct({
54
+ id: e.String,
55
+ title: e.optional(e.String),
56
+ description: e.optional(e.String),
57
+ type: f,
58
+ outputKey: e.optional(e.String),
59
+ prompt: e.optional(e.String),
60
+ modality: e.optional(p),
61
+ model: e.optional(e.String),
62
+ params: e.optional(v),
63
+ mediaInputs: e.optional(_),
64
+ agentRef: e.optional(e.String),
65
+ schema: e.optional(v),
66
+ reviewMode: e.optional(m),
67
+ options: e.optional(e.Array(h)),
68
+ when: e.optional(n),
69
+ group: e.optional(e.String),
70
+ timeoutMs: e.optional(e.Number)
71
+ }), b = e.Struct({
72
+ key: e.String,
73
+ label: e.String,
74
+ type: e.Literal("text", "textarea", "number", "boolean"),
75
+ required: e.optional(e.Boolean),
76
+ placeholder: e.optional(e.String)
77
+ }), x = e.Struct({
78
+ flowRef: e.String,
79
+ mappings: e.Array(e.Struct({
80
+ targetKey: e.String,
81
+ sourceKey: e.String
82
+ })),
83
+ requireConfirmation: e.optional(e.Boolean)
84
+ }), S = e.Struct({
85
+ frequency: e.optional(e.Literal("weekly", "monthly")),
86
+ repeats: e.Array(e.Number),
87
+ intervalWeeks: e.optional(e.Number),
88
+ anchorDate: e.optional(e.String),
89
+ weekOfMonth: e.optional(e.Union(e.Literal(1, 2, 3, 4), e.Literal("last"))),
90
+ hour: e.optional(e.Number),
91
+ minute: e.optional(e.Literal(0, 15, 30, 45)),
92
+ defaultInput: e.optional(v)
93
+ }), C = e.Literal("pending", "running", "waiting", "succeeded", "failed", "skipped"), w = e.Struct({
94
+ mode: m,
95
+ options: e.optional(e.Array(h)),
96
+ prompt: e.String
97
+ }), T = e.Struct({
98
+ id: e.Number,
99
+ title: e.String,
100
+ description: e.optional(e.String),
101
+ type: f,
102
+ status: C,
103
+ startedAt: e.optional(e.String),
104
+ completedAt: e.optional(e.String),
105
+ costMicroUsd: e.optional(e.Number),
106
+ requestId: e.optional(e.String),
107
+ url: e.optional(e.String),
108
+ output: e.optional(e.Struct({ text: e.String })),
109
+ errorMessage: e.optional(e.String),
110
+ skipReason: e.optional(e.String),
111
+ review: e.optional(w)
112
+ }), E = e.Struct({
113
+ decision: e.optional(e.Literal("approve", "reject")),
114
+ value: e.optional(e.String),
115
+ text: e.optional(e.String),
116
+ respondedAt: e.optional(e.String)
117
+ }), D = e.Struct({
118
+ flowRef: e.String,
119
+ input: v,
120
+ status: e.Literal("pending", "confirmed", "dismissed")
121
+ }), O = 30, k = 1, A = 100, j = (e) => e == null || !Number.isFinite(e) ? 30 : Math.min(100, Math.max(1, Math.trunc(e))), M = (e) => e.replace(/[^\w.-]/g, "_"), N = (e) => {
122
+ let t = (e.steps ?? []).map((e) => e.outputKey ? {
123
+ ...e,
124
+ outputKey: M(e.outputKey)
125
+ } : e), n = e.orchestratorModel || e.orchestratorInstructions ? {
126
+ ...e.orchestratorModel ? { model: e.orchestratorModel } : {},
127
+ ...e.orchestratorInstructions ? { instructions: e.orchestratorInstructions } : {}
128
+ } : void 0;
129
+ return {
130
+ name: e.name,
131
+ mode: e.mode ?? "deterministic",
132
+ steps: t,
133
+ maxSteps: j(e.maxSteps),
134
+ brief: e.brief ?? [],
135
+ ...n ? { orchestrator: n } : {},
136
+ ...e.chain ? { chain: e.chain } : {},
137
+ ...e.cadence ? { cadence: e.cadence } : {},
138
+ ...e.humanTimeoutMs != null && Number.isFinite(e.humanTimeoutMs) ? { humanTimeoutMs: e.humanTimeoutMs } : {}
139
+ };
140
+ };
141
+ //#endregion
142
+ export { o as A, f as C, M as D, N as E, r as M, t as O, C as S, j as T, h as _, s as a, u as b, E as c, A as d, k as f, m as g, p as h, x as i, a as j, n as k, v as l, _ as m, D as n, c as o, g as p, S as r, y as s, b as t, O as u, w as v, l as w, T as x, d as y };
package/dist/ir.d.ts CHANGED
@@ -37,6 +37,26 @@ export declare interface ChainPending {
37
37
 
38
38
  export declare const clampMaxSteps: (n: number | null | undefined) => number;
39
39
 
40
+ /** The comparison a `when:` performs. */
41
+ export declare const ConditionOp: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
42
+
43
+ export declare type ConditionOp = typeof ConditionOp.Type;
44
+
45
+ /** A one-line rendering for the run timeline and the editor preview. Exported
46
+ * so the reason a step was skipped is the SAME sentence in both places. */
47
+ export declare const describeCondition: (condition: StepCondition) => string;
48
+
49
+ /**
50
+ * Evaluate `condition` against a run context.
51
+ *
52
+ * A condition on a ref the context does not carry evaluates as ABSENT — false
53
+ * for `truthy`/`eq`/`contains`, true for `falsy`/`neq`. That asymmetry is the
54
+ * honest reading of "the producing step did not run", and it is why
55
+ * `validateFlow` refuses a dangling ref at registration: at run time, a typo and
56
+ * a genuinely-skipped producer are indistinguishable here.
57
+ */
58
+ export declare const evaluateCondition: (condition: StepCondition, context: Readonly<Record<string, unknown>>) => boolean;
59
+
40
60
  /** Schedule spec — richer than a single cron expr (weekly interval + anchor,
41
61
  * monthly weekOfMonth), evaluated by the shared `cadenceMatches` predicate. */
42
62
  export declare const FlowCadence: Schema.Struct<{
@@ -178,6 +198,24 @@ export declare const FlowStep: Schema.Struct<{
178
198
  value: typeof Schema.String;
179
199
  label: typeof Schema.String;
180
200
  }>>>;
201
+ /** Gate this step on a prior value. Absent → the step always runs. A step
202
+ * whose condition is false is SKIPPED, not failed: it produces no output, so
203
+ * anything referencing its `outputKey` sees an absent value. */
204
+ when: Schema.optional<Schema.Struct<{
205
+ ref: typeof Schema.String;
206
+ op: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
207
+ value: Schema.optional<typeof Schema.Unknown>;
208
+ }>>;
209
+ /**
210
+ * Fan-out group. CONSECUTIVE steps sharing a group name run CONCURRENTLY,
211
+ * each still journaled as its own durable step.
212
+ *
213
+ * Steps in one group may not reference each other's outputs — they have no
214
+ * order between them — and `validateFlow` refuses a flow that tries. A `human`
215
+ * step may not join a group either: it suspends the whole workflow, which is
216
+ * not a thing one branch of a fan-out can do.
217
+ */
218
+ group: Schema.optional<typeof Schema.String>;
181
219
  /** `human` step only — how long the run parks awaiting this review before it
182
220
  * fails, in milliseconds. Omitted → the flow's `humanTimeoutMs`, then the
183
221
  * engine/plugin default (see `resolveHumanReviewTimeoutMs`). `0` means WAIT
@@ -223,6 +261,8 @@ export declare interface FlowStep {
223
261
  readonly schema?: Json | undefined;
224
262
  readonly reviewMode?: ReviewMode | undefined;
225
263
  readonly options?: ReadonlyArray<ReviewOption> | undefined;
264
+ readonly when?: StepCondition | undefined;
265
+ readonly group?: string | undefined;
226
266
  readonly timeoutMs?: number | undefined;
227
267
  }
228
268
 
@@ -242,6 +282,20 @@ export declare interface HumanResponse {
242
282
  readonly respondedAt?: string | undefined;
243
283
  }
244
284
 
285
+ /**
286
+ * Truthiness, defined once so the engine and the editor cannot disagree.
287
+ *
288
+ * Deliberately NOT JavaScript's: `0` and `''` are falsy in JS, and a step gated
289
+ * on a count or a generated string would then be skipped for a legitimate value.
290
+ * A flow author writing `when: { ref: 'count', op: 'truthy' }` means "there is a
291
+ * count", not "the count is non-zero" — the second is `{ op: 'neq', value: 0 }`,
292
+ * which they can say if they mean it.
293
+ *
294
+ * Absent, null and the empty ARRAY are the falsy set: those are the three
295
+ * shapes a step that produced nothing actually leaves behind.
296
+ */
297
+ export declare const isTruthy: (v: unknown) => boolean;
298
+
245
299
  /** A JSON value (for `params` / `input` / a `structured` step's JSON Schema). */
246
300
  export declare const Json: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
247
301
 
@@ -329,7 +383,7 @@ export declare const RunStep: Schema.Struct<{
329
383
  title: typeof Schema.String;
330
384
  description: Schema.optional<typeof Schema.String>;
331
385
  type: Schema.Literal<["agent", "generate", "structured", "note", "human"]>;
332
- status: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed"]>;
386
+ status: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed", "skipped"]>;
333
387
  startedAt: Schema.optional<typeof Schema.String>;
334
388
  completedAt: Schema.optional<typeof Schema.String>;
335
389
  /** Per-step cost in micro-USD (the app view projects to €/cents). */
@@ -343,6 +397,9 @@ export declare const RunStep: Schema.Struct<{
343
397
  text: typeof Schema.String;
344
398
  }>>;
345
399
  errorMessage: Schema.optional<typeof Schema.String>;
400
+ /** Why a `skipped` step was skipped — the rendered condition, so the timeline
401
+ * says "{{approved}} is not set" instead of leaving a silent gap. */
402
+ skipReason: Schema.optional<typeof Schema.String>;
346
403
  review: Schema.optional<Schema.Struct<{
347
404
  mode: Schema.Literal<["approve", "choice", "text"]>;
348
405
  options: Schema.optional<Schema.Array$<Schema.Struct<{
@@ -371,16 +428,36 @@ export declare interface RunStep {
371
428
  readonly text: string;
372
429
  } | undefined;
373
430
  readonly errorMessage?: string | undefined;
431
+ readonly skipReason?: string | undefined;
374
432
  readonly review?: RunReview | undefined;
375
433
  }
376
434
 
377
- export declare const RunStepStatus: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed"]>;
435
+ export declare const RunStepStatus: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed", "skipped"]>;
378
436
 
379
437
  export declare type RunStepStatus = typeof RunStepStatus.Type;
380
438
 
381
439
  /** Sanitize an outputKey to the `{{ref}}`-safe charset. */
382
440
  export declare const sanitizeOutputKey: (key: string) => string;
383
441
 
442
+ declare const StepCondition: Schema.Struct<{
443
+ /** A brief-field key or an EARLIER step's `outputKey`. */
444
+ ref: typeof Schema.String;
445
+ op: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
446
+ /** The right-hand side for `eq` / `neq` / `contains`. Ignored by the unary ops. */
447
+ value: Schema.optional<typeof Schema.Unknown>;
448
+ }>;
449
+
450
+ /** An INTERFACE, for the same api-report reason as `FlowStep` — it is reached
451
+ * from an exported table column, and an alias to a mapped type gets expanded
452
+ * structurally in an order that is not stable across build scopes. */
453
+ declare interface StepCondition {
454
+ readonly ref: string;
455
+ readonly op: ConditionOp;
456
+ readonly value?: unknown;
457
+ }
458
+ export { StepCondition }
459
+ export { StepCondition as StepConditionType }
460
+
384
461
  /** The five step types. `generate` fans out to four modalities. */
385
462
  export declare const StepType: Schema.Literal<["agent", "generate", "structured", "note", "human"]>;
386
463
 
package/dist/ir.js CHANGED
@@ -1,104 +1,2 @@
1
- import { Schema as e } from "effect";
2
- //#region src/ir.ts
3
- var t = e.Literal("deterministic", "agentic"), n = e.Literal("draft", "active", "archived"), r = e.Literal("private", "organization", "shared"), i = e.Literal("pending", "running", "waiting", "succeeded", "failed", "cancelled"), a = e.Literal("manual", "cron"), o = e.Literal("agent", "generate", "structured", "note", "human"), s = e.Literal("text", "image", "video", "audio"), c = e.Literal("approve", "choice", "text"), l = e.Struct({
4
- value: e.String,
5
- label: e.String
6
- }), u = e.Union(e.Struct({
7
- from: e.Literal("upload"),
8
- url: e.String
9
- }), e.Struct({
10
- from: e.Literal("step"),
11
- ref: e.String
12
- })), d = e.Record({
13
- key: e.String,
14
- value: u
15
- }), f = e.Record({
16
- key: e.String,
17
- value: e.Unknown
18
- }), p = e.Struct({
19
- id: e.String,
20
- title: e.optional(e.String),
21
- description: e.optional(e.String),
22
- type: o,
23
- outputKey: e.optional(e.String),
24
- prompt: e.optional(e.String),
25
- modality: e.optional(s),
26
- model: e.optional(e.String),
27
- params: e.optional(f),
28
- mediaInputs: e.optional(d),
29
- agentRef: e.optional(e.String),
30
- schema: e.optional(f),
31
- reviewMode: e.optional(c),
32
- options: e.optional(e.Array(l)),
33
- timeoutMs: e.optional(e.Number)
34
- }), m = e.Struct({
35
- key: e.String,
36
- label: e.String,
37
- type: e.Literal("text", "textarea", "number", "boolean"),
38
- required: e.optional(e.Boolean),
39
- placeholder: e.optional(e.String)
40
- }), h = e.Struct({
41
- flowRef: e.String,
42
- mappings: e.Array(e.Struct({
43
- targetKey: e.String,
44
- sourceKey: e.String
45
- })),
46
- requireConfirmation: e.optional(e.Boolean)
47
- }), g = e.Struct({
48
- frequency: e.optional(e.Literal("weekly", "monthly")),
49
- repeats: e.Array(e.Number),
50
- intervalWeeks: e.optional(e.Number),
51
- anchorDate: e.optional(e.String),
52
- weekOfMonth: e.optional(e.Union(e.Literal(1, 2, 3, 4), e.Literal("last"))),
53
- hour: e.optional(e.Number),
54
- minute: e.optional(e.Literal(0, 15, 30, 45)),
55
- defaultInput: e.optional(f)
56
- }), _ = e.Literal("pending", "running", "waiting", "succeeded", "failed"), v = e.Struct({
57
- mode: c,
58
- options: e.optional(e.Array(l)),
59
- prompt: e.String
60
- }), y = e.Struct({
61
- id: e.Number,
62
- title: e.String,
63
- description: e.optional(e.String),
64
- type: o,
65
- status: _,
66
- startedAt: e.optional(e.String),
67
- completedAt: e.optional(e.String),
68
- costMicroUsd: e.optional(e.Number),
69
- requestId: e.optional(e.String),
70
- url: e.optional(e.String),
71
- output: e.optional(e.Struct({ text: e.String })),
72
- errorMessage: e.optional(e.String),
73
- review: e.optional(v)
74
- }), b = e.Struct({
75
- decision: e.optional(e.Literal("approve", "reject")),
76
- value: e.optional(e.String),
77
- text: e.optional(e.String),
78
- respondedAt: e.optional(e.String)
79
- }), x = e.Struct({
80
- flowRef: e.String,
81
- input: f,
82
- status: e.Literal("pending", "confirmed", "dismissed")
83
- }), S = 30, C = 1, w = 100, T = (e) => e == null || !Number.isFinite(e) ? 30 : Math.min(100, Math.max(1, Math.trunc(e))), E = (e) => e.replace(/[^\w.-]/g, "_"), D = (e) => {
84
- let t = (e.steps ?? []).map((e) => e.outputKey ? {
85
- ...e,
86
- outputKey: E(e.outputKey)
87
- } : e), n = e.orchestratorModel || e.orchestratorInstructions ? {
88
- ...e.orchestratorModel ? { model: e.orchestratorModel } : {},
89
- ...e.orchestratorInstructions ? { instructions: e.orchestratorInstructions } : {}
90
- } : void 0;
91
- return {
92
- name: e.name,
93
- mode: e.mode ?? "deterministic",
94
- steps: t,
95
- maxSteps: T(e.maxSteps),
96
- brief: e.brief ?? [],
97
- ...n ? { orchestrator: n } : {},
98
- ...e.chain ? { chain: e.chain } : {},
99
- ...e.cadence ? { cadence: e.cadence } : {},
100
- ...e.humanTimeoutMs != null && Number.isFinite(e.humanTimeoutMs) ? { humanTimeoutMs: e.humanTimeoutMs } : {}
101
- };
102
- };
103
- //#endregion
104
- export { m as BriefField, x as ChainPending, g as FlowCadence, h as FlowChain, t as FlowMode, n as FlowStatus, p as FlowStep, b as HumanResponse, f as Json, S as MAX_STEPS_DEFAULT, w as MAX_STEPS_MAX, C as MAX_STEPS_MIN, u as MediaInputSlot, d as MediaInputs, s as Modality, c as ReviewMode, l as ReviewOption, v as RunReview, a as RunSource, i as RunStatus, y as RunStep, _ as RunStepStatus, o as StepType, r as Visibility, T as clampMaxSteps, D as normalizeFlow, E as sanitizeOutputKey };
1
+ import { A as e, C as t, D as n, E as r, M as i, O as a, S as o, T as s, _ as c, a as l, b as u, c as d, d as f, f as p, g as m, h, i as g, j as _, k as v, l as y, m as b, n as x, o as S, p as C, r as w, s as T, t as E, u as D, v as O, w as k, x as A, y as j } from "./ir-BOv5uwYI.js";
2
+ export { E as BriefField, x as ChainPending, a as ConditionOp, w as FlowCadence, g as FlowChain, l as FlowMode, S as FlowStatus, T as FlowStep, d as HumanResponse, y as Json, D as MAX_STEPS_DEFAULT, f as MAX_STEPS_MAX, p as MAX_STEPS_MIN, C as MediaInputSlot, b as MediaInputs, h as Modality, m as ReviewMode, c as ReviewOption, O as RunReview, j as RunSource, u as RunStatus, A as RunStep, o as RunStepStatus, v as StepCondition, t as StepType, k as Visibility, s as clampMaxSteps, e as describeCondition, _ as evaluateCondition, i as isTruthy, r as normalizeFlow, n as sanitizeOutputKey };
package/dist/web.d.ts CHANGED
@@ -28,6 +28,11 @@ export declare interface BriefField {
28
28
  */
29
29
  export declare const cadenceMatches: (cadence: FlowCadence, now: Date) => boolean;
30
30
 
31
+ /** The comparison a `when:` performs. */
32
+ declare const ConditionOp: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
33
+
34
+ declare type ConditionOp = typeof ConditionOp.Type;
35
+
31
36
  export declare const DEFAULT_FLOW_RPC_TAGS: FlowRpcTags;
32
37
 
33
38
  /** Schedule spec — richer than a single cron expr (weekly interval + anchor,
@@ -193,6 +198,24 @@ export declare const FlowStep: Schema.Struct<{
193
198
  value: typeof Schema.String;
194
199
  label: typeof Schema.String;
195
200
  }>>>;
201
+ /** Gate this step on a prior value. Absent → the step always runs. A step
202
+ * whose condition is false is SKIPPED, not failed: it produces no output, so
203
+ * anything referencing its `outputKey` sees an absent value. */
204
+ when: Schema.optional<Schema.Struct<{
205
+ ref: typeof Schema.String;
206
+ op: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
207
+ value: Schema.optional<typeof Schema.Unknown>;
208
+ }>>;
209
+ /**
210
+ * Fan-out group. CONSECUTIVE steps sharing a group name run CONCURRENTLY,
211
+ * each still journaled as its own durable step.
212
+ *
213
+ * Steps in one group may not reference each other's outputs — they have no
214
+ * order between them — and `validateFlow` refuses a flow that tries. A `human`
215
+ * step may not join a group either: it suspends the whole workflow, which is
216
+ * not a thing one branch of a fan-out can do.
217
+ */
218
+ group: Schema.optional<typeof Schema.String>;
196
219
  /** `human` step only — how long the run parks awaiting this review before it
197
220
  * fails, in milliseconds. Omitted → the flow's `humanTimeoutMs`, then the
198
221
  * engine/plugin default (see `resolveHumanReviewTimeoutMs`). `0` means WAIT
@@ -238,6 +261,8 @@ export declare interface FlowStep {
238
261
  readonly schema?: Json | undefined;
239
262
  readonly reviewMode?: ReviewMode | undefined;
240
263
  readonly options?: ReadonlyArray<ReviewOption> | undefined;
264
+ readonly when?: StepCondition | undefined;
265
+ readonly group?: string | undefined;
241
266
  readonly timeoutMs?: number | undefined;
242
267
  }
243
268
 
@@ -330,7 +355,7 @@ export declare const RunStep: Schema.Struct<{
330
355
  title: typeof Schema.String;
331
356
  description: Schema.optional<typeof Schema.String>;
332
357
  type: Schema.Literal<["agent", "generate", "structured", "note", "human"]>;
333
- status: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed"]>;
358
+ status: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed", "skipped"]>;
334
359
  startedAt: Schema.optional<typeof Schema.String>;
335
360
  completedAt: Schema.optional<typeof Schema.String>;
336
361
  /** Per-step cost in micro-USD (the app view projects to €/cents). */
@@ -344,6 +369,9 @@ export declare const RunStep: Schema.Struct<{
344
369
  text: typeof Schema.String;
345
370
  }>>;
346
371
  errorMessage: Schema.optional<typeof Schema.String>;
372
+ /** Why a `skipped` step was skipped — the rendered condition, so the timeline
373
+ * says "{{approved}} is not set" instead of leaving a silent gap. */
374
+ skipReason: Schema.optional<typeof Schema.String>;
347
375
  review: Schema.optional<Schema.Struct<{
348
376
  mode: Schema.Literal<["approve", "choice", "text"]>;
349
377
  options: Schema.optional<Schema.Array$<Schema.Struct<{
@@ -372,13 +400,31 @@ export declare interface RunStep {
372
400
  readonly text: string;
373
401
  } | undefined;
374
402
  readonly errorMessage?: string | undefined;
403
+ readonly skipReason?: string | undefined;
375
404
  readonly review?: RunReview | undefined;
376
405
  }
377
406
 
378
- declare const RunStepStatus: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed"]>;
407
+ declare const RunStepStatus: Schema.Literal<["pending", "running", "waiting", "succeeded", "failed", "skipped"]>;
379
408
 
380
409
  declare type RunStepStatus = typeof RunStepStatus.Type;
381
410
 
411
+ declare const StepCondition: Schema.Struct<{
412
+ /** A brief-field key or an EARLIER step's `outputKey`. */
413
+ ref: typeof Schema.String;
414
+ op: Schema.Literal<["truthy", "falsy", "eq", "neq", "contains"]>;
415
+ /** The right-hand side for `eq` / `neq` / `contains`. Ignored by the unary ops. */
416
+ value: Schema.optional<typeof Schema.Unknown>;
417
+ }>;
418
+
419
+ /** An INTERFACE, for the same api-report reason as `FlowStep` — it is reached
420
+ * from an exported table column, and an alias to a mapped type gets expanded
421
+ * structurally in an order that is not stable across build scopes. */
422
+ declare interface StepCondition {
423
+ readonly ref: string;
424
+ readonly op: ConditionOp;
425
+ readonly value?: unknown;
426
+ }
427
+
382
428
  /** The five step types. `generate` fans out to four modalities. */
383
429
  export declare const StepType: Schema.Literal<["agent", "generate", "structured", "note", "human"]>;
384
430
 
@@ -12,6 +12,7 @@ export declare interface FlowRunPayload {
12
12
  readonly input: Record<string, unknown>;
13
13
  readonly source: 'manual' | 'cron';
14
14
  readonly requestId: string;
15
+ readonly chainPath?: ReadonlyArray<string> | undefined;
15
16
  }
16
17
 
17
18
  /**
@@ -30,6 +31,19 @@ export declare const flowRunWorkflow: Workflow<"flow.run", Schema.Struct<{
30
31
  source: Schema.Literal<["manual", "cron"]>;
31
32
  /** Correlation + idempotency key. */
32
33
  requestId: typeof Schema.String;
34
+ /**
35
+ * The chain of flows that led here, oldest first, INCLUDING this one.
36
+ *
37
+ * `chainTo` had a self-chain guard and nothing else, so A→B→A was an
38
+ * unbounded loop: each hop started a fresh child workflow with a fresh
39
+ * `requestId`, so idempotency never collapsed it and nothing counted the
40
+ * hops. The framework would happily run it until an operator noticed the
41
+ * bill.
42
+ *
43
+ * Optional so a run started by an older caller (or by hand) still decodes;
44
+ * an absent path is read as "this is the first flow", which it is.
45
+ */
46
+ chainPath: Schema.optional<Schema.Array$<typeof Schema.String>>;
33
47
  }>, Schema.Struct<{
34
48
  runId: typeof Schema.String;
35
49
  status: Schema.Literal<["succeeded", "failed", "cancelled", "waiting"]>;
package/dist/workflow.js CHANGED
@@ -11,7 +11,8 @@ var n = "flow.run", r = "flow-human-response", i = (e) => `${r}:${e}`, a = t({
11
11
  value: e.Unknown
12
12
  }),
13
13
  source: e.Literal("manual", "cron"),
14
- requestId: e.String
14
+ requestId: e.String,
15
+ chainPath: e.optional(e.Array(e.String))
15
16
  },
16
17
  success: e.Struct({
17
18
  runId: e.String,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-ai-flows",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "AI Flows — durable multi-step AI pipelines (deterministic + agentic) with human-in-the-loop, chaining, and cadence. Author flows in code (defineFlow) or as data (visual editor rows); one engine runs both on @voltro/workflow durability, @voltro/ai generation, storage artifacts, and notifications.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -58,16 +58,16 @@
58
58
  "node": ">=24.0.0"
59
59
  },
60
60
  "dependencies": {
61
- "@voltro/ai": "0.34.0",
62
- "@voltro/client": "0.34.0",
63
- "@voltro/database": "0.34.0",
64
- "@voltro/env": "0.34.0",
65
- "@voltro/plugin-audit": "0.34.0",
66
- "@voltro/plugin-multitenancy": "0.34.0",
67
- "@voltro/plugin-soft-delete": "0.34.0",
68
- "@voltro/protocol": "0.34.0",
69
- "@voltro/runtime": "0.34.0",
70
- "@voltro/workflow": "0.34.0"
61
+ "@voltro/ai": "0.36.0",
62
+ "@voltro/client": "0.36.0",
63
+ "@voltro/database": "0.36.0",
64
+ "@voltro/env": "0.36.0",
65
+ "@voltro/plugin-audit": "0.36.0",
66
+ "@voltro/plugin-multitenancy": "0.36.0",
67
+ "@voltro/plugin-soft-delete": "0.36.0",
68
+ "@voltro/protocol": "0.36.0",
69
+ "@voltro/runtime": "0.36.0",
70
+ "@voltro/workflow": "0.36.0"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "effect": "^3.22.0"