@ixo/editor 6.22.0 → 6.24.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,1039 @@
1
+ import { a6 as FlowNode, a7 as FlowNodeAuthzExtension, F as FlowRuntimeStateManager, g as UcanService, I as InvocationStore, a8 as EvaluationStatus, a9 as IxoEditorType, aa as PendingInvocation, ab as ActionServices, ac as ActionHandlers, ad as RunEventAppender, ae as ActionResult, af as ActionDefinition, j as UcanCapability, ag as FlowNodeRuntimeState, U as UcanDelegationStore, S as StoredDelegation } from './index-x1R0c9P_.js';
2
+ import * as Y from 'yjs';
3
+ import { Doc, Map } from 'yjs';
4
+ import { MatrixClient } from 'matrix-js-sdk';
5
+
6
+ /** Condition that gates when a capability activates. */
7
+ interface ConditionRef {
8
+ /** ID of the upstream capability whose output is checked. */
9
+ sourceId: string;
10
+ /** Output field path to inspect, e.g., "decision". */
11
+ field: string;
12
+ /** Comparison operator. */
13
+ operator: 'eq' | 'neq' | 'gt' | 'lt' | 'in' | 'exists';
14
+ /** Value to compare against (omit for 'exists'). */
15
+ value?: unknown;
16
+ /** What happens when the condition is (not) met. */
17
+ effect?: {
18
+ action: 'enable' | 'disable' | 'hide' | 'show';
19
+ message?: string;
20
+ };
21
+ }
22
+ /** Authorization constraint for a capability. */
23
+ interface ActorConstraint {
24
+ /** Whitelisted actor DIDs. */
25
+ authorisedActors?: string[];
26
+ /** Parent capability URI for delegation chain. */
27
+ parentCapability?: string;
28
+ }
29
+ /** Time-to-live constraint for a capability. */
30
+ interface TTLConstraint {
31
+ /** Hard deadline (ISO 8601 date string). */
32
+ absoluteDueDate?: string;
33
+ /** Duration from when the block becomes enabled (ISO 8601 duration, e.g., "P7D"). */
34
+ fromEnablement?: string;
35
+ /** Duration from when an actor commits (ISO 8601 duration, e.g., "PT2H"). */
36
+ fromCommitment?: string;
37
+ }
38
+ /**
39
+ * A single capability in a Base UCAN flow plan.
40
+ *
41
+ * UCAN semantics:
42
+ * can = the action
43
+ * with = the resource or scope
44
+ * nb = typed caveats, inputs, parameters
45
+ *
46
+ * Workflow semantics (kept separate from nb):
47
+ * condition, trigger, parallelGroup, phase
48
+ */
49
+ interface FlowCapability {
50
+ /** Stable node identifier for this step. */
51
+ id: string;
52
+ /** UCAN-style ability string, e.g., "bid/submit", "email/send". */
53
+ can: string;
54
+ /** Resource URI, e.g., "ixo:flow:{flowId}" or "ixo:flow:{flowId}:{nodeId}". */
55
+ with: string;
56
+ /** Typed caveats / input parameters. Shape is dictated by the action registry. */
57
+ nb?: Record<string, unknown>;
58
+ /** Condition that must be met for this capability to activate. */
59
+ condition?: ConditionRef;
60
+ /** Capabilities sharing a parallelGroup run concurrently. */
61
+ parallelGroup?: string;
62
+ /** Semantic grouping for layout lanes. */
63
+ phase?: string;
64
+ /** Who can execute this step. */
65
+ actor?: ActorConstraint;
66
+ /** Time-to-live constraints. */
67
+ ttl?: TTLConstraint;
68
+ /**
69
+ * When and how this block fires. Defaults to `{ type: 'manual' }` if absent
70
+ * (i.e. block runs only when a user explicitly invokes it).
71
+ *
72
+ * `block.event` triggers turn this block into a listener: when the source
73
+ * block emits an event matching `eventName`, a pending invocation is
74
+ * queued on this block and the assigned actor is DM'd to invoke it. See
75
+ * `docs/flow-engine/events-and-triggers-plan.md` §3.7 and §18 for the full model.
76
+ *
77
+ * Triggers are a NEW sibling field on FlowCapability (decided in eng review
78
+ * pass 2 phase 0 #1). They are NOT an extension of `condition`. The decompile
79
+ * branch's author needs to add `props.trigger` parsing when their work merges.
80
+ */
81
+ trigger?: TriggerSpec;
82
+ /** Display title (falls back to can statement). */
83
+ title?: string;
84
+ /** Description of what this step does. */
85
+ description?: string;
86
+ /** Icon identifier. */
87
+ icon?: string;
88
+ }
89
+ /**
90
+ * Trigger declaration for a block. When and how it fires.
91
+ */
92
+ /** A single event source in a barrier trigger. */
93
+ interface TriggerSource {
94
+ /** ID of the block that emits the event. */
95
+ sourceBlockId: string;
96
+ /** Name of the event (must match an event declared on the source action's `events` vocabulary). */
97
+ eventName: string;
98
+ /** Namespace alias for payload access: `trigger.payload.<alias>.*` */
99
+ alias: string;
100
+ /**
101
+ * When `true`, this source does NOT block queuing — the barrier fires once all
102
+ * *required* (non-optional) sources have fired. If an optional source fired before
103
+ * the barrier completes, its payload is merged in; if it never fires, it's skipped.
104
+ * Defaults to `false` (required) for backward compatibility.
105
+ */
106
+ optional?: boolean;
107
+ }
108
+ interface TriggerSpec {
109
+ /**
110
+ * - `manual`: block runs only when a user explicitly invokes it (default).
111
+ * - `flow.start`: block runs when the flow execution begins.
112
+ * - `block.event`: block runs each time another block emits a matching event.
113
+ * - `block.event.all`: block runs once ALL listed sources have emitted (barrier/join).
114
+ */
115
+ type: 'manual' | 'flow.start' | 'block.event' | 'block.event.all';
116
+ /** Required when `type === 'block.event'`. ID of the block that emits the event. */
117
+ sourceBlockId?: string;
118
+ /** Required when `type === 'block.event'`. Name of the event (must match an event declared on the source action's `events` vocabulary). */
119
+ eventName?: string;
120
+ /** Required when `type === 'block.event.all'`. Array of event sources — ALL must fire before the listener is queued. */
121
+ sources?: TriggerSource[];
122
+ }
123
+ /**
124
+ * The Base UCAN flow plan — the intermediate representation between
125
+ * user intent and the compiled flow graph.
126
+ *
127
+ * capabilities is an ordered list. Each capability carries its own stable `id`.
128
+ */
129
+ interface BaseUcanFlow {
130
+ kind: 'qi.flow.base-ucan';
131
+ version: '1.0';
132
+ flowId: string;
133
+ title: string;
134
+ goal?: string;
135
+ meta?: {
136
+ entityDid?: string;
137
+ flowUri?: string;
138
+ rootIssuer?: string;
139
+ };
140
+ /** Ordered capabilities, each with its own stable node ID. */
141
+ capabilities: FlowCapability[];
142
+ }
143
+ /** A reference to an upstream node's output field. Format: "nodeId.output.fieldPath" */
144
+ interface RuntimeRef {
145
+ $ref: string;
146
+ }
147
+ declare function isRuntimeRef(value: unknown): value is RuntimeRef;
148
+ /** A single block ready for insertion into BlockNote. */
149
+ interface CompiledBlock {
150
+ /** Pre-generated stable block ID. */
151
+ id: string;
152
+ /** BlockNote block type (e.g., "action"). */
153
+ type: string;
154
+ /** Block props — all values are strings per BlockNote convention. */
155
+ props: Record<string, string>;
156
+ }
157
+ /**
158
+ * An edge in the flow graph. Currently only `kind: 'trigger'` edges are
159
+ * synthesized — they come from `block.event` triggers and represent the
160
+ * source → listener relationship visually on the canvas.
161
+ */
162
+ interface CompiledEdge {
163
+ id: string;
164
+ source: string;
165
+ target: string;
166
+ kind: 'trigger';
167
+ condition?: ConditionRef;
168
+ }
169
+ /** A compiled flow node stored in qi.flow.nodes. */
170
+ interface CompiledFlowNode {
171
+ id: string;
172
+ blockId: string;
173
+ can: string;
174
+ with: string;
175
+ registryType: string;
176
+ title: string;
177
+ description: string;
178
+ props: Record<string, string>;
179
+ phase?: string;
180
+ parallelGroup?: string;
181
+ actor?: ActorConstraint;
182
+ }
183
+ /** The complete compiled output from the Base UCAN compiler. */
184
+ interface CompiledFlow {
185
+ /** Flow metadata for qi.flow.meta and Y.Map('root'). */
186
+ meta: {
187
+ flowId: string;
188
+ title: string;
189
+ goal?: string;
190
+ version: string;
191
+ flowOwnerDid: string;
192
+ flowUri?: string;
193
+ compiledAt: string;
194
+ compiledFrom: 'BaseUcanFlow';
195
+ };
196
+ /** Blocks to insert into BlockNote, in topological order. */
197
+ blocks: CompiledBlock[];
198
+ /** Flow graph nodes, keyed by node ID. */
199
+ nodes: Record<string, CompiledFlowNode>;
200
+ /** Dependency edges. */
201
+ edges: CompiledEdge[];
202
+ /** Topological order of node IDs. */
203
+ order: string[];
204
+ /** nodeId → blockId mapping. */
205
+ blockIndex: Record<string, string>;
206
+ }
207
+ /** Strategy for how a compiled flow is applied to an existing document. */
208
+ type FlowStrategy = 'full' | 'merge' | 'patch';
209
+
210
+ declare const buildAuthzFromProps: (props: Record<string, any>) => FlowNodeAuthzExtension;
211
+ declare const buildFlowNodeFromBlock: (block: any) => FlowNode;
212
+
213
+ /**
214
+ * Context passed to resolveRuntimeRefs when resolving inputs for a triggered
215
+ * listener block. The runtime constructs this from a PendingInvocation when
216
+ * the assignee invokes the listener.
217
+ *
218
+ * - `payload`: the frozen event payload, used to resolve `trigger.payload.*` refs
219
+ * - `refSnapshots`: frozen `nodeId.output.*` values captured at queue time,
220
+ * used to resolve those refs against the moment of emission rather than
221
+ * current state. See docs/flow-engine/events-and-triggers-plan.md §3.5.1 for the
222
+ * Sally → Mike scenario this fixes.
223
+ */
224
+ interface TriggerResolutionContext {
225
+ payload: Record<string, unknown>;
226
+ refSnapshots: Record<string, unknown>;
227
+ }
228
+ /**
229
+ * Resolve runtime references in an nb (caveats) object.
230
+ *
231
+ * At compile time, `{ "$ref": "nodeId.output.fieldPath" }` values are serialized
232
+ * as-is into the block's `inputs` JSON string. At execution time, this function
233
+ * replaces them with actual output values from upstream nodes.
234
+ *
235
+ * Two ref namespaces are supported:
236
+ * - `nodeId.output.fieldPath` — looked up via `getNodeOutput`. When a
237
+ * `triggerContext` is provided AND `refSnapshots[ref]` exists, the
238
+ * snapshot is preferred over the live lookup. This is the load-bearing
239
+ * fix for the lag-time scenario where multiple pending invocations are
240
+ * queued and the source block re-runs before the assignee acts on them.
241
+ * - `trigger.payload.fieldPath` — looked up in `triggerContext.payload`.
242
+ * Only valid when `triggerContext` is provided (i.e. resolving inputs
243
+ * for a triggered listener invocation). Compile-time validation in
244
+ * compiler.ts guarantees these only appear on `block.event`-triggered
245
+ * blocks.
246
+ *
247
+ * @param nb - The caveats object (may contain nested RuntimeRef values)
248
+ * @param getNodeOutput - Lookup function for upstream node outputs
249
+ * @param triggerContext - Optional, present only for triggered listener invocations
250
+ * @returns A new object with all $ref values resolved
251
+ */
252
+ declare function resolveRuntimeRefs(nb: Record<string, unknown>, getNodeOutput: (nodeId: string) => Record<string, unknown> | undefined, triggerContext?: TriggerResolutionContext): Record<string, unknown>;
253
+
254
+ interface NodeActionResult {
255
+ claimId?: string;
256
+ evaluationStatus?: EvaluationStatus;
257
+ submittedByDid?: string;
258
+ payload?: any;
259
+ }
260
+ interface ExecutionContext {
261
+ runtime: FlowRuntimeStateManager;
262
+ /** UCAN service — optional for v0.x flows, required for v1.0.0+ */
263
+ ucanService?: UcanService;
264
+ /** Invocation store — optional for v0.x flows */
265
+ invocationStore?: InvocationStore;
266
+ flowUri: string;
267
+ flowId: string;
268
+ /** Session run that owns runtime and invocation records for this execution. */
269
+ sessionRunId?: string;
270
+ flowOwnerDid: string;
271
+ /** Flow schema version. When set, controls whether UCAN is enforced. */
272
+ schemaVersion?: string;
273
+ now?: () => number;
274
+ }
275
+ interface ExecutionOutcome {
276
+ success: boolean;
277
+ stage: 'authorization' | 'claim' | 'action' | 'complete';
278
+ error?: string;
279
+ result?: NodeActionResult;
280
+ capabilityId?: string;
281
+ invocationCid?: string;
282
+ }
283
+ interface ExecuteNodeParams {
284
+ node: FlowNode;
285
+ actorDid: string;
286
+ actorType: 'entity' | 'user';
287
+ entityRoomId?: string;
288
+ context: ExecutionContext;
289
+ action: () => Promise<NodeActionResult>;
290
+ pin: string;
291
+ }
292
+ /**
293
+ * Execute a node.
294
+ *
295
+ * The pipeline adapts based on the flow's schema version (via context.schemaVersion):
296
+ *
297
+ * **v0.x (legacy):** activation → action → runtime update.
298
+ * UCAN authorization and invocations are skipped when ucanService is not available.
299
+ *
300
+ * **v1.0.0+:** activation → UCAN authorization → invocation → action → runtime update.
301
+ * Requires a configured ucanService with valid delegation chains.
302
+ */
303
+ declare const executeNode: ({ node, actorDid, actorType, entityRoomId, context, action, pin }: ExecuteNodeParams) => Promise<ExecutionOutcome>;
304
+
305
+ type ActionBlockLike = {
306
+ id?: string;
307
+ props?: Record<string, unknown>;
308
+ };
309
+ type EditorOrYDoc = IxoEditorType | Y.Doc;
310
+ interface BuildActionRunInputsParams {
311
+ editorOrYDoc?: EditorOrYDoc | null;
312
+ block?: ActionBlockLike | null;
313
+ blockId?: string;
314
+ document?: ActionBlockLike[];
315
+ runtime?: FlowRuntimeStateManager;
316
+ savedInputs?: unknown;
317
+ runtimeInputs?: Record<string, unknown>;
318
+ pendingInvocationId?: string;
319
+ /**
320
+ * The **session run** this execution belongs to (`runs.ts`), not the
321
+ * per-execution audit id. Defaults to `resolveActiveRunId(yDoc)`, which is a
322
+ * constant in Phase A.
323
+ *
324
+ * D2: every *new* parameter named `runId` means the session run. The
325
+ * execution-scoped id keeps its historical name only where it is already
326
+ * persisted (`RunRecordDetails.runId`), and is surfaced going forward as
327
+ * {@link ExecuteActionBlockResult.executionId}.
328
+ */
329
+ runId?: string;
330
+ }
331
+ interface BuildActionRunInputsResult {
332
+ inputs: Record<string, unknown>;
333
+ savedInputs: Record<string, unknown>;
334
+ mergedInputs: Record<string, unknown>;
335
+ pendingInvocation?: PendingInvocation;
336
+ triggerContext?: TriggerResolutionContext;
337
+ }
338
+ type ActionExecutionCompletionState = 'completed' | 'failed' | 'awaiting_readback' | 'needs_verification';
339
+ interface ExecuteActionBlockParams extends BuildActionRunInputsParams {
340
+ actorDid: string;
341
+ actorType: 'entity' | 'user';
342
+ entityRoomId?: string;
343
+ ucanService?: UcanService;
344
+ invocationStore?: InvocationStore;
345
+ services?: ActionServices;
346
+ handlers?: ActionHandlers;
347
+ runEventLog?: RunEventAppender;
348
+ pin?: string;
349
+ now?: () => number;
350
+ flowUri?: string;
351
+ flowId?: string;
352
+ flowOwnerDid?: string;
353
+ schemaVersion?: string;
354
+ }
355
+ interface ExecuteActionBlockResult {
356
+ success: boolean;
357
+ stage: ExecutionOutcome['stage'] | 'input' | 'action_lookup';
358
+ blockId: string;
359
+ actionType?: string;
360
+ output?: Record<string, unknown>;
361
+ events: NonNullable<ActionResult['events']>;
362
+ error?: string;
363
+ invocationCid?: string;
364
+ capabilityId?: string;
365
+ /**
366
+ * Id of the audit record this execution wrote (`run-<ts>-<rand>`).
367
+ *
368
+ * @deprecated Use {@link ExecuteActionBlockResult.executionId}. Both fields
369
+ * carry the same value and both stay populated — `flow-manager` and
370
+ * `flow-agent` read `runId` today, and `RunRecordDetails.runId` is persisted
371
+ * live data feeding `computePendingInvocationId`, so the *stored* name is
372
+ * never renamed.
373
+ *
374
+ * PHASE A COMPAT — REMOVAL TRIGGER: Phase B, once both orchestrators read
375
+ * `executionId` (compat register item 8).
376
+ */
377
+ runId?: string;
378
+ /**
379
+ * Forward name for the per-execution audit id. Identical to {@link runId}.
380
+ *
381
+ * D2: bare `runId` now means the *session* run everywhere in new code, so the
382
+ * execution id gets an unambiguous name rather than the persisted one being
383
+ * renamed out from under live rooms.
384
+ */
385
+ executionId?: string;
386
+ pendingInvocationRemoved?: boolean;
387
+ completionState: ActionExecutionCompletionState;
388
+ pendingInvocation?: PendingInvocation;
389
+ }
390
+ declare function buildActionRunInputs(params: BuildActionRunInputsParams): BuildActionRunInputsResult;
391
+ declare function executeActionBlock(params: ExecuteActionBlockParams): Promise<ExecuteActionBlockResult>;
392
+
393
+ interface AuthorizationResult {
394
+ authorized: boolean;
395
+ reason?: string;
396
+ capabilityId?: string;
397
+ proofCids?: string[];
398
+ }
399
+ /**
400
+ * Check if an actor is authorized to execute a block.
401
+ *
402
+ * Behaviour depends on the flow's schema version:
403
+ * - v0.x (or no version): UCAN is optional. If ucanService is unavailable,
404
+ * execution is allowed without a delegation chain.
405
+ * - v1.0.0+: UCAN is required. A valid delegation chain must exist.
406
+ */
407
+ declare const isAuthorized: (blockId: string, actorDid: string, ucanService: UcanService | undefined, flowUri: string, schemaVersion?: string) => Promise<AuthorizationResult>;
408
+
409
+ /** Registry interface expected by the compiler (keeps it pure / testable). */
410
+ interface CompilerRegistry {
411
+ getActionByCan(can: string): ActionDefinition | undefined;
412
+ }
413
+ /**
414
+ * Compile a Base UCAN flow plan into blocks, graph state, and metadata.
415
+ *
416
+ * This is a **pure function** — no React, no Yjs, no side effects.
417
+ * The output is consumed by `hydrateFlowFromPlan()`.
418
+ */
419
+ declare function compileBaseUcanFlow(plan: BaseUcanFlow, registry: CompilerRegistry): CompiledFlow;
420
+
421
+ /** Describes what changed when merging two compiled flows. */
422
+ interface MergeResult {
423
+ /** The merged compiled flow (full state). */
424
+ merged: CompiledFlow;
425
+ /** Node IDs that were added (new in incoming, not in existing). */
426
+ added: string[];
427
+ /** Node IDs that were replaced (patch only — existed and was overwritten). */
428
+ replaced: string[];
429
+ /** Node IDs that were kept unchanged from existing. */
430
+ kept: string[];
431
+ }
432
+ /**
433
+ * Merge an incoming compiled flow into an existing one.
434
+ *
435
+ * This is a **pure function** — no Yjs, no side effects.
436
+ *
437
+ * Strategies:
438
+ * - `merge`: existing nodes win on ID collision. Incoming nodes with new IDs
439
+ * are added. Existing nodes are never modified.
440
+ * - `patch`: incoming nodes overwrite existing nodes on ID collision. Incoming
441
+ * nodes with new IDs are added. Existing nodes not in incoming are kept.
442
+ *
443
+ * Edges in the merged result are the union of edges from both sides, filtered
444
+ * to those whose source and target both exist in the merged node set, with
445
+ * duplicates collapsed by edge id. Order is the merged node insertion order
446
+ * (existing first, then newly added) — there is no topological sort because
447
+ * there is no inferred dependency relationship.
448
+ */
449
+ declare function mergeCompiledFlows(existing: CompiledFlow, incoming: CompiledFlow, strategy: 'merge' | 'patch'): MergeResult;
450
+
451
+ interface SetupFlowOptions {
452
+ /** The Base UCAN flow plan to compile. */
453
+ plan: BaseUcanFlow;
454
+ /** Matrix room ID to hydrate the flow into. */
455
+ roomId: string;
456
+ /** Authenticated Matrix client. */
457
+ matrixClient: MatrixClient;
458
+ /** DID of the user setting up the flow. */
459
+ creatorDid: string;
460
+ /** Optional doc ID override (defaults to plan.flowId). */
461
+ docId?: string;
462
+ /**
463
+ * Room ID of the template this flow was instantiated from, if any.
464
+ * Recorded into the root map as `source_template_id` for lineage tracking.
465
+ */
466
+ templateId?: string;
467
+ /**
468
+ * How to apply the plan to the existing document.
469
+ * - `full` (default): wipe existing flow state and rebuild entirely.
470
+ * - `merge`: keep existing blocks, add new capabilities from the plan.
471
+ * - `patch`: replace matching nodes, add new ones, keep the rest.
472
+ */
473
+ strategy?: FlowStrategy;
474
+ }
475
+ interface SetupFlowResult {
476
+ /** The compiled flow artifacts. */
477
+ compiled: CompiledFlow;
478
+ /** The room ID (same as input, for convenience). */
479
+ roomId: string;
480
+ /** The flow ID from the plan. */
481
+ flowId: string;
482
+ }
483
+ interface ReadFlowOptions {
484
+ /** Matrix room ID to read from. */
485
+ roomId: string;
486
+ /** Authenticated Matrix client. */
487
+ matrixClient: MatrixClient;
488
+ }
489
+ interface ReadFlowResult {
490
+ /** The current flow plan in BaseUcanFlow format (what the agent reads/writes). */
491
+ plan: BaseUcanFlow;
492
+ /** The compiled flow state (lower-level representation). */
493
+ compiled: CompiledFlow;
494
+ /** The room ID (same as input, for convenience). */
495
+ roomId: string;
496
+ }
497
+ /**
498
+ * Read the current Base UCAN flow plan from a Matrix room.
499
+ *
500
+ * Connects to the room, reads the Y.Doc, decompiles the flow state
501
+ * back into a `BaseUcanFlow`, and disconnects. Returns `null` if the
502
+ * room has no flow state.
503
+ *
504
+ * **This is the lower-level API.** It exists for cases where you need
505
+ * to read a flow from a room you are NOT currently editing — e.g.
506
+ * cross-room operations or background reads. For the common case of
507
+ * "read the flow the user is currently looking at," use
508
+ * `readFlowFromEditor(editor)` instead. That function takes no
509
+ * parameters from the agent and reads from the editor's existing
510
+ * Y.Doc directly — no Matrix connection, no async, no room ID
511
+ * resolution. The browser tool that exposes `read_flow` to the AI
512
+ * agent should wrap `readFlowFromEditor`, not this function.
513
+ */
514
+ declare function readFlowAsBaseUcan(options: ReadFlowOptions): Promise<ReadFlowResult | null>;
515
+ /**
516
+ * Minimal editor shape required by `readFlowFromEditor` and the active
517
+ * editor registry. The full `IxoEditorType` is fine to pass — this is
518
+ * just a structural lower bound so test harnesses can construct fake
519
+ * editors without pulling in React dependencies.
520
+ */
521
+ interface ReadableEditor {
522
+ _yDoc?: Y.Doc;
523
+ getRoomId?: () => string;
524
+ }
525
+ /**
526
+ * Read the current flow from a specific editor instance, synchronously.
527
+ *
528
+ * Use this when you have an editor reference in hand (e.g. from inside a
529
+ * React component, a test, or a multi-editor host that needs to target a
530
+ * specific instance). For the common case of "read the flow the user is
531
+ * currently looking at," prefer the parameterless `readFlow()` below — it
532
+ * uses the active editor registry so you don't have to thread an editor
533
+ * reference through your call sites.
534
+ *
535
+ * Returns `null` ONLY when the editor's Y.Doc has no flow state at all
536
+ * (no nodes AND no meta). An empty `flowId` string is no longer treated
537
+ * as "no flow" — that was the historical bug where successful
538
+ * `setup_flow` calls authored with `flowId: ""` (the documented agent
539
+ * convention) showed up as null on subsequent reads.
540
+ */
541
+ declare function readFlowFromEditor(editor: ReadableEditor): ReadFlowResult | null;
542
+ /**
543
+ * Register an editor as the current active editor. Called by the editor's
544
+ * mount hook when the document is connected and ready. Pass `null` on
545
+ * unmount to clear the registry.
546
+ *
547
+ * If a different editor is already registered, it is replaced silently —
548
+ * the most recent registration wins. This matches the single-editor
549
+ * assumption above.
550
+ */
551
+ declare function setActiveEditor(editor: ReadableEditor | null): void;
552
+ /**
553
+ * Get the currently registered active editor, or null if none is set.
554
+ * Provided for cases where a caller wants to check the registry directly
555
+ * (e.g. for diagnostics) without going through `readFlow()`.
556
+ */
557
+ declare function getActiveEditor(): ReadableEditor | null;
558
+ /**
559
+ * Read the current flow from the active editor, with no parameters at all.
560
+ *
561
+ * This is the parameterless API the AI agent's `read_flow` browser tool
562
+ * should wrap. The browser tool literally becomes `read_flow: () => readFlow()`.
563
+ *
564
+ * Returns `null` if there is no active editor registered (e.g. the editor
565
+ * hasn't finished mounting yet, or no editor is open in this JS context),
566
+ * OR if the active editor's Y.Doc has no flow state at all.
567
+ *
568
+ * If `null` is returned and the user is referencing an existing flow, that
569
+ * is a hard signal to STOP and report to the user — never silently rebuild,
570
+ * because the duplicate-blocks bug is the inevitable consequence.
571
+ */
572
+ declare function readFlow(): ReadFlowResult | null;
573
+ /**
574
+ * One-shot function that compiles a Base UCAN flow plan and writes it
575
+ * into a Matrix room's Y.Doc. After this completes, the room is a
576
+ * normal flow that anyone can open via `useCreateCollaborativeIxoEditor`.
577
+ *
578
+ * Supports three strategies:
579
+ * - `full` (default): clears any existing flow and rebuilds from scratch.
580
+ * - `merge`: adds new capabilities alongside existing ones.
581
+ * - `patch`: replaces matching nodes and adds new ones.
582
+ */
583
+ declare function setupFlowFromBaseUcan(options: SetupFlowOptions): Promise<SetupFlowResult>;
584
+
585
+ /**
586
+ * Read the current compiled flow state from a live Y.Doc.
587
+ *
588
+ * This is the inverse of `hydrateYDocFromCompiledFlow`. It extracts the flow
589
+ * graph state from Yjs maps so that the pure merge/patch logic can operate
590
+ * on plain objects.
591
+ *
592
+ * Returns `null` if the document has no flow state.
593
+ */
594
+ declare function readCompiledFlowFromYDoc(yDoc: Doc): CompiledFlow | null;
595
+
596
+ /**
597
+ * Reconstruct a BaseUcanFlow plan from a CompiledFlow.
598
+ *
599
+ * This is the inverse of `compileBaseUcanFlow`. It allows the AI agent to
600
+ * read the current flow in the same format it writes — a plain capability
601
+ * list it can reason about, modify, and pass back to the compiler.
602
+ *
603
+ * This is a **pure function** — no Yjs, no side effects.
604
+ *
605
+ * Note: some information is lossy (e.g. conditions are stored as a JSON
606
+ * string in props and are not fully round-tripped back to ConditionRef).
607
+ * The core capability data (can, with, nb, trigger, actor, ttl, metadata)
608
+ * is fully preserved.
609
+ */
610
+ declare function decompileToBaseUcanFlow(compiled: CompiledFlow): BaseUcanFlow;
611
+
612
+ /**
613
+ * Top-level Y.Doc map holding the flow's **human** participant roster, keyed by
614
+ * Matrix user id. Deliberately not called "agents": everything under
615
+ * `lib/flowAgent/*` means an AI/oracle actor, and the two must not be conflated.
616
+ *
617
+ * This is template *configuration*, not execution state — the roster records who
618
+ * a flow is meant to be run with, at what Matrix power level, and which of the
619
+ * flow's connections (`connections.ts`) each of them needs access to, so that
620
+ * instantiating a template into a room can invite exactly those users and ask
621
+ * for exactly the right grants. It is therefore NOT cleared by
622
+ * `clearRuntimeForTemplateClone` (`flowEngine/runtime.ts`), which only clears
623
+ * observation/execution maps; a clone must carry the roster forward or the
624
+ * instantiated room comes up empty.
625
+ */
626
+ declare const FLOW_PARTICIPANTS_MAP_KEY = "qi.flow.participants";
627
+ type FlowParticipant = {
628
+ /** Matrix user id, e.g. `'@alice:matrix.example.org'`. */
629
+ userId: string;
630
+ /** Matrix power level; integer, `0` when unknown. */
631
+ powerLevel: number;
632
+ displayName?: string;
633
+ /** `mxc://` url. */
634
+ avatarUrl?: string;
635
+ /**
636
+ * Toolkit keys (`FlowConnection.toolkit`) this participant needs access to.
637
+ * Absent when the participant needs none.
638
+ */
639
+ requires?: string[];
640
+ /** Epoch ms. Supplied by the caller — this module never calls `Date.now()`. */
641
+ addedAt?: number;
642
+ };
643
+ /**
644
+ * Read the participant roster.
645
+ *
646
+ * Normalizes defensively rather than trusting the map: this is read against a
647
+ * live, replicated doc, so an entry can be observed mid-sync, written by an older
648
+ * client, or malformed outright. Anything that is not a `Y.Map` carrying a
649
+ * non-empty `userId` is skipped, and a read never throws.
650
+ *
651
+ * Order is stable across peers — `addedAt` ascending with unstamped entries last,
652
+ * tie-broken by `userId` — so a rendered roster does not reshuffle on every sync.
653
+ */
654
+ declare function readFlowParticipants(yDoc: Doc): FlowParticipant[];
655
+ /**
656
+ * Add a participant, or merge into the existing entry for the same `userId`.
657
+ *
658
+ * `addedAt` is write-once: an existing stamp is preserved so a later edit
659
+ * (a power-level bump, a display-name refresh) cannot reorder the roster.
660
+ * Optional string fields are merged — omit one to leave it as it is, pass `''`
661
+ * to clear it; `requires` follows the same rule with `[]` as the clearing value.
662
+ */
663
+ declare function upsertFlowParticipant(yDoc: Doc, participant: FlowParticipant): void;
664
+ /** Remove a participant. No-op when the roster has no entry for `userId`. */
665
+ declare function removeFlowParticipant(yDoc: Doc, userId: string): void;
666
+ /**
667
+ * Set an existing participant's power level. No-op when the participant is not on
668
+ * the roster — a power level alone carries no identity, so creating an entry from
669
+ * one would invent a participant nobody added.
670
+ */
671
+ declare function setFlowParticipantPowerLevel(yDoc: Doc, userId: string, powerLevel: number): void;
672
+ /**
673
+ * Replace the connections an existing participant needs access to. Pass `[]` to
674
+ * clear them. No-op when the participant is not on the roster, for the same
675
+ * reason as {@link setFlowParticipantPowerLevel}.
676
+ */
677
+ declare function setFlowParticipantRequirements(yDoc: Doc, userId: string, requires: string[]): void;
678
+
679
+ /**
680
+ * Top-level Y.Doc map holding the integrations a flow needs, keyed by toolkit
681
+ * slug. This is template *configuration* — "this flow talks to Xero, and needs
682
+ * an organisation and a bank account picked before it can run" — so it is NOT
683
+ * cleared by `clearRuntimeForTemplateClone` (`flowEngine/runtime.ts`), exactly
684
+ * like the participant roster: a clone that lost its requirements would come up
685
+ * looking ready while every integration call has nowhere to go.
686
+ */
687
+ declare const FLOW_CONNECTIONS_MAP_KEY = "qi.flow.connections";
688
+ /**
689
+ * Top-level Y.Doc map holding the concrete account/organisation/bank selections
690
+ * made for *this* flow, keyed by toolkit slug.
691
+ *
692
+ * Bindings are execution state, not configuration: they name one workspace's
693
+ * connected account. `clearRuntimeForTemplateClone` therefore clears this map —
694
+ * a template cloned out of a live flow must not carry another entity's Xero
695
+ * account into a room whose members were never granted access to it.
696
+ */
697
+ declare const FLOW_CONNECTION_BINDINGS_MAP_KEY = "qi.flow.connectionBindings";
698
+ /** Sub-selections a toolkit needs before it counts as satisfied. */
699
+ type FlowConnectionRequirementKey = 'org' | 'bankAccount';
700
+ type FlowConnection = {
701
+ /** Toolkit slug, e.g. `'xero'`. Also the map key — one entry per toolkit. */
702
+ toolkit: string;
703
+ label?: string;
704
+ /** Absent/false = required. True = the flow may start without it. */
705
+ optional?: boolean;
706
+ requires: FlowConnectionRequirementKey[];
707
+ /** Epoch ms. Supplied by the caller — this module never calls `Date.now()`. */
708
+ addedAt?: number;
709
+ };
710
+ type FlowConnectionBinding = {
711
+ /** Toolkit slug, e.g. `'xero'`. Also the map key — one entry per toolkit. */
712
+ toolkit: string;
713
+ connectedAccountId: string;
714
+ /** Entity the connection belongs to — guards template-copy across workspaces. */
715
+ entityDid: string;
716
+ /** Xero organisation (tenant). */
717
+ tenantId?: string;
718
+ /** Xero chart-of-accounts UUID for the bank/treasury account. */
719
+ bankAccountId?: string;
720
+ /** Epoch ms. Supplied by the caller — this module never calls `Date.now()`. */
721
+ boundAt?: number;
722
+ boundByDid?: string;
723
+ };
724
+ /**
725
+ * Read the connections a flow requires.
726
+ *
727
+ * Normalizes defensively rather than trusting the map: this is read against a
728
+ * live, replicated doc, so an entry can be observed mid-sync, written by an older
729
+ * client, or malformed outright. Anything that is not a `Y.Map` carrying a
730
+ * non-empty `toolkit` is skipped, and a read never throws.
731
+ *
732
+ * Order is stable across peers — `addedAt` ascending with unstamped entries last,
733
+ * tie-broken by `toolkit` — so a rendered list does not reshuffle on every sync.
734
+ */
735
+ declare function readFlowConnections(yDoc: Doc): FlowConnection[];
736
+ /**
737
+ * Add a connection, or merge into the existing entry for the same `toolkit`.
738
+ *
739
+ * `addedAt` is write-once: an existing stamp is preserved so a later edit (a
740
+ * label refresh, a requirement change) cannot reorder the list. `requires` is
741
+ * part of the connection's identity as a requirement and is always written —
742
+ * pass the full list. `optional` and the optional string fields are merged —
743
+ * omit one to leave it as it is, pass `''` (or `false`) to clear it.
744
+ */
745
+ declare function upsertFlowConnection(yDoc: Doc, connection: FlowConnection): void;
746
+ /** Remove a connection. No-op when the flow has no entry for `toolkit`. */
747
+ declare function removeFlowConnection(yDoc: Doc, toolkit: string): void;
748
+ /**
749
+ * Mark an existing connection optional or required. No-op when the flow has no
750
+ * entry for `toolkit` — a flag alone carries no identity, so creating an entry
751
+ * from one would invent a requirement nobody declared.
752
+ */
753
+ declare function setFlowConnectionOptional(yDoc: Doc, toolkit: string, optional: boolean): void;
754
+ /**
755
+ * Replace an existing connection's sub-selection requirements. No-op when the
756
+ * flow has no entry for `toolkit`, for the same reason as
757
+ * {@link setFlowConnectionOptional}.
758
+ */
759
+ declare function setFlowConnectionRequires(yDoc: Doc, toolkit: string, requires: FlowConnectionRequirementKey[]): void;
760
+ /**
761
+ * Read the concrete connection bindings for this flow.
762
+ *
763
+ * Normalized as defensively as {@link readFlowConnections}: an entry is skipped
764
+ * unless it carries the three fields that make a binding usable — `toolkit`,
765
+ * `connectedAccountId` and `entityDid`. A half-written binding is not a binding
766
+ * to a different account, it is no binding at all.
767
+ *
768
+ * Order is stable across peers — `boundAt` ascending with unstamped entries last,
769
+ * tie-broken by `toolkit`.
770
+ */
771
+ declare function readFlowConnectionBindings(yDoc: Doc): FlowConnectionBinding[];
772
+ /**
773
+ * Bind a toolkit to a connected account, or merge into the existing binding for
774
+ * the same `toolkit`.
775
+ *
776
+ * `boundAt` is write-once, so re-picking an organisation does not reorder the
777
+ * list. Optional string fields are merged — omit one to leave it as it is, pass
778
+ * `''` to clear it. Ignored unless `toolkit`, `connectedAccountId` and
779
+ * `entityDid` are all present: those three are what a binding *is*.
780
+ */
781
+ declare function upsertFlowConnectionBinding(yDoc: Doc, binding: FlowConnectionBinding): void;
782
+ /** Remove a binding. No-op when the flow has no binding for `toolkit`. */
783
+ declare function removeFlowConnectionBinding(yDoc: Doc, toolkit: string): void;
784
+
785
+ type FlowAgentPublicNodeState = 'Pending' | 'Blocked' | 'Overdue' | 'Done';
786
+ type FlowAgentRunPhase = 'Running' | 'Validating' | 'Failed' | 'Archived';
787
+ type FlowAgentBlockerCause = 'missing_input' | 'failed_upstream' | 'missing_ucan' | 'stale_config' | 'service_error' | 'external_confirmation_pending' | 'validation_mismatch' | 'unverified_completion' | 'awaiting_verification' | 'unknown';
788
+ type FlowAgentCommandType = 'diagnose_blocker' | 'assign_actor' | 'notify_actor' | 'execute_action' | 'validate_external_state' | 'archive_flow' | 'propose_config_change';
789
+ type FlowAgentCommandStatus = 'queued' | 'leased' | 'running' | 'confirmed' | 'awaiting_readback' | 'failed' | 'skipped';
790
+ type FlowAgentLedgerEventType = 'agent.decision' | 'agent.command' | 'agent.validation' | 'agent.escalation' | 'agent.memory';
791
+ interface FlowAgentActor {
792
+ did: string;
793
+ matrixUserId?: string;
794
+ displayName?: string;
795
+ skills?: string[];
796
+ }
797
+ interface FlowAgentLease {
798
+ id: string;
799
+ commandId: string;
800
+ sessionRunId: string;
801
+ nodeId: string;
802
+ actorDid: string;
803
+ acquiredAt: number;
804
+ expiresAt: number;
805
+ epoch: number;
806
+ }
807
+ interface FlowAgentNodeSnapshot {
808
+ nodeId: string;
809
+ blockType: string;
810
+ actionType?: string;
811
+ title?: string;
812
+ runtime: FlowNodeRuntimeState;
813
+ publicState: FlowAgentPublicNodeState;
814
+ blockerCause?: FlowAgentBlockerCause;
815
+ assigneeDid?: string;
816
+ dueAt?: number;
817
+ pendingInvocationCount: number;
818
+ }
819
+ interface FlowAgentCommandBase {
820
+ id: string;
821
+ type: FlowAgentCommandType;
822
+ flowId: string;
823
+ sessionRunId: string;
824
+ flowUri: string;
825
+ nodeId: string;
826
+ actorDid: string;
827
+ status: FlowAgentCommandStatus;
828
+ capability: UcanCapability;
829
+ idempotencyKey: string;
830
+ createdAt: number;
831
+ updatedAt: number;
832
+ reason: string;
833
+ payload: Record<string, unknown>;
834
+ lease?: FlowAgentLease;
835
+ error?: string;
836
+ }
837
+ type FlowAgentCommand = FlowAgentCommandBase;
838
+ interface FlowAgentLedgerEvent {
839
+ id: string;
840
+ type: FlowAgentLedgerEventType;
841
+ flowId: string;
842
+ sessionRunId: string;
843
+ nodeId?: string;
844
+ commandId?: string;
845
+ actorDid: string;
846
+ timestamp: number;
847
+ details: Record<string, unknown>;
848
+ }
849
+ interface FlowAgentMaps {
850
+ outbox: Map<FlowAgentCommand>;
851
+ leases: Map<FlowAgentLease>;
852
+ }
853
+ interface FlowAgentPolicyDecision {
854
+ allowed: boolean;
855
+ reason: string;
856
+ capability: UcanCapability;
857
+ proofCids: string[];
858
+ }
859
+ interface FlowAgentContext {
860
+ yDoc: Doc;
861
+ editor?: IxoEditorType;
862
+ /** Planner targets, including executable actions and manual work nodes. */
863
+ blocks?: unknown[];
864
+ /** Full recursive document used to resolve condition source blocks. */
865
+ documentBlocks?: unknown[];
866
+ flowId: string;
867
+ sessionRunId: string;
868
+ flowUri: string;
869
+ actor: FlowAgentActor;
870
+ now?: () => number;
871
+ }
872
+ interface FlowAgentCommandResult {
873
+ commandId: string;
874
+ success: boolean;
875
+ output?: Record<string, unknown>;
876
+ confirmed?: boolean;
877
+ completionState?: ActionExecutionCompletionState;
878
+ status?: FlowAgentCommandStatus;
879
+ error?: string;
880
+ }
881
+ interface FlowAgentExecutor {
882
+ executeAction?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
883
+ assignActor?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
884
+ notifyActor?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
885
+ validateExternalState?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
886
+ archiveFlow?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
887
+ proposeConfigChange?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
888
+ diagnoseBlocker?: (command: FlowAgentCommand, context: FlowAgentContext) => Promise<FlowAgentCommandResult>;
889
+ }
890
+ interface FlowAgentTickResult {
891
+ flowDone: boolean;
892
+ snapshots: FlowAgentNodeSnapshot[];
893
+ queuedCommands: FlowAgentCommand[];
894
+ executedCommands: FlowAgentCommandResult[];
895
+ }
896
+
897
+ interface CreateAgentCommandParams {
898
+ type: FlowAgentCommandType;
899
+ flowId: string;
900
+ sessionRunId: string;
901
+ flowUri: string;
902
+ nodeId: string;
903
+ actor: FlowAgentActor;
904
+ reason: string;
905
+ payload?: Record<string, unknown>;
906
+ now?: number;
907
+ }
908
+ declare function createAgentCommand({ type, flowId, sessionRunId, flowUri, nodeId, actor, reason, payload, now }: CreateAgentCommandParams): FlowAgentCommand;
909
+ declare function validateAgentCommand(command: FlowAgentCommand): {
910
+ valid: boolean;
911
+ error?: string;
912
+ };
913
+ declare function isExternalMutation(type: FlowAgentCommandType): boolean;
914
+
915
+ interface BuildFlowAgentContextParams {
916
+ yDoc: Doc;
917
+ flowId: string;
918
+ sessionRunId: string;
919
+ flowUri?: string;
920
+ actor: FlowAgentActor;
921
+ blocks?: unknown[];
922
+ documentBlocks?: unknown[];
923
+ editor?: IxoEditorType;
924
+ now?: () => number;
925
+ }
926
+ /**
927
+ * Builds the host-facing runtime context expected by the Flow Agent.
928
+ *
929
+ * Headless hosts own Matrix login, room joins, and Y.Doc sync. Once a host has
930
+ * a live room document and an agent identity, this helper gives it the stable
931
+ * context shape to pass into `tickFlowAgent` or `FlowAgentService`.
932
+ */
933
+ declare function buildFlowAgentContext({ yDoc, flowId, sessionRunId, flowUri, actor, blocks, documentBlocks, editor, now, }: BuildFlowAgentContextParams): FlowAgentContext;
934
+
935
+ interface AcquireFlowAgentLeaseParams {
936
+ leases: Map<FlowAgentLease>;
937
+ commandId: string;
938
+ sessionRunId: string;
939
+ nodeId: string;
940
+ actorDid: string;
941
+ now?: number;
942
+ ttlMs?: number;
943
+ }
944
+ declare function acquireFlowAgentLease({ leases, commandId, sessionRunId, nodeId, actorDid, now, ttlMs, }: AcquireFlowAgentLeaseParams): FlowAgentLease | null;
945
+ declare function validateFlowAgentLease(leases: Map<FlowAgentLease>, lease: FlowAgentLease, now?: number): boolean;
946
+ declare function releaseFlowAgentLease(leases: Map<FlowAgentLease>, lease: FlowAgentLease): boolean;
947
+ declare function cleanupExpiredFlowAgentLeases(leases: Map<FlowAgentLease>, now?: number): FlowAgentLease[];
948
+
949
+ declare function requiredCapabilityForCommand(type: FlowAgentCommandType, flowUri: string, nodeId: string): UcanCapability;
950
+ declare function isCapabilityMatch(granted: UcanCapability, required: UcanCapability): boolean;
951
+ /**
952
+ * Match a granted capability pattern against a required `can`.
953
+ *
954
+ * ⚠️ This is the **trusted flow-agent policy** matcher: it allows the global
955
+ * `'*'` wildcard, so a delegation granting `can: '*'` matches every ability.
956
+ * It is re-exported from `src/core/index.ts` — do not use it to gate public or
957
+ * untrusted matching. For that, call `capabilityPatternCoversCan` directly and
958
+ * leave `allowGlobalWildcard` off (the default), which rejects `'*'`.
959
+ *
960
+ * Note: the granted side is normalized (`normalizeCan`), so dotted legacy
961
+ * grants such as `flow.notify` / `flow.*` now match `flow/notify` where they
962
+ * were previously inert.
963
+ */
964
+ declare function canMatches(granted: string, required: string): boolean;
965
+ declare function resourceMatches(granted: string, required: string): boolean;
966
+ interface EvaluateFlowAgentPolicyParams {
967
+ actorDid: string;
968
+ commandType: FlowAgentCommandType;
969
+ flowUri: string;
970
+ nodeId: string;
971
+ delegationStore?: UcanDelegationStore;
972
+ delegations?: StoredDelegation[];
973
+ now?: number;
974
+ }
975
+ declare function evaluateFlowAgentPolicy({ actorDid, commandType, flowUri, nodeId, delegationStore, delegations, now, }: EvaluateFlowAgentPolicyParams): FlowAgentPolicyDecision;
976
+
977
+ interface FlowAgentOrchestratorOptions {
978
+ delegationStore?: UcanDelegationStore;
979
+ delegations?: StoredDelegation[];
980
+ candidateActors?: FlowAgentActor[];
981
+ executor?: FlowAgentExecutor;
982
+ leaseTtlMs?: number;
983
+ archiveWhenDone?: boolean;
984
+ }
985
+ declare function planRalphLoopCommands(context: FlowAgentContext, options?: FlowAgentOrchestratorOptions): {
986
+ snapshots: FlowAgentNodeSnapshot[];
987
+ queuedCommands: FlowAgentCommand[];
988
+ flowDone: boolean;
989
+ };
990
+ declare function executeQueuedAgentCommands(context: FlowAgentContext, options?: FlowAgentOrchestratorOptions): Promise<FlowAgentCommandResult[]>;
991
+ declare function tickFlowAgent(context: FlowAgentContext, options?: FlowAgentOrchestratorOptions): Promise<FlowAgentTickResult>;
992
+
993
+ interface FlowAgentServiceOptions extends FlowAgentOrchestratorOptions {
994
+ intervalMs?: number;
995
+ onTick?: (result: FlowAgentTickResult) => void | Promise<void>;
996
+ onError?: (error: unknown) => void;
997
+ }
998
+ /**
999
+ * Headless-service adapter boundary.
1000
+ *
1001
+ * Host applications own Matrix login, room joins, and Y.Doc sync. Once they
1002
+ * have a live Y.Doc/editor snapshot, this service provides the deterministic
1003
+ * Ralph-loop tick and command execution cycle.
1004
+ */
1005
+ declare class FlowAgentService {
1006
+ private readonly context;
1007
+ private readonly options;
1008
+ private timer;
1009
+ private running;
1010
+ constructor(context: FlowAgentContext, options?: FlowAgentServiceOptions);
1011
+ tick(): Promise<FlowAgentTickResult>;
1012
+ start(): void;
1013
+ stop(): void;
1014
+ }
1015
+
1016
+ declare function getFlowAgentMaps(yDoc: Doc): FlowAgentMaps;
1017
+ declare function computeAgentCommandId(params: {
1018
+ flowId: string;
1019
+ sessionRunId: string;
1020
+ nodeId: string;
1021
+ type: string;
1022
+ payload: Record<string, unknown>;
1023
+ }): string;
1024
+ declare function queueAgentCommand(yDoc: Doc, command: FlowAgentCommand): {
1025
+ command: FlowAgentCommand;
1026
+ created: boolean;
1027
+ };
1028
+ /** Read every command for one session without creating the outbox map. */
1029
+ declare function readAgentCommands(yDoc: Doc, sessionRunId?: string): FlowAgentCommand[];
1030
+ declare function readQueuedAgentCommands(yDoc: Doc, sessionRunId?: string): FlowAgentCommand[];
1031
+ /** Read every lease for one session without creating the leases map. */
1032
+ declare function readFlowAgentLeases(yDoc: Doc, sessionRunId?: string): FlowAgentLease[];
1033
+ declare function updateAgentCommand(yDoc: Doc, commandId: string, patch: Partial<FlowAgentCommand>): FlowAgentCommand | null;
1034
+ declare function appendAgentLedgerEvent(yDoc: Doc, event: Omit<FlowAgentLedgerEvent, 'id'> & {
1035
+ id?: string;
1036
+ }): FlowAgentLedgerEvent;
1037
+ declare function readAgentLedgerEvents(yDoc: Doc, eventType?: FlowAgentLedgerEventType, sessionRunId?: string): FlowAgentLedgerEvent[];
1038
+
1039
+ export { buildFlowAgentContext as $, type AuthorizationResult as A, readFlowConnectionBindings as B, upsertFlowConnectionBinding as C, removeFlowConnectionBinding as D, type ExecuteNodeParams as E, FLOW_PARTICIPANTS_MAP_KEY as F, FLOW_CONNECTIONS_MAP_KEY as G, FLOW_CONNECTION_BINDINGS_MAP_KEY as H, type SetupFlowResult as I, type ReadFlowResult as J, type ReadableEditor as K, type CompilerRegistry as L, type MergeResult as M, type NodeActionResult as N, type FlowParticipant as O, type FlowConnection as P, type FlowConnectionBinding as Q, type ReadFlowOptions as R, type SetupFlowOptions as S, type FlowConnectionRequirementKey as T, type BaseUcanFlow as U, type FlowCapability as V, type CompiledFlow as W, type FlowStrategy as X, FlowAgentService as Y, acquireFlowAgentLease as Z, appendAgentLedgerEvent as _, buildAuthzFromProps as a, type TriggerSpec as a$, cleanupExpiredFlowAgentLeases as a0, createAgentCommand as a1, evaluateFlowAgentPolicy as a2, executeQueuedAgentCommands as a3, getFlowAgentMaps as a4, planRalphLoopCommands as a5, queueAgentCommand as a6, readAgentLedgerEvents as a7, readQueuedAgentCommands as a8, releaseFlowAgentLease as a9, isCapabilityMatch as aA, isExternalMutation as aB, readAgentCommands as aC, readFlowAgentLeases as aD, requiredCapabilityForCommand as aE, resourceMatches as aF, updateAgentCommand as aG, type AcquireFlowAgentLeaseParams as aH, type CreateAgentCommandParams as aI, type EvaluateFlowAgentPolicyParams as aJ, type FlowAgentOrchestratorOptions as aK, type FlowAgentServiceOptions as aL, type FlowAgentCommandBase as aM, type FlowAgentCommandStatus as aN, type FlowAgentCommandType as aO, type FlowAgentLedgerEvent as aP, type FlowAgentLedgerEventType as aQ, type FlowAgentMaps as aR, type FlowAgentPolicyDecision as aS, type FlowAgentRunPhase as aT, resolveRuntimeRefs as aU, type TriggerResolutionContext as aV, type ConditionRef as aW, type ActorConstraint as aX, type TTLConstraint as aY, type RuntimeRef as aZ, type CompiledFlowNode as a_, tickFlowAgent as aa, validateAgentCommand as ab, validateFlowAgentLease as ac, type BuildFlowAgentContextParams as ad, type FlowAgentActor as ae, type FlowAgentCommand as af, type FlowAgentCommandResult as ag, type FlowAgentContext as ah, type FlowAgentExecutor as ai, type FlowAgentLease as aj, type FlowAgentNodeSnapshot as ak, type FlowAgentPublicNodeState as al, type FlowAgentTickResult as am, type CompiledBlock as an, type CompiledEdge as ao, type FlowAgentBlockerCause as ap, buildActionRunInputs as aq, executeActionBlock as ar, type ActionBlockLike as as, type ActionExecutionCompletionState as at, type BuildActionRunInputsParams as au, type BuildActionRunInputsResult as av, type ExecuteActionBlockParams as aw, type ExecuteActionBlockResult as ax, canMatches as ay, computeAgentCommandId as az, buildFlowNodeFromBlock as b, isRuntimeRef as b0, type ExecutionOutcome as c, type ExecutionContext as d, executeNode as e, readFlowFromEditor as f, readFlow as g, setActiveEditor as h, isAuthorized as i, getActiveEditor as j, compileBaseUcanFlow as k, readCompiledFlowFromYDoc as l, mergeCompiledFlows as m, decompileToBaseUcanFlow as n, readFlowParticipants as o, removeFlowParticipant as p, setFlowParticipantPowerLevel as q, readFlowAsBaseUcan as r, setupFlowFromBaseUcan as s, setFlowParticipantRequirements as t, upsertFlowParticipant as u, readFlowConnections as v, upsertFlowConnection as w, removeFlowConnection as x, setFlowConnectionOptional as y, setFlowConnectionRequires as z };