@voltro/plugin-ai-flows 0.33.0 → 0.34.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/index.d.ts CHANGED
@@ -42,20 +42,9 @@ export declare const aiFlowRunsTable: Table<"_voltro_ai_flow_runs", FieldDefinit
42
42
  readonly currentStep: ColumnBuilder<number, "integer", true>;
43
43
  readonly totalSteps: ColumnBuilder<number, "integer", true>;
44
44
  /** The HITL answer, written by `respond`. */
45
- readonly humanResponse: ColumnBuilder<{
46
- readonly value?: string | undefined;
47
- readonly text?: string | undefined;
48
- readonly decision?: "approve" | "reject" | undefined;
49
- readonly respondedAt?: string | undefined;
50
- } | null, "json", boolean>;
45
+ readonly humanResponse: ColumnBuilder<HumanResponse | null, "json", boolean>;
51
46
  /** Staged follow-up when the chain requires confirmation. */
52
- readonly chainPending: ColumnBuilder<{
53
- readonly status: "pending" | "confirmed" | "dismissed";
54
- readonly input: {
55
- readonly [x: string]: unknown;
56
- };
57
- readonly flowRef: string;
58
- } | null, "json", boolean>;
47
+ readonly chainPending: ColumnBuilder<ChainPending | null, "json", boolean>;
59
48
  /** Accumulated cost in micro-USD (app view projects to €/cents). */
60
49
  readonly costMicroUsd: ColumnBuilder<number, "integer", true>;
61
50
  readonly durationMs: ColumnBuilder<number | null, "integer", boolean>;
@@ -79,6 +68,12 @@ export declare const aiFlowRunsTable: Table<"_voltro_ai_flow_runs", FieldDefinit
79
68
  readonly updatedBy: ColumnDefinition<string | null, "reference", boolean>;
80
69
  }, true, never>;
81
70
 
71
+ /** Tunables an `aiFlowsPlugin({...})` call can set from `app.config.ts`. */
72
+ export declare interface AiFlowsDefaults {
73
+ /** Park bound for a `human` step, in ms. `0` = wait forever. */
74
+ readonly humanReviewTimeoutMs?: number;
75
+ }
76
+
82
77
  /**
83
78
  * The AI-Flows plugin. Install it in `app.config.ts` `plugins: [...]`. It
84
79
  * contributes the canonical `ai_flows` + `ai_flow_runs` tables plus the
@@ -94,6 +89,59 @@ export declare const aiFlowsPlugin: (options?: AiFlowsPluginOptions) => VoltroPl
94
89
  export declare interface AiFlowsPluginOptions {
95
90
  /** Namespace for this plugin's routes / inspect endpoints. Default `aiFlows`. */
96
91
  readonly alias?: string;
92
+ /**
93
+ * Contribute this plugin's tables via `extendSchema.tables`. Default `true`.
94
+ *
95
+ * Set `false` when your app ALREADY declares equivalent tables and you want to
96
+ * keep them — the seam this exists for. The plugin then contributes no DDL and
97
+ * the declarative differ never proposes its tables; everything else (routes,
98
+ * inspect, interceptors) is unchanged.
99
+ *
100
+ * **What you take over, exactly:** `_voltro_ai_flows` (definitions) and
101
+ * `_voltro_ai_flow_runs` (runs). The retention sweep registered below still
102
+ * targets `_voltro_ai_flow_runs` by name, so your table inherits the same TTL
103
+ * — which is the intent, but say so to whoever owns that table.
104
+ *
105
+ * It is not offered on every plugin, and the omissions are deliberate rather
106
+ * than unfinished: a `tables: false` that quietly disables a table carrying an
107
+ * AUTHORIZATION or SAFETY decision — the SAML replay cache, SCIM provisioning
108
+ * state, billing's usage counters, cdc-out's outbox — is a security regression
109
+ * shipped as an ergonomics feature. Those plugins need a named store seam
110
+ * first, not a boolean.
111
+ */
112
+ readonly tables?: boolean;
113
+ /**
114
+ * How long a `human` step parks awaiting its review before the run fails, in
115
+ * ms. Default 7 days (`VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS` overrides at
116
+ * deploy time); `0` = wait forever. A flow's `humanTimeoutMs` and a step's own
117
+ * `timeoutMs` both outrank this.
118
+ */
119
+ readonly humanReviewTimeoutMs?: number;
120
+ /**
121
+ * How long a FINISHED flow run is kept, in ms. Default 90 days
122
+ * (`VOLTRO_AI_FLOW_RUNS_TTL_HOURS`).
123
+ *
124
+ * `_voltro_ai_flow_runs` stores every step's full output plus review payloads
125
+ * and grows with traffic, so it is registered with the framework retention
126
+ * sweep like every other run-family table. Two properties of that policy are
127
+ * deliberate:
128
+ *
129
+ * - **Only TERMINAL runs are swept.** The predicate is status-aware
130
+ * (`succeeded | failed | cancelled`), so a run parked on a human review
131
+ * survives regardless of age — a plain time-TTL would delete the pending
132
+ * approvals this plugin exists to support.
133
+ * - **Media artifacts are NOT swept with the row.** A run's steps carry hosted
134
+ * URLs whose blobs belong to the storage plugin; deleting the row orphans
135
+ * them. Keep this TTL at or above the app's own media-purge window, or purge
136
+ * by run id before the row ages out.
137
+ */
138
+ readonly runsTtlMs?: number;
139
+ /**
140
+ * Ceiling on rows ONE inspect call may return (`/flows`, `/runs`). Default
141
+ * 200. A run row carries every step's full output, so the page size is a real
142
+ * response-size knob, not a formality.
143
+ */
144
+ readonly inspectPageMax?: number;
97
145
  }
98
146
 
99
147
  /** `ai_flows` — a reusable flow DEFINITION (the data front door persists these;
@@ -115,33 +163,12 @@ export declare const aiFlowsTable: Table<"_voltro_ai_flows", FieldDefinitions<{
115
163
  /** The ordered plan (`FlowStep[]`); null ≡ empty. */
116
164
  readonly steps: ColumnBuilder<readonly FlowStep[] | null, "json", boolean>;
117
165
  /** The launch-form brief (`BriefField[]`). */
118
- readonly inputSchema: ColumnBuilder<readonly {
119
- readonly key: string;
120
- readonly type: "number" | "boolean" | "text" | "textarea";
121
- readonly required?: boolean | undefined;
122
- readonly label: string;
123
- readonly placeholder?: string | undefined;
124
- }[] | null, "json", boolean>;
125
- readonly chainTo: ColumnBuilder<{
126
- readonly flowRef: string;
127
- readonly mappings: readonly {
128
- readonly sourceKey: string;
129
- readonly targetKey: string;
130
- }[];
131
- readonly requireConfirmation?: boolean | undefined;
132
- } | null, "json", boolean>;
133
- readonly cadence: ColumnBuilder<{
134
- readonly frequency?: "weekly" | "monthly" | undefined;
135
- readonly repeats: readonly number[];
136
- readonly intervalWeeks?: number | undefined;
137
- readonly anchorDate?: string | undefined;
138
- readonly weekOfMonth?: 1 | 2 | 3 | 4 | "last" | undefined;
139
- readonly hour?: number | undefined;
140
- readonly minute?: 0 | 15 | 30 | 45 | undefined;
141
- readonly defaultInput?: {
142
- readonly [x: string]: unknown;
143
- } | undefined;
144
- } | null, "json", boolean>;
166
+ readonly inputSchema: ColumnBuilder<readonly BriefField[] | null, "json", boolean>;
167
+ readonly chainTo: ColumnBuilder<FlowChain | null, "json", boolean>;
168
+ readonly cadence: ColumnBuilder<FlowCadence | null, "json", boolean>;
169
+ /** Flow-wide park bound for `human` steps, in ms (a step's own `timeoutMs`
170
+ * wins). Null → the plugin default. `0` = wait forever. */
171
+ readonly humanTimeoutMs: ColumnBuilder<number | null, "integer", boolean>;
145
172
  /** Master on/off — gates scheduling. */
146
173
  readonly isEnabled: ColumnBuilder<boolean, "boolean", true>;
147
174
  readonly metadata: ColumnBuilder<Record<string, unknown> | null, "json", boolean>;
@@ -177,7 +204,17 @@ export declare const BriefField: Schema.Struct<{
177
204
  placeholder: Schema.optional<typeof Schema.String>;
178
205
  }>;
179
206
 
180
- export declare type BriefField = typeof BriefField.Type;
207
+ /** An INTERFACE for the same reason as {@link FlowStep} — it is reached from an
208
+ * exported table (`aiFlows.inputSchema`), so an alias to a mapped type gets
209
+ * expanded structurally into the api report, in an order that is not stable
210
+ * across build scopes. The `Equals` pin below fails to compile on any drift. */
211
+ export declare interface BriefField {
212
+ readonly key: string;
213
+ readonly label: string;
214
+ readonly type: 'text' | 'textarea' | 'number' | 'boolean';
215
+ readonly required?: boolean | undefined;
216
+ readonly placeholder?: string | undefined;
217
+ }
181
218
 
182
219
  /**
183
220
  * Build the `flow.run` executor over a live `AppContext` + injected capabilities.
@@ -221,7 +258,12 @@ export declare const ChainPending: Schema.Struct<{
221
258
  status: Schema.Literal<["pending", "confirmed", "dismissed"]>;
222
259
  }>;
223
260
 
224
- export declare type ChainPending = typeof ChainPending.Type;
261
+ /** An INTERFACE reached from `aiFlowRuns.chainPending`; see {@link BriefField}. */
262
+ export declare interface ChainPending {
263
+ readonly flowRef: string;
264
+ readonly input: Json;
265
+ readonly status: 'pending' | 'confirmed' | 'dismissed';
266
+ }
225
267
 
226
268
  export declare const clampMaxSteps: (n: number | null | undefined) => number;
227
269
 
@@ -257,6 +299,9 @@ export declare interface DefineFlowInput {
257
299
  };
258
300
  readonly chain?: FlowChain;
259
301
  readonly cadence?: FlowCadence;
302
+ /** Flow-wide park bound for every `human` step that sets no `timeoutMs` of its
303
+ * own, in ms. `0` = wait forever. */
304
+ readonly humanTimeoutMs?: number;
260
305
  }
261
306
 
262
307
  /** Hard-delete a flow row by id. Returns whether a row was removed. */
@@ -268,7 +313,8 @@ export declare interface EngineDeps {
268
313
  /** Resolve an `agent` step's `agentRef` → system prompt + model. Absent →
269
314
  * agent steps with an `agentRef` fail with FlowCapabilityMissing. */
270
315
  readonly resolveAgent?: (agentRef: string) => Effect.Effect<AgentResolution, FlowNotFound, unknown>;
271
- /** Media generator for image/video/audio steps (task #31 plugs in here). */
316
+ /** Media generator for image/video/audio steps (`makeMediaGenerator` in
317
+ * `mediaAdapter.ts` is the @voltro/ai-backed implementation). */
272
318
  readonly generateMedia?: MediaGenerator;
273
319
  /** Fallback model per modality when a step omits `model`. */
274
320
  readonly defaultModels?: {
@@ -288,11 +334,23 @@ export declare interface EngineDeps {
288
334
  /** Long-term-memory prefix prepended to the orchestrator system prompt (wire
289
335
  * to the app's ai_user_memories). Best-effort. */
290
336
  readonly memoryPrefix?: (ownerId: string | null) => Effect.Effect<string, never, unknown>;
337
+ /** Default park bound for `human` steps in ms, for flows that set none. Sits
338
+ * between the flow's own `humanTimeoutMs` and the `aiFlowsPlugin({...})`
339
+ * option / `VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS` (see `defaults.ts`).
340
+ * `0` = wait forever. */
341
+ readonly humanReviewTimeoutMs?: number;
291
342
  }
292
343
 
293
344
  /** The canonical name the launch action starts + the app re-export registers. */
294
345
  export declare const FLOW_RUN_WORKFLOW = "flow.run";
295
346
 
347
+ /** Deploy-time override for {@link FLOW_RUNS_TTL_HOURS_DEFAULT}, in hours. */
348
+ export declare const FLOW_RUNS_TTL_ENV = "VOLTRO_AI_FLOW_RUNS_TTL_HOURS";
349
+
350
+ /** Framework default retention for `_voltro_ai_flow_runs`: 90 days of finished
351
+ * runs. Only TERMINAL runs are swept — see `index.ts`. */
352
+ export declare const FLOW_RUNS_TTL_HOURS_DEFAULT: number;
353
+
296
354
  /** A required brief field was blank at launch. */
297
355
  declare class FlowBriefIncomplete extends FlowBriefIncomplete_base {
298
356
  }
@@ -322,7 +380,25 @@ export declare const FlowCadence: Schema.Struct<{
322
380
  defaultInput: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
323
381
  }>;
324
382
 
325
- export declare type FlowCadence = typeof FlowCadence.Type;
383
+ /**
384
+ * An INTERFACE — reached from `aiFlows.cadence`; see {@link BriefField}.
385
+ *
386
+ * This one is not hypothetical: a single-package `api:report` and a
387
+ * full-monorepo build emitted `hour`/`minute` in DIFFERENT positions inside the
388
+ * structurally-expanded alias, so the api-surface gate rejected a file nobody
389
+ * had touched, showing the same members moving. That is the exact failure
390
+ * {@link FlowStep}'s comment describes.
391
+ */
392
+ export declare interface FlowCadence {
393
+ readonly frequency?: 'weekly' | 'monthly' | undefined;
394
+ readonly repeats: ReadonlyArray<number>;
395
+ readonly intervalWeeks?: number | undefined;
396
+ readonly anchorDate?: string | undefined;
397
+ readonly weekOfMonth?: 1 | 2 | 3 | 4 | 'last' | undefined;
398
+ readonly hour?: number | undefined;
399
+ readonly minute?: 0 | 15 | 30 | 45 | undefined;
400
+ readonly defaultInput?: Json | undefined;
401
+ }
326
402
 
327
403
  /** A generation step required a capability the host did not provide (e.g. a
328
404
  * media modality with no configured generator, or storage for the artifact). */
@@ -347,7 +423,15 @@ export declare const FlowChain: Schema.Struct<{
347
423
  requireConfirmation: Schema.optional<typeof Schema.Boolean>;
348
424
  }>;
349
425
 
350
- export declare type FlowChain = typeof FlowChain.Type;
426
+ /** An INTERFACE reached from `aiFlows.chainTo`; see {@link BriefField}. */
427
+ export declare interface FlowChain {
428
+ readonly flowRef: string;
429
+ readonly mappings: ReadonlyArray<{
430
+ readonly targetKey: string;
431
+ readonly sourceKey: string;
432
+ }>;
433
+ readonly requireConfirmation?: boolean | undefined;
434
+ }
351
435
 
352
436
  /** The raw flow definition either front door supplies before normalization.
353
437
  * Optional fields tolerate explicit `undefined` (row reads produce it). */
@@ -361,6 +445,7 @@ export declare interface FlowDefinitionInput {
361
445
  readonly orchestratorInstructions?: string | null | undefined;
362
446
  readonly chain?: FlowChain | null | undefined;
363
447
  readonly cadence?: FlowCadence | null | undefined;
448
+ readonly humanTimeoutMs?: number | null | undefined;
364
449
  }
365
450
 
366
451
  /** The single representation the engine interprets, from either front door. */
@@ -376,6 +461,10 @@ export declare interface FlowIR {
376
461
  };
377
462
  readonly chain?: FlowChain;
378
463
  readonly cadence?: FlowCadence;
464
+ /** Flow-wide default for a `human` step's park bound, in ms. A step's own
465
+ * `timeoutMs` wins; absent here too → the engine/plugin default. `0` = wait
466
+ * forever. */
467
+ readonly humanTimeoutMs?: number;
379
468
  }
380
469
 
381
470
  /** A lifecycle event the host can turn into a notification (best-effort). */
@@ -479,6 +568,11 @@ export declare const FlowStep: Schema.Struct<{
479
568
  value: typeof Schema.String;
480
569
  label: typeof Schema.String;
481
570
  }>>>;
571
+ /** `human` step only — how long the run parks awaiting this review before it
572
+ * fails, in milliseconds. Omitted → the flow's `humanTimeoutMs`, then the
573
+ * engine/plugin default (see `resolveHumanReviewTimeoutMs`). `0` means WAIT
574
+ * FOREVER (the park is slot-free, so an unbounded wait costs no worker). */
575
+ timeoutMs: Schema.optional<typeof Schema.Number>;
482
576
  }>;
483
577
 
484
578
  /**
@@ -519,6 +613,7 @@ export declare interface FlowStep {
519
613
  readonly schema?: Json | undefined;
520
614
  readonly reviewMode?: ReviewMode | undefined;
521
615
  readonly options?: ReadonlyArray<ReviewOption> | undefined;
616
+ readonly timeoutMs?: number | undefined;
522
617
  }
523
618
 
524
619
  /** Typed builders for each step kind — terser + safer than hand-writing the
@@ -566,7 +661,8 @@ export declare const flowStep: {
566
661
  prompt: string;
567
662
  outputKey?: string;
568
663
  }) => FlowStepDraft;
569
- /** Pause for human review. */
664
+ /** Pause for human review. `timeoutMs` bounds the park (default: the flow's
665
+ * `humanTimeoutMs`, then the plugin default — 7 days; `0` = wait forever). */
570
666
  human: (o: {
571
667
  title?: string;
572
668
  description?: string;
@@ -574,12 +670,16 @@ export declare const flowStep: {
574
670
  options?: ReadonlyArray<ReviewOption>;
575
671
  prompt?: string;
576
672
  outputKey?: string;
673
+ timeoutMs?: number;
577
674
  }) => FlowStepDraft;
578
675
  };
579
676
 
580
677
  /** A step without its `id` — `defineFlow` assigns stable index-based ids. */
581
678
  export declare type FlowStepDraft = Omit<FlowStep, 'id'>;
582
679
 
680
+ /** The recorded `app.config.ts` tunables. */
681
+ export declare const getAiFlowsDefaults: () => AiFlowsDefaults;
682
+
583
683
  /** One stored flow by id, or undefined (tenant-scoped by the runtime). */
584
684
  export declare const getFlow: (ctx: AppContext, args: {
585
685
  id: string;
@@ -588,8 +688,18 @@ export declare const getFlow: (ctx: AppContext, args: {
588
688
  /** Resolve a code-registered flow by name, or `undefined` for a data flow. */
589
689
  export declare const getRegisteredFlow: (name: string) => FlowIR | undefined;
590
690
 
591
- /** The signal a `human` step parks on; `respondToFlow` sends it to resume. */
592
- export declare const HUMAN_RESPONSE_SIGNAL = "flow-human-response";
691
+ /** Namespace of the signal a `human` step parks on. Never used on its own
692
+ * the signal a step actually awaits is {@link humanResponseSignalName}. */
693
+ export declare const HUMAN_RESPONSE_SIGNAL_PREFIX = "flow-human-response";
694
+
695
+ /** Deploy-time override for {@link HUMAN_REVIEW_TIMEOUT_HOURS_DEFAULT}, in
696
+ * hours. `0` (or a negative value) means WAIT FOREVER. */
697
+ export declare const HUMAN_REVIEW_TIMEOUT_ENV = "VOLTRO_AI_FLOW_HUMAN_REVIEW_TIMEOUT_HOURS";
698
+
699
+ /** Framework default park bound for a human review: 7 days. Long enough to
700
+ * survive a weekend + a holiday Monday, short enough that an abandoned review
701
+ * eventually settles instead of pinning a run forever. */
702
+ export declare const HUMAN_REVIEW_TIMEOUT_HOURS_DEFAULT: number;
593
703
 
594
704
  /** The HITL answer, written by `respond`. */
595
705
  export declare const HumanResponse: Schema.Struct<{
@@ -599,7 +709,38 @@ export declare const HumanResponse: Schema.Struct<{
599
709
  respondedAt: Schema.optional<typeof Schema.String>;
600
710
  }>;
601
711
 
602
- export declare type HumanResponse = typeof HumanResponse.Type;
712
+ /** An INTERFACE reached from `aiFlowRuns.humanResponse`; see {@link BriefField}. */
713
+ export declare interface HumanResponse {
714
+ readonly decision?: 'approve' | 'reject' | undefined;
715
+ readonly value?: string | undefined;
716
+ readonly text?: string | undefined;
717
+ readonly respondedAt?: string | undefined;
718
+ }
719
+
720
+ /**
721
+ * The signal name ONE human step parks on, derived from that step's position in
722
+ * the (journal-pinned) plan.
723
+ *
724
+ * **Why this is per-step and not one constant.** `awaitSignalSuspending` derives
725
+ * its `DurableDeferred` from workflow-name + signal-name per execution, so a
726
+ * single `'flow-human-response'` for every human step in a flow meant the SECOND
727
+ * review awaited the deferred the FIRST answer had already completed — it
728
+ * resolved instantly, with the first reviewer's payload, and the run finished
729
+ * carrying an approval nobody gave. Measured before the fix: a two-review flow,
730
+ * one answer, `status: 'succeeded'`, `output: { first: 'answer-one', second:
731
+ * 'answer-one' }`. The same collision aliased the wait's durable timeout clock
732
+ * (`await-signal-timeout/<name>`), so the second park inherited the first's
733
+ * expiry too.
734
+ *
735
+ * **Why the INDEX is the identity.** It must be identical on every replay and
736
+ * derivable by the responder without re-resolving the flow definition (which may
737
+ * have been edited mid-run). The step list is pinned in the run's journal at
738
+ * step 0, so a step's index is fixed for the life of the run; the responder
739
+ * reads it back off the run row's live timeline. A counter, a clock, or a
740
+ * `randomUUID` would all break replay; the authored step `id` would break the
741
+ * moment a definition is re-ordered under a live run.
742
+ */
743
+ export declare const humanResponseSignalName: (stepIndex: number) => string;
603
744
 
604
745
  /** Replace `{{key}}` with `context[key]` (objects/arrays JSON-stringified; an
605
746
  * absent/null value renders empty). */
@@ -610,6 +751,25 @@ export declare const Json: Schema.Record$<typeof Schema.String, typeof Schema.Un
610
751
 
611
752
  export declare type Json = typeof Json.Type;
612
753
 
754
+ /** A JSON-Schema node as stored on a `structured` step. */
755
+ export declare type JsonSchemaNode = Record<string, unknown>;
756
+
757
+ /**
758
+ * Adapt a `structured` step's JSON Schema to the Effect `Schema` the engine
759
+ * hands `generateObject`. An absent / unusable schema falls back to the open
760
+ * record, so a step authored without one behaves exactly as before.
761
+ *
762
+ * ```ts
763
+ * jsonSchemaToEffectSchema({
764
+ * type: 'object',
765
+ * properties: { title: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } } },
766
+ * required: ['title'],
767
+ * })
768
+ * // ≡ Schema.Struct({ title: Schema.String, tags: Schema.optional(Schema.Array(Schema.String)) })
769
+ * ```
770
+ */
771
+ export declare const jsonSchemaToEffectSchema: (jsonSchema: JsonSchemaNode | undefined) => Schema.Schema<unknown, unknown>;
772
+
613
773
  /** Launch a fresh run of a flow (by code name or row id). Validates the brief,
614
774
  * creates the run row, and starts the durable `flow.run` workflow. */
615
775
  export declare const launchFlow: (ctx: AppContext, args: {
@@ -745,16 +905,38 @@ export declare const normalizeFlow: (def: FlowDefinitionInput) => FlowIR;
745
905
  * `defineFlow` when the module is loaded. */
746
906
  export declare const registerFlow: (ir: FlowIR) => void;
747
907
 
908
+ /** Test seam — forget the recorded tunables. */
909
+ export declare const resetAiFlowsDefaults: () => void;
910
+
911
+ /**
912
+ * Resolve a human step's park bound down the precedence ladder. Pure — the env
913
+ * read is passed in so a test can drive it without touching `process.env`.
914
+ */
915
+ export declare const resolveHumanReviewTimeoutMs: (input: {
916
+ readonly step?: {
917
+ readonly timeoutMs?: number | undefined;
918
+ };
919
+ readonly flow?: {
920
+ readonly humanTimeoutMs?: number | undefined;
921
+ };
922
+ readonly deps?: {
923
+ readonly humanReviewTimeoutMs?: number | undefined;
924
+ };
925
+ readonly env?: NodeJS.ProcessEnv;
926
+ }) => number;
927
+
748
928
  /** Resolve the IR: code registry by name first, else the stored `ai_flows` row. */
749
929
  export declare const resolveIr: (ctx: AppContext, flowRef: string) => Effect.Effect<FlowIR, FlowNotFound>;
750
930
 
751
931
  /** Record a human's answer to a `waiting` run + signal the parked workflow to
752
- * resume. (The park itself lands with the slot-freeing await, task #32/#35.) */
932
+ * resume. The signal is addressed to THAT STEP's own durable deferred — one
933
+ * shared name meant a later review resolved with an earlier answer. */
753
934
  export declare const respondToFlow: (ctx: AppContext, args: {
754
935
  runId: string;
755
936
  response: HumanResponse;
756
937
  }) => Effect.Effect<{
757
938
  runId: string;
939
+ stepIndex: number;
758
940
  }, FlowRunNotFound | FlowNotAwaitingResponse, never>;
759
941
 
760
942
  /** Re-run a run from its first unfinished step. Reuses the SAME requestId so the
@@ -871,6 +1053,9 @@ export declare type RunStepStatus = typeof RunStepStatus.Type;
871
1053
  /** Sanitize an outputKey to the `{{ref}}`-safe charset. */
872
1054
  export declare const sanitizeOutputKey: (key: string) => string;
873
1055
 
1056
+ /** Record the `app.config.ts` tunables (called by the plugin factory). */
1057
+ export declare const setAiFlowsDefaults: (defaults: AiFlowsDefaults) => void;
1058
+
874
1059
  /** The five step types. `generate` fans out to four modalities. */
875
1060
  export declare const StepType: Schema.Literal<["agent", "generate", "structured", "note", "human"]>;
876
1061
 
@@ -905,4 +1090,16 @@ export declare const Visibility: Schema.Literal<["private", "organization", "sha
905
1090
 
906
1091
  export declare type Visibility = typeof Visibility.Type;
907
1092
 
1093
+ /**
1094
+ * Which human step a `waiting` run is parked on — the index the engine used to
1095
+ * derive the step's signal name.
1096
+ *
1097
+ * Read from the LIVE TIMELINE the engine wrote when it parked (`steps[i].status
1098
+ * === 'waiting'`), with `currentStep` as the fallback for a row whose timeline
1099
+ * hasn't been written yet. Deliberately NOT re-resolved from the flow definition:
1100
+ * a definition edited mid-run would hand back a different plan than the one the
1101
+ * run's journal pinned, and the answer would be delivered to the wrong step.
1102
+ */
1103
+ export declare const waitingStepIndex: (row: Record<string, unknown>) => number;
1104
+
908
1105
  export { }