Application Work
Exact spawn, getWork, WorkHandle, AgentWorkHandle, status, event, control, and statistics contracts.
Application Work accepts an exported Flow or process-local Agent and returns a typed live handle for one finite occurrence. Flow Work is durable through a Runtime host. Agent Work in this slice is process-local and honest about that limit.
import {
createAgentWorkHost,
createWorkHost,
getWork,
spawn,
type AgentWorkHandle,
type WorkHandle,
} from "@use-crux/core";spawn()
const work = await spawn(reviewDocument, input, {
idempotencyKey: "request_123",
});
const inputlessWork = await spawn(refreshIndex, {
idempotencyKey: "refresh_123",
});For an inputless Flow, omit input. Acceptance atomically writes the canonical
Runtime Work record, initial Flow snapshot, definition identity, result
obligation, and wake outbox row. A compatible idempotent retry returns the same
occurrence. The key is scoped by Runtime namespace and target.
getWork()
const work = await getWork(reviewDocument, workId);Reconnects retained Work without executing it. The exported target must match the target accepted for the Work ID.
Both functions require an active host binding:
const host = createWorkHost({ runtime, program });
const work = await host.run(() => getWork(reviewDocument, workId));WorkHandle<TResult>
interface WorkHandle<TResult> {
readonly id: string;
readonly effects: EffectScopeRef;
status(): Promise<WorkStatus>;
result(): Promise<TResult>;
progress(update: WorkProgress): Promise<void>;
cancel(options?: CancelOptions): Promise<CancelReceipt>;
detach(): Promise<DetachReceipt>;
stream(options?: WorkStreamOptions): AsyncIterable<WorkEvent>;
stats(): Promise<ExecutionStats>;
}effects is the stable Effect scope allocated at acceptance. The handle is a
live control reference, not a serializable durable reference; persist id and
reconnect with getWork().
Process-local Agent Work
const host = createAgentWorkHost({ executor });
const child = await host.run(() =>
spawn(researcher, { task: "Investigate the regression." }),
);
await child.send("Prioritize primary sources.");
const report = await child.result();interface AgentWorkHandle<TResult> extends WorkHandle<TResult> {
send(content: string | readonly ContentPart[]): Promise<WorkSteeringReceipt>;
}Agent spawn requires createAgentWorkHost and returns AgentWorkHandle. Flow
handles remain ordinary WorkHandle values and do not expose send at compile
time. Steering is ordered, payload-safe in identity records (Blob/byte sources
are hashed; unsupported opaque sources reject), and delivered only at the next
semantic provider-step boundary. Agent-tool occurrence identity is partitioned
by parent execution owner and turn/step so concurrent requests cannot
cross-connect. Process exit loses the registry, pending steering, and rejoin
capability—there is no durable Agent-child execution in this slice.
status()
Returns a safe snapshot whose state is queued, running, suspended,
blocked, completed, failed, or cancelled. Every state includes id,
ownership, updatedAt, and optional latest progress. Status never exposes
the result or a raw failure.
type WorkStatus =
| (WorkStatusBase & { readonly state: "queued"; readonly acceptedAt: Date })
| (WorkStatusBase & { readonly state: "running"; readonly startedAt: Date })
| (WorkStatusBase & {
readonly state: "suspended";
readonly suspendedOn: WorkSuspensionSummary;
})
| (WorkStatusBase & {
readonly state: "blocked";
readonly blockedOn: WorkBlockSummary;
})
| (WorkStatusBase & {
readonly state: "completed";
readonly completedAt: Date;
readonly resultAvailable: boolean;
})
| (WorkStatusBase & {
readonly state: "failed";
readonly failedAt: Date;
readonly failure: WorkFailure;
})
| (WorkStatusBase & {
readonly state: "cancelled";
readonly cancelledAt: Date;
readonly reason?: string;
});
interface WorkStatusBase {
readonly id: string;
readonly progress?: WorkProgressSnapshot;
readonly ownership: WorkOwnership;
readonly updatedAt: Date;
}
type WorkOwnership =
| { readonly state: "attached" }
| {
readonly state: "detached";
readonly reason: "explicit" | "owner-ended";
readonly detachedAt: Date;
};result()
Waits for the exact inferred successful Flow result. It throws:
| Error | Meaning |
|---|---|
WorkFailedError | Work reached a safe terminal failure. |
WorkCancelledError | Cooperative cancellation terminalized Work. |
WorkResultExpiredError | The terminal result payload is not retained. |
Joining never re-enqueues or re-executes Work.
progress()
interface WorkProgress {
readonly message?: string; // at most 1,024 characters
readonly current?: number; // finite and non-negative
readonly total?: number; // finite and non-negative
}Replaces the full latest snapshot and publishes one safe ordered progress
event. When both counts are present, current <= total. Terminal Work throws
WorkNotActiveError.
cancel()
interface CancelOptions {
readonly reason?: string; // at most 512 safe characters
}
interface CancelReceipt {
readonly workId: string;
readonly outcome: "cancelled" | "already-terminal";
readonly status: Extract<
WorkStatus,
{ readonly state: "completed" | "failed" | "cancelled" }
>;
}Cancellation is idempotent and cooperative. The terminal transaction preserves a completion that wins the race and cancels owned durable wait registrations when cancellation wins.
detach()
interface DetachReceipt {
readonly workId: string;
readonly outcome: "detached" | "already-detached" | "already-terminal";
readonly ownership: WorkOwnership;
}Detachment changes ownership only. It does not cancel, wake, or re-execute Work.
stream()
interface WorkStreamOptions {
readonly after?: string;
}
type WorkEvent =
| (WorkEventBase & {
readonly type: "work.snapshot";
readonly status: WorkStatus;
})
| (WorkEventBase & {
readonly type: "work.status";
readonly status: WorkStatus;
})
| (WorkEventBase & {
readonly type: "work.progress";
readonly progress: WorkProgressSnapshot;
});
interface WorkEventBase {
readonly id: string;
readonly cursor: string;
readonly workId: string;
readonly occurredAt: Date;
}Every event has id, opaque cursor, workId, and occurredAt. Without
after, the stream starts with one snapshot. A retained cursor resumes strictly
after it; an expired cursor produces a replacement snapshot. The stream ends at
the completed, failed, or cancelled boundary and contains no results or raw
failures.
stats()
Returns ExecutionStats, an alias of the bounded owner-scoped ScopeStats
projection. The canonical statistics ledger tracks mechanical timing and
lifecycle facts and is reconstructed from the Work record after restart.