import type { LaunchMetadata, QueryDefinition, SearchAttributeValue, SignalDefinition, SignalDeliveryOptions, UpdateDefinition, WorkflowState } from '../types.ts'; import type { WorkflowSnapshot } from '../types/workflow-snapshot.ts'; /** * Which atomic path a `startOrSignal` call took, returned alongside the * {@link WorkflowHandle} in `StartOrSignalResult` (not a field on the handle — * converged concurrent callers share one cached engine handle, so the outcome * rides the per-call result instead). `'started'` when the call created the * run, `'signalled'` when it delivered a signal to a run that already existed. */ export type StartOrSignalOutcome = 'started' | 'signalled'; export declare function getWorkflowExecutionStartedAt(state: Pick): number; export declare const HANDLE_RESULT_PROMISE: unique symbol; export interface WorkflowHandleEngine extends EventTarget { [HANDLE_RESULT_PROMISE](workflowId: string): Promise; cancel(workflowId: string): Promise; suspend(workflowId: string): Promise; resume(workflowId: string): Promise; signal(workflowId: string, name: string, payload?: unknown, options?: SignalDeliveryOptions): Promise; update(workflowId: string, name: string, payload?: unknown, options?: { timeout?: number; }): Promise; query(workflowId: string, name: string, input?: unknown): Promise; getAttributes(workflowId: string): Promise | null>; setAttributes(workflowId: string, attributes: Record): Promise; addTags(workflowId: string, ...tags: string[]): Promise; removeTags(workflowId: string, ...tags: string[]): Promise; get(workflowId: string): Promise; /** * Current checkpoint step (the run's cursor) for a workflow, or `null` when no * checkpoint exists. Reads the in-memory checkpoint when the run is live in * this engine, otherwise the durably persisted checkpoint — so it is correct * for both an in-flight run and one recovered or inspected in a fresh process. */ getCurrentCheckpointStep(workflowId: string): Promise; } /** * Handle to a running or completed workflow. Returned by {@link Engine.start} * and {@link Engine.getHandle}. Use `handle.result()` to await the final * value, `handle.cancel()` to stop execution, `handle.signal(name, payload)` * to send a signal, and `handle.update(name, payload)` to send a synchronous * update. Use `query()`, `getAttributes()`/`setAttributes()`, and * `addTags()`/`removeTags()` for read-only handlers, search metadata, and tag * management. Also an `AsyncIterable` of lifecycle events. * * @example * ```ts * import { workflow, Engine, WorkflowHandle, activity } from '@lostgradient/weft'; * import type { WorkflowContext, Context } from '@lostgradient/weft'; * * const greet = activity({ name: 'greet', execute: async (i: unknown) => `hi ${i}` }); * const engine = new Engine(); * engine.register( * workflow({ name: 'wave' }).execute(async function* (ctx: WorkflowContext, input: unknown) { * return yield* ctx.run(greet, input); * }), * ); * * const handle = await engine.start('wave', 'world'); * const typedHandle: WorkflowHandle = handle; * const result = await handle.result(); * void typedHandle; * console.log(result); // 'hi world' * ``` * * @example Iterate workflow lifecycle events * ```ts * import { Engine, workflow, type WorkflowHandle } from '@lostgradient/weft'; * * const engine = new Engine(); * engine.register(workflow({ name: 'ping' }).execute(async function* () { return 'pong'; })); * * const handle = await engine.start('ping', null); * const typedHandle: WorkflowHandle = handle; * for await (const event of handle) { * console.log(event.type); * } * void typedHandle; * ``` */ export declare class WorkflowHandle extends EventTarget implements AsyncDisposable { #private; readonly id: string; constructor(id: string, engine: WorkflowHandleEngine); result(): Promise; cancel(): Promise; /** * Suspend this workflow without terminating it: it transitions to the * non-terminal `'suspended'` status, keeps its durable checkpoint, and is * later resumable via {@link WorkflowHandle.resume}. Unlike `cancel()`, this * does not run cancel handlers and does not settle `result()` — the result * promise stays pending until a later `resume()` completes the run. A * suspended workflow is NOT auto-recovered by `engine.recoverAll()`; resume it * explicitly. Suspending a workflow that is not running is a no-op. */ suspend(): Promise; /** * Resume this workflow from its persisted checkpoint after it was suspended * (or left `'running'` by a prior process). The run is re-driven on this * engine; `result()` on this handle resolves when the resumed run completes. * Throws if the workflow is in a status that cannot be resumed (terminal, * pending, or not found). */ resume(): Promise; /** * Reconstruct this workflow's launch context — its original `input` and the * launch options recoverable from durable state — from the persisted * {@link WorkflowState}. Resolves `null` if the workflow no longer exists * (never started, or purged). * * Designed for the post-`recoverAll()` case: a recovered handle can recover * the input a run was started with (and its `id`/`tags`) without the caller * keeping a side table correlating recovered workflows back to their launch * context. This is an async read (it loads state) so it behaves identically * on handles from `start()`, `recoverAll()`, and `getHandle()` — none of which * is special-cased — rather than a sync property that would be `undefined` on * a handle created without a state load. * * @example * ```ts * import { Engine } from '@lostgradient/weft'; * * const engine = new Engine(); * const handles = await engine.recoverAll(); * for (const handle of handles) { * const metadata = await handle.getLaunchMetadata(); * if (metadata) { * // rebuild this run's dependencies from metadata.input * void metadata.input; * } * } * ``` */ getLaunchMetadata(): Promise; /** * A point-in-time view of this workflow's progress: its status and current * checkpoint step (cursor). Resolves `null` if the workflow no longer exists. * The `status` matches `engine.get(id)` — notably it reports `'pending'` for a * run whose inline start is still queued, even though its persisted status is * `'running'`. * * Designed for observing a recovered run: after `engine.recoverAll()`, a * caller can read where a resumed run currently is — and rebuild its own * progress adapter to re-register the run on a live surface — without waiting * for the run's final `result()`. It is an async read (loads state + * checkpoint), so it behaves identically on handles from `start()`, * `recoverAll()`, and `getHandle()`. * * @example * ```ts * import { Engine } from '@lostgradient/weft'; * * const engine = new Engine(); * const handles = await engine.recoverAll(); * for (const handle of handles) { * const snapshot = await handle.snapshot(); * if (snapshot) { * // re-register a progress adapter at snapshot.step * void snapshot.step; * } * } * ``` */ snapshot(): Promise; signal(name: SignalDefinition): Promise; signal(name: SignalDefinition, payload: TInput, options?: SignalDeliveryOptions): Promise; signal(name: string, payload?: unknown, options?: SignalDeliveryOptions): Promise; update(name: UpdateDefinition, payload?: void, options?: { timeout?: number; }): Promise; update(name: UpdateDefinition, payload: TInput, options?: { timeout?: number; }): Promise; update(name: string, payload?: unknown, options?: { timeout?: number; }): Promise; query(name: QueryDefinition): Promise; query(name: QueryDefinition, input: TInput): Promise; query(name: string, input?: unknown): Promise; getAttributes(): Promise | null>; setAttributes(attributes: Record): Promise; addTags(...tags: string[]): Promise; removeTags(...tags: string[]): Promise; [Symbol.asyncIterator](): AsyncIterableIterator; [Symbol.observable](): { subscribe: (observer: { next?: (event: Event) => void; complete?: () => void; error?: (error: Error) => void; }) => { unsubscribe: () => void; }; }; [Symbol.asyncDispose](): Promise; }