@voltro/plugin-ai-flows 0.71.0 → 0.72.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.
package/dist/errors.js CHANGED
@@ -28,6 +28,11 @@ var t = class extends e.TaggedError()("FlowNotFound", {
28
28
  }) {}, c = class extends e.TaggedError()("FlowCapabilityMissing", {
29
29
  capability: e.String,
30
30
  message: e.String
31
+ }) {}, l = class extends e.TaggedError()("FlowAgentUnavailable", {
32
+ agentRef: e.String,
33
+ reason: e.Literal("not-found", "access-revoked", "model-unusable"),
34
+ message: e.String,
35
+ publicMessage: e.optional(e.String)
31
36
  }) {};
32
37
  //#endregion
33
- export { a as FlowBriefIncomplete, c as FlowCapabilityMissing, i as FlowDependencyError, o as FlowNotAwaitingResponse, t as FlowNotFound, s as FlowReviewDeliveryError, n as FlowRunNotFound, r as FlowValidationError };
38
+ export { l as FlowAgentUnavailable, a as FlowBriefIncomplete, c as FlowCapabilityMissing, i as FlowDependencyError, o as FlowNotAwaitingResponse, t as FlowNotFound, s as FlowReviewDeliveryError, n as FlowRunNotFound, r as FlowValidationError };
package/dist/index.d.ts CHANGED
@@ -12,6 +12,11 @@ import { TableLike } from '@voltro/database';
12
12
  import { VoltroPlugin } from '@voltro/protocol';
13
13
  import { WorkflowEngine } from '@effect/workflow/WorkflowEngine';
14
14
  import { WorkflowInstance } from '@effect/workflow/WorkflowEngine';
15
+ import { WorkflowRunHandle } from '@voltro/protocol';
16
+
17
+ /** The handle a flow row records for an ADMITTED start: the execution the
18
+ * engine owns. `id` is that execution too — the stable handle callers pass on. */
19
+ export declare const admittedStartHandle: (executionId: string) => WorkflowRunHandle;
15
20
 
16
21
  /** An `agent` step's resolved persona. */
17
22
  export declare interface AgentResolution {
@@ -76,6 +81,8 @@ export declare const aiFlowRunsTable: Table<"_voltro_ai_flow_runs", FieldDefinit
76
81
  readonly chainRefusal: ColumnBuilder<string | null, "text", boolean>;
77
82
  /** Accumulated cost in micro-USD (app view projects to €/cents). */
78
83
  readonly costMicroUsd: ColumnBuilder<number, "integer", true>;
84
+ /** `actual` | `estimated` | `incomplete` | `unknown` — whether `costMicroUsd` can be booked as spend, or only read as a floor. */
85
+ readonly costCompleteness: ColumnBuilder<string, "text", true>;
79
86
  readonly durationMs: ColumnBuilder<number | null, "integer", boolean>;
80
87
  readonly error: ColumnBuilder<string | null, "text", boolean>;
81
88
  readonly errorMessage: ColumnBuilder<string | null, "text", boolean>;
@@ -285,13 +292,25 @@ export declare const buildFlowRunExecute: (ctx: AppContext, deps?: EngineDeps) =
285
292
  */
286
293
  export declare const cadenceMatches: (cadence: FlowCadence, now: Date) => boolean;
287
294
 
288
- /** Cooperatively cancel a run: flip the row → the engine aborts before its next
289
- * step (it can't interrupt an in-flight generation). */
295
+ /**
296
+ * Cancel a run on BOTH sides in one operation: the row (what the UI and the
297
+ * cadence read) and the workflow execution (what actually runs, or waits at a
298
+ * human step). Either half alone drifts — a row marked cancelled over a
299
+ * suspended execution, or a cancelled execution under a row that still says
300
+ * `waiting` — and a reader of one side never learns about the other.
301
+ *
302
+ * The row is patched first, so a cooperative check between steps sees it even
303
+ * if the engine's interrupt is slow; the execution's own interrupt handler
304
+ * (`buildFlowRunExecute`) writes the same terminal fields again, harmlessly.
305
+ * An execution that cannot be reached (no runtime, not started) leaves the row
306
+ * cancelled and says so in `engine`.
307
+ */
290
308
  export declare const cancelFlow: (ctx: AppContext, args: {
291
309
  runId: string;
292
310
  }) => Effect.Effect<{
293
311
  runId: string;
294
312
  status: RunStatus;
313
+ engine: "unreachable" | "cancelled";
295
314
  }, FlowRunNotFound, never>;
296
315
 
297
316
  /** Staged follow-up when `chainTo.requireConfirmation` is set. */
@@ -321,6 +340,16 @@ export declare const ConditionOp: Schema.Literal<["truthy", "falsy", "eq", "neq"
321
340
 
322
341
  export declare type ConditionOp = typeof ConditionOp.Type;
323
342
 
343
+ /** A run's cost verdict over its steps: every step gateway-priced → `actual`; none unpriced → `estimated`; any unpriced → `incomplete`; no priced step → `unknown`. */
344
+ export declare const CostCompleteness: Schema.Literal<["actual", "estimated", "incomplete", "unknown"]>;
345
+
346
+ export declare type CostCompleteness = typeof CostCompleteness.Type;
347
+
348
+ /** Where a cost figure came from. `unpriced` means the number beside it is a placeholder, not a price. */
349
+ export declare const CostSource: Schema.Literal<["gateway", "estimated", "unpriced"]>;
350
+
351
+ export declare type CostSource = typeof CostSource.Type;
352
+
324
353
  /** Create a flow row. Generates an id when absent; tenant/audit columns are
325
354
  * auto-stamped by the runtime. */
326
355
  export declare const createFlow: (ctx: AppContext, fields: Record<string, unknown>) => Effect.Effect<Readonly<Record<string, unknown>>, never, never>;
@@ -364,13 +393,27 @@ export declare const describeCondition: (condition: StepCondition) => string;
364
393
  export declare interface EngineDeps {
365
394
  /** Resolve an agent in the durable run's tenant, in deterministic steps AND
366
395
  * the agentic `run_agent` tool. Do not infer tenancy from the current subject:
367
- * a crash resume may run as system. Absent → FlowCapabilityMissing. */
368
- readonly resolveAgent?: (agentRef: string, run: MediaRunContext) => Effect.Effect<AgentResolution, FlowNotFound, unknown>;
396
+ * a crash resume may run as system. Absent → FlowCapabilityMissing.
397
+ *
398
+ * Refuse with `FlowAgentUnavailable` for anything that is not "no such
399
+ * agent" — revoked access, a model the host cannot serve. Its `message` is
400
+ * for the HOST (the step's error, the run's log); what the MODEL sees in the
401
+ * agentic planner is `publicMessage`, or a generic "agent unavailable" — a
402
+ * resolver's words are tool output, so never say anything private in
403
+ * `publicMessage`. */
404
+ readonly resolveAgent?: (agentRef: string, run: FlowRunContext) => Effect.Effect<AgentResolution, FlowNotFound | FlowAgentUnavailable, unknown>;
369
405
  /** Additional tools for the agentic planner, resolved inside its durable step.
370
406
  * The original flow reference and durable tenant are scope, not authorization.
371
407
  * Enforce current access in this callback and the tools. Internal planner names
372
- * cannot be replaced. Deterministic agent tools belong to AgentResolution. */
373
- readonly resolveTools?: (flowRef: string, run: MediaRunContext) => Effect.Effect<Record<string, AnyTool>, FlowCapabilityMissing, unknown>;
408
+ * cannot be replaced. Deterministic agent tools belong to AgentResolution.
409
+ * `planner.provider()` is the provider the planner will run on — a host that
410
+ * offers a provider-native search (web search on OpenAI, Google, Anthropic)
411
+ * picks the matching tool from it. A thunk: resolved only when asked, so a
412
+ * resolver that fails never resolves the model. A failure here fails the planner with
413
+ * the resolver's own message. */
414
+ readonly resolveTools?: (flowRef: string, run: FlowRunContext, planner: {
415
+ readonly provider: () => ProviderConfig | undefined;
416
+ }) => Effect.Effect<Record<string, AnyTool>, FlowCapabilityMissing, unknown>;
374
417
  /** Media generator for image/video/audio steps (`makeMediaGenerator` in
375
418
  * `mediaAdapter.ts` is the @voltro/ai-backed implementation). */
376
419
  readonly generateMedia?: MediaGenerator;
@@ -425,6 +468,24 @@ export declare const FLOW_RUNS_TTL_ENV = "VOLTRO_AI_FLOW_RUNS_TTL_HOURS";
425
468
  * runs. Only TERMINAL runs are swept — see `index.ts`. */
426
469
  export declare const FLOW_RUNS_TTL_HOURS_DEFAULT: number;
427
470
 
471
+ /**
472
+ * An agent the host will not serve for this run: access revoked, a model it
473
+ * cannot run, or nothing under that reference. `message` is for the host —
474
+ * the step's error and the run's log. `publicMessage` is the ONLY sentence
475
+ * the agentic planner's model sees; leave it out for a generic line.
476
+ */
477
+ declare class FlowAgentUnavailable extends FlowAgentUnavailable_base {
478
+ }
479
+
480
+ declare const FlowAgentUnavailable_base: Schema.TaggedErrorClass<FlowAgentUnavailable, "FlowAgentUnavailable", {
481
+ readonly _tag: Schema.tag<"FlowAgentUnavailable">;
482
+ } & {
483
+ agentRef: typeof Schema.String;
484
+ reason: Schema.Literal<["not-found", "access-revoked", "model-unusable"]>;
485
+ message: typeof Schema.String;
486
+ publicMessage: Schema.optional<typeof Schema.String>;
487
+ }>;
488
+
428
489
  /** A required brief field was blank at launch. */
429
490
  declare class FlowBriefIncomplete extends FlowBriefIncomplete_base {
430
491
  }
@@ -618,6 +679,24 @@ export declare const FlowReviewReceipt: Schema.Struct<{
618
679
 
619
680
  export declare type FlowReviewReceipt = typeof FlowReviewReceipt.Type;
620
681
 
682
+ /** The run an agent or media step executes within, resolved from the durable RUN ROW —
683
+ * not the caller subject. This is the difference between a normal call and a
684
+ * resume: a BOOTSTRAP/crash resume runs under a tenant-less `SYSTEM_SUBJECT`,
685
+ * so a host that persists artifacts per tenant cannot read the tenant from
686
+ * `ctx.request.subject` (it isn't there) and would either fail closed or, worse
687
+ * under the old fallback, write into the wrong tenant. The engine reads the run
688
+ * row it already loads and hands the original tenant down here, so persistence
689
+ * continues in the right place on replay. */
690
+ export declare interface FlowRunContext {
691
+ readonly runId: string;
692
+ /** The run's tenant as stamped when it was created, or `null` for a
693
+ * single-tenant app. Authoritative on resume, where the subject has none. */
694
+ readonly tenantId: string | null;
695
+ /** The run's owner as stored on its row, or `null`. Every host used to load
696
+ * the row again for this; a cadence run has none. */
697
+ readonly ownerId: string | null;
698
+ }
699
+
621
700
  /** A flow run row could not be found. */
622
701
  declare class FlowRunNotFound extends FlowRunNotFound_base {
623
702
  }
@@ -684,6 +763,22 @@ export declare const FlowStartReceipt: Schema.Struct<{
684
763
 
685
764
  export declare type FlowStartReceipt = typeof FlowStartReceipt.Type;
686
765
 
766
+ export declare interface FlowStartReconciliation {
767
+ /** Rows read with a queued handle. */
768
+ readonly examined: number;
769
+ /** Rows now `running` under the execution the engine accepted. */
770
+ readonly accepted: number;
771
+ /** Rows now `failed` because their start will never run. */
772
+ readonly notExecuted: number;
773
+ /** Rows still queued. */
774
+ readonly pending: number;
775
+ /** Per-row read or write failures; the other rows were still reconciled. */
776
+ readonly failures: ReadonlyArray<{
777
+ readonly runId: string;
778
+ readonly cause: unknown;
779
+ }>;
780
+ }
781
+
687
782
  /** Flow-definition lifecycle. */
688
783
  export declare const FlowStatus: Schema.Literal<["draft", "active", "archived"]>;
689
784
 
@@ -711,6 +806,7 @@ export declare const FlowStep: Schema.Struct<{
711
806
  mediaInputs: Schema.optional<Schema.Record$<typeof Schema.String, Schema.Union<[Schema.Struct<{
712
807
  from: Schema.Literal<["upload"]>;
713
808
  url: typeof Schema.String;
809
+ mediaType: Schema.optional<typeof Schema.NonEmptyString>;
714
810
  }>, Schema.Struct<{
715
811
  from: Schema.Literal<["step"]>;
716
812
  ref: typeof Schema.String;
@@ -1038,14 +1134,21 @@ export declare type MediaGenerator = (args: {
1038
1134
  readonly prompt: string;
1039
1135
  readonly params?: Record<string, unknown>;
1040
1136
  readonly inputs?: ResolvedMediaInputs;
1137
+ /** The same slots as TYPED references, for the models that require one
1138
+ * (`inputReferences: [{ type: 'url', url, mediaType }]`): an upload that
1139
+ * declared its `mediaType`, and a prior step's artifact, whose type the
1140
+ * engine already knows. A slot without a known type is in `inputs` only —
1141
+ * a type is never guessed from a URL. */
1142
+ readonly inputRefs?: ResolvedMediaInputRefs;
1041
1143
  /** The run this artifact belongs to — pin persistence to `run.tenantId` rather
1042
1144
  * than the current subject, so a system/bootstrap resume stores it correctly. */
1043
- readonly run: MediaRunContext;
1145
+ readonly run: FlowRunContext;
1044
1146
  }) => Effect.Effect<MediaResult, FlowCapabilityMissing, unknown>;
1045
1147
 
1046
1148
  export declare const MediaInputs: Schema.Record$<typeof Schema.String, Schema.Union<[Schema.Struct<{
1047
1149
  from: Schema.Literal<["upload"]>;
1048
1150
  url: typeof Schema.String;
1151
+ mediaType: Schema.optional<typeof Schema.NonEmptyString>;
1049
1152
  }>, Schema.Struct<{
1050
1153
  from: Schema.Literal<["step"]>;
1051
1154
  ref: typeof Schema.String;
@@ -1060,6 +1163,7 @@ export declare type MediaInputs = typeof MediaInputs.Type;
1060
1163
  export declare const MediaInputSlot: Schema.Union<[Schema.Struct<{
1061
1164
  from: Schema.Literal<["upload"]>;
1062
1165
  url: typeof Schema.String;
1166
+ mediaType: Schema.optional<typeof Schema.NonEmptyString>;
1063
1167
  }>, Schema.Struct<{
1064
1168
  from: Schema.Literal<["step"]>;
1065
1169
  ref: typeof Schema.String;
@@ -1071,7 +1175,7 @@ export declare type MediaInputSlot = typeof MediaInputSlot.Type;
1071
1175
  /** Host-supplied persistence (from @voltro/plugin-storage's StorageService).
1072
1176
  *
1073
1177
  * Both hooks receive the `run` — its `tenantId` is the durable, resume-safe
1074
- * tenant (see `MediaRunContext`). Pass it to `storage.put`/`ingestUrl` as the
1178
+ * tenant (see `FlowRunContext`). Pass it to `storage.put`/`ingestUrl` as the
1075
1179
  * scope so a BOOTSTRAP resume, whose subject has no tenant, still writes into
1076
1180
  * the run's own tenant instead of failing or defaulting. */
1077
1181
  export declare interface MediaPersist {
@@ -1079,11 +1183,11 @@ export declare interface MediaPersist {
1079
1183
  readonly put: (args: {
1080
1184
  data: Uint8Array | string;
1081
1185
  mediaType: string;
1082
- run: MediaRunContext;
1186
+ run: FlowRunContext;
1083
1187
  }) => Effect.Effect<StoredMedia, unknown, unknown>;
1084
1188
  /** Re-host a provider URL (StorageService.ingestUrl). If absent, a provider
1085
1189
  * video URL is used as-is (no re-hosting). */
1086
- readonly ingestUrl?: (url: string, run: MediaRunContext) => Effect.Effect<StoredMedia, unknown, unknown>;
1190
+ readonly ingestUrl?: (url: string, run: FlowRunContext) => Effect.Effect<StoredMedia, unknown, unknown>;
1087
1191
  }
1088
1192
 
1089
1193
  export declare const MediaResult: Schema.Struct<{
@@ -1095,27 +1199,13 @@ export declare const MediaResult: Schema.Struct<{
1095
1199
  providerMetadata: Schema.optional<typeof Schema.Unknown>;
1096
1200
  }>>>;
1097
1201
  costMicroUsd: Schema.optional<Schema.filter<typeof Schema.Finite>>;
1202
+ costSource: Schema.optional<Schema.Literal<["gateway", "estimated", "unpriced"]>>;
1098
1203
  /** A provider/usage correlation only, never a storage reference. */
1099
1204
  requestId: Schema.optional<typeof Schema.NonEmptyString>;
1100
1205
  }>;
1101
1206
 
1102
1207
  export declare type MediaResult = typeof MediaResult.Type;
1103
1208
 
1104
- /** The run an agent or media step executes within, resolved from the durable RUN ROW —
1105
- * not the caller subject. This is the difference between a normal call and a
1106
- * resume: a BOOTSTRAP/crash resume runs under a tenant-less `SYSTEM_SUBJECT`,
1107
- * so a host that persists artifacts per tenant cannot read the tenant from
1108
- * `ctx.request.subject` (it isn't there) and would either fail closed or, worse
1109
- * under the old fallback, write into the wrong tenant. The engine reads the run
1110
- * row it already loads and hands the original tenant down here, so persistence
1111
- * continues in the right place on replay. */
1112
- export declare interface MediaRunContext {
1113
- readonly runId: string;
1114
- /** The run's tenant as stamped when it was created, or `null` for a
1115
- * single-tenant app. Authoritative on resume, where the subject has none. */
1116
- readonly tenantId: string | null;
1117
- }
1118
-
1119
1209
  export declare const Modality: Schema.Literal<["text", "image", "video", "audio"]>;
1120
1210
 
1121
1211
  export declare type Modality = typeof Modality.Type;
@@ -1137,6 +1227,15 @@ export declare const nonContiguousGroups: (segments: ReadonlyArray<FlowSegment>)
1137
1227
  */
1138
1228
  export declare const normalizeFlow: (def: FlowDefinitionInput) => FlowIR;
1139
1229
 
1230
+ /**
1231
+ * Converge every pending flow run whose start was recorded as `queued` onto
1232
+ * what that start has become. Bounded by `take` (newest-first is not needed:
1233
+ * a pending row is pending until it converges, so any order visits it).
1234
+ */
1235
+ export declare const reconcileFlowStarts: (ctx: AppContext, options?: {
1236
+ readonly take?: number;
1237
+ }) => Promise<FlowStartReconciliation>;
1238
+
1140
1239
  /** Register (or replace) a code-defined flow by its IR name. Called by
1141
1240
  * `defineFlow` when the module is loaded. */
1142
1241
  export declare const registerFlow: (ir: FlowIR) => void;
@@ -1144,6 +1243,13 @@ export declare const registerFlow: (ir: FlowIR) => void;
1144
1243
  /** Test seam — forget the recorded tunables. */
1145
1244
  export declare const resetAiFlowsDefaults: () => void;
1146
1245
 
1246
+ declare const ResolvedMediaInputRefs: Schema.Record$<typeof Schema.String, Schema.Struct<{
1247
+ url: Schema.filter<typeof Schema.String>;
1248
+ mediaType: typeof Schema.NonEmptyString;
1249
+ }>>;
1250
+
1251
+ declare type ResolvedMediaInputRefs = typeof ResolvedMediaInputRefs.Type;
1252
+
1147
1253
  declare const ResolvedMediaInputs: Schema.Record$<typeof Schema.String, Schema.filter<typeof Schema.String>>;
1148
1254
 
1149
1255
  declare type ResolvedMediaInputs = typeof ResolvedMediaInputs.Type;
@@ -1236,6 +1342,7 @@ export declare type ReviewOption = typeof ReviewOption.Type;
1236
1342
  export declare const runCadenceTick: (ctx: AppContext, now?: Date) => Promise<{
1237
1343
  readonly matched: number;
1238
1344
  readonly started: ReadonlyArray<string>;
1345
+ readonly reconciled: FlowStartReconciliation;
1239
1346
  }>;
1240
1347
 
1241
1348
  /** Human-review config projected onto a `waiting` run step. */
@@ -1272,6 +1379,9 @@ export declare const RunStep: Schema.Struct<{
1272
1379
  completedAt: Schema.optional<typeof Schema.String>;
1273
1380
  /** Per-step cost in micro-USD (the app view projects to €/cents). */
1274
1381
  costMicroUsd: Schema.optional<typeof Schema.Number>;
1382
+ /** Where that number came from — the gateway's actual charge, a price-table
1383
+ * estimate, or nothing (`unpriced`: the 0 above is not a price). */
1384
+ costSource: Schema.optional<Schema.Literal<["gateway", "estimated", "unpriced"]>>;
1275
1385
  /** Links a media step to its generation/usage record. */
1276
1386
  requestId: Schema.optional<typeof Schema.String>;
1277
1387
  /** Hosted media artifact URL (image/video/audio), via storage. */
@@ -1314,6 +1424,7 @@ export declare interface RunStep {
1314
1424
  readonly startedAt?: string | undefined;
1315
1425
  readonly completedAt?: string | undefined;
1316
1426
  readonly costMicroUsd?: number | undefined;
1427
+ readonly costSource?: CostSource | undefined;
1317
1428
  readonly requestId?: string | undefined;
1318
1429
  readonly url?: string | undefined;
1319
1430
  readonly artifacts?: ReadonlyArray<MediaArtifact> | undefined;