@voltro/plugin-ai-flows 0.85.0 → 0.87.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.
@@ -37,4 +37,4 @@ var e = (e) => (e.getDay() + 6) % 7, t = (e) => Math.ceil(e.getDate() / 7), n =
37
37
  return r;
38
38
  };
39
39
  //#endregion
40
- export { o as n, a as t };
40
+ export { a as cadenceMatches, o as nextRuns };
@@ -0,0 +1,80 @@
1
+ import { listRegisteredFlows as e } from "./registry.js";
2
+ import { flowRunWorkflow as t } from "./workflow.js";
3
+ import { reconcileFlowStarts as n } from "./startReconcile.js";
4
+ import { cadenceMatches as r } from "./cadence.js";
5
+ import { recordFlowStartReceipt as i } from "./startReceipt.js";
6
+ import { Schema as a } from "effect";
7
+ import { createHash as o } from "node:crypto";
8
+ //#region src/cadenceTick.ts
9
+ var s = (e, t, n) => {
10
+ if (!Number.isFinite(t.getTime())) throw Error("cadence timestamp must be valid");
11
+ return `cron-${o("sha256").update(JSON.stringify([
12
+ n,
13
+ e,
14
+ Math.floor(t.getTime() / 6e4)
15
+ ])).digest("hex")}`;
16
+ }, c = async (t) => {
17
+ let n = [];
18
+ for (let r of e()) r.cadence && (r.cadence.repeats?.length ?? 0) > 0 && n.push({
19
+ tenantId: t.request.subject.tenantId,
20
+ flowRef: r.name,
21
+ cadence: r.cadence,
22
+ defaultInput: r.cadence.defaultInput ?? {}
23
+ });
24
+ let r = await t.store.select("_voltro_ai_flows").where("isEnabled", !0).all();
25
+ for (let e of r) e.id && e.cadence && (e.cadence.repeats?.length ?? 0) > 0 && n.push({
26
+ tenantId: e.tenantId ?? t.request.subject.tenantId,
27
+ flowRef: e.id,
28
+ cadence: e.cadence,
29
+ defaultInput: e.cadence.defaultInput ?? {}
30
+ });
31
+ return n;
32
+ }, l = async (e, o = /* @__PURE__ */ new Date()) => {
33
+ let l = [], u = 0, d = [], f = e.workflows ? await n(e) : {
34
+ examined: 0,
35
+ accepted: 0,
36
+ notExecuted: 0,
37
+ pending: 0,
38
+ failures: []
39
+ };
40
+ d.push(...f.failures.map((e) => Error(`flow run ${e.runId}: start reconciliation failed`, { cause: e.cause })));
41
+ let p = await c(e);
42
+ for (let n of p) {
43
+ if (!r(n.cadence, o)) continue;
44
+ u += 1;
45
+ let c = s(n.flowRef, o, n.tenantId), f = `run-${c}`;
46
+ try {
47
+ if (!e.workflows) throw Error("workflow runtime unavailable; no cadence start was submitted");
48
+ (await e.transaction(async (e) => {
49
+ let r = e.workflows;
50
+ if (!r) throw Error("workflow runtime unavailable in transaction; no cadence start was submitted");
51
+ await e.store.insertIgnoreWithOutcome("_voltro_ai_flow_runs", {
52
+ id: f,
53
+ flowRef: n.flowRef,
54
+ flowName: n.flowRef,
55
+ status: "pending",
56
+ source: "cron",
57
+ requestId: c,
58
+ input: n.defaultInput
59
+ }, { conflictColumns: ["id"] });
60
+ let o = await e.store.select("_voltro_ai_flow_runs").where("id", f).first();
61
+ if (!o) throw Error("cadence run is not readable after persistence");
62
+ let s = a.decodeUnknownSync(t.payloadSchema.pipe(a.check(a.makeFilter((e) => e.runId === f && e.flowRef === n.flowRef && e.requestId === c && e.source === "cron"))))({
63
+ ...o,
64
+ runId: o.id
65
+ });
66
+ return i(e, f, await r.start("flow.run", s));
67
+ }, n.tenantId === null ? void 0 : { tenantId: n.tenantId })).status === "running" && l.push(c);
68
+ } catch (e) {
69
+ d.push(e);
70
+ }
71
+ }
72
+ if (d.length > 0) throw AggregateError(d, `${d.length} cadence submission(s) failed`);
73
+ return {
74
+ matched: u,
75
+ started: l,
76
+ reconciled: f
77
+ };
78
+ };
79
+ //#endregion
80
+ export { s as cronRequestId, l as runCadenceTick };
@@ -0,0 +1,46 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/condition.ts
3
+ var t = e.Literals([
4
+ "truthy",
5
+ "falsy",
6
+ "eq",
7
+ "neq",
8
+ "contains"
9
+ ]), n = e.Struct({
10
+ ref: e.String,
11
+ op: t,
12
+ value: e.optional(e.Unknown)
13
+ }), r = (e) => e == null ? !1 : typeof e == "boolean" ? e : !Array.isArray(e) || e.length > 0, i = (e, t) => {
14
+ if (e === t) return !0;
15
+ if (e === null || t === null || e === void 0 || t === void 0) return !1;
16
+ if (typeof e == "object" || typeof t == "object") try {
17
+ return JSON.stringify(e) === JSON.stringify(t);
18
+ } catch {
19
+ return !1;
20
+ }
21
+ return String(e) === String(t);
22
+ }, a = (e, t) => {
23
+ let n = t[e.ref];
24
+ switch (e.op) {
25
+ case "truthy": return r(n);
26
+ case "falsy": return !r(n);
27
+ case "eq": return i(n, e.value);
28
+ case "neq": return !i(n, e.value);
29
+ case "contains": {
30
+ if (n == null) return !1;
31
+ if (Array.isArray(n)) return n.some((t) => i(t, e.value));
32
+ let t = e.value;
33
+ return typeof t != "string" && typeof t != "number" ? !1 : String(n).includes(String(t));
34
+ }
35
+ }
36
+ }, o = (e) => {
37
+ switch (e.op) {
38
+ case "truthy": return `{{${e.ref}}} is set`;
39
+ case "falsy": return `{{${e.ref}}} is not set`;
40
+ case "eq": return `{{${e.ref}}} equals ${JSON.stringify(e.value)}`;
41
+ case "neq": return `{{${e.ref}}} does not equal ${JSON.stringify(e.value)}`;
42
+ case "contains": return `{{${e.ref}}} contains ${JSON.stringify(e.value)}`;
43
+ }
44
+ };
45
+ //#endregion
46
+ export { t as ConditionOp, n as StepCondition, o as describeCondition, a as evaluateCondition, r as isTruthy };
@@ -0,0 +1,18 @@
1
+ //#region src/defaults.ts
2
+ var e = 168, t = "VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS", n = 2160, r = "VOLTRO_AI_FLOW_RUNS_TTL_HOURS", i = 5, a = "VOLTRO_AI_FLOW_MAX_CHAIN_DEPTH", o = { current: {} }, s = (e) => {
3
+ o.current = e;
4
+ }, c = () => o.current, l = () => {
5
+ o.current = {};
6
+ }, u = (e, t) => {
7
+ if (e === void 0 || e.trim() === "") return t * 36e5;
8
+ let n = Number(e);
9
+ return !Number.isFinite(n) || n < 0 ? t * 36e5 : n * 36e5;
10
+ }, d = (e) => Number.isFinite(e) && e > 0 ? e : Infinity, f = (e) => {
11
+ let t = e.env ?? process.env;
12
+ return d(e.step?.timeoutMs ?? e.flow?.humanTimeoutMs ?? e.deps?.humanReviewTimeoutMs ?? c().humanReviewTimeoutMs ?? u(t.VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS, 168));
13
+ }, p = (e) => {
14
+ let t = e?.env ?? process.env, n = Number(t[a]), r = e?.maxChainDepth ?? c().maxChainDepth ?? (Number.isFinite(n) && n >= 1 ? n : 5);
15
+ return Number.isFinite(r) && r >= 1 ? r : 5;
16
+ };
17
+ //#endregion
18
+ export { r as FLOW_RUNS_TTL_ENV, n as FLOW_RUNS_TTL_HOURS_DEFAULT, t as HUMAN_REVIEW_TIMEOUT_ENV, e as HUMAN_REVIEW_TIMEOUT_HOURS_DEFAULT, i as MAX_CHAIN_DEPTH_DEFAULT, a as MAX_CHAIN_DEPTH_ENV, c as getAiFlowsDefaults, u as msFromHoursEnv, l as resetAiFlowsDefaults, f as resolveHumanReviewTimeoutMs, p as resolveMaxChainDepth, s as setAiFlowsDefaults, d as toTimeoutMs };
package/dist/define.js ADDED
@@ -0,0 +1,71 @@
1
+ import { registerFlow as e } from "./registry.js";
2
+ import { normalizeFlow as t, sanitizeOutputKey as n } from "./ir.js";
3
+ import { collectRefs as r } from "./interpolate.js";
4
+ import { flowSegments as i, nonContiguousGroups as a } from "./segments.js";
5
+ import { FlowValidationError as o } from "./errors.js";
6
+ //#region src/define.ts
7
+ var s = {
8
+ text: (e) => ({
9
+ type: "generate",
10
+ modality: "text",
11
+ ...e
12
+ }),
13
+ media: (e, t) => ({
14
+ type: "generate",
15
+ modality: e,
16
+ ...t
17
+ }),
18
+ agent: (e) => ({
19
+ type: "agent",
20
+ ...e
21
+ }),
22
+ structured: (e) => ({
23
+ type: "structured",
24
+ ...e
25
+ }),
26
+ note: (e) => ({
27
+ type: "note",
28
+ ...e
29
+ }),
30
+ human: (e) => ({
31
+ type: "human",
32
+ ...e
33
+ })
34
+ }, c = (e) => e.map((e, t) => "id" in e && e.id ? e : {
35
+ id: `step-${t}`,
36
+ ...e
37
+ }), l = (e) => {
38
+ let t = [], s = i(e.steps);
39
+ for (let e of a(s)) t.push(`group "${e}" is not contiguous — only CONSECUTIVE steps sharing a group run concurrently, so this would execute as two separate fan-outs`);
40
+ let c = new Set(e.brief.map((e) => e.key));
41
+ for (let e of s) {
42
+ for (let { step: n } of e.steps) {
43
+ n.type === "human" && n.group !== void 0 && t.push(`step "${n.id}" is a human review inside group "${n.group}" — a review SUSPENDS the whole run, which one branch of a fan-out cannot do`);
44
+ for (let i of r(n.prompt ?? "")) c.has(i) || t.push(e.steps.length > 1 ? `step "${n.id}" references {{${i}}} which is produced inside its OWN group "${n.group}" or does not exist — grouped steps run concurrently, so they cannot read each other` : `step "${n.id}" references {{${i}}} which is not a brief field or a prior step outputKey`);
45
+ for (let [e, r] of Object.entries(n.mediaInputs ?? {})) r.from === "step" && !c.has(r.ref) && t.push(`step "${n.id}" media input "${e}" references "${r.ref}" which is not a brief field or an earlier segment's outputKey`);
46
+ n.when !== void 0 && !c.has(n.when.ref) && t.push(`step "${n.id}" has a when-condition on {{${n.when.ref}}} which is not a brief field or a prior step outputKey — it would never run`);
47
+ }
48
+ for (let { step: t } of e.steps) t.outputKey && c.add(n(t.outputKey));
49
+ }
50
+ if (t.length > 0) throw new o({
51
+ flowRef: e.name,
52
+ message: `flow "${e.name}" has ${t.length} problem(s)`,
53
+ issues: t
54
+ });
55
+ }, u = (n) => {
56
+ let r = t({
57
+ name: n.name,
58
+ mode: n.mode,
59
+ steps: c(n.steps),
60
+ maxSteps: n.maxSteps,
61
+ brief: n.brief,
62
+ orchestratorModel: n.orchestrator?.model,
63
+ orchestratorInstructions: n.orchestrator?.instructions,
64
+ chain: n.chain,
65
+ cadence: n.cadence,
66
+ humanTimeoutMs: n.humanTimeoutMs
67
+ });
68
+ return l(r), e(r), r;
69
+ };
70
+ //#endregion
71
+ export { u as defineFlow, s as flowStep, l as validateFlow };