viby

Generations and events

A Generation is an addressable logical request. Attempts are immutable executions appended for initial work, retry, resume, or task resolution.

Start a generation

const generation = await chat.start({
  prompt: "Add a billing settings page",
  model: "fast",
  instructions: "Preserve the existing navigation contract.",
  skills: { security: ["company/skills/billing-security"] },
});

chat.start() commits the generation before execution begins. user.generations.get(id) or chat.getGeneration(id) can rehydrate the same handle after a request, reload, or process restart.

Status model

Generation statusMeaning
queuedDurable work exists but no active attempt currently owns execution.
runningAn embedded runner or leased worker owns the active attempt.
waitingA typed plan, question, or permission task must be resolved.
succeededA final assistant message and immutable version were committed.
failedThe active attempt ended with a durable safe error.
cancelledCancellation was committed with a reason.

Attempt statuses also include interrupted. Retrying, resuming, or resolving a task never overwrites an older attempt.

GenerationCollection

MethodBehavior
get(id)Loads an in-scope generation by ID and returns a live handle over its durable state.

Generation

Read state

MethodReturnsBehavior
data()GenerationDataCurrent logical status, active attempt, configuration, base/result versions, usage, cost, and timestamps.
attempts()GenerationAttemptData[]Immutable execution history in order.
tasks()GenerationTaskData[]All proposed and resolved typed tasks for the generation.
toolCalls()ToolCallData[]Redacted durable arguments, results, ownership, status, and idempotency.
artifacts()GeneratedArtifactData[]Metadata for generated images, audio, video, documents, or binary outputs.
getArtifact(id)GeneratedArtifactContentChecksum-verifies and returns one scoped artifact's bytes.
events({ after?, limit? })GenerationEventPageReads one durable event page after an opaque cursor.
outboundDeliveries({ sink, status? })delivery recordsInspects persisted delivery attempts and dead letters for one configured sink.

Subscribe and wait

MethodBehavior
stream({ after?, pollIntervalMs?, signal? })Yields durable events after the cursor and ends at waiting, succeeded, failed, or cancelled. Aborting only stops this subscriber.
toEventStreamResponse(options?)Returns a Web-standard SSE Response, reading Last-Event-ID unless after is explicit.
wait({ pollIntervalMs?, signal? })Waits for a terminal or waiting state and returns a discriminated GenerationOutcome. Aborting does not cancel work.
for await (const event of generation.stream({ after: lastCursor })) {
  lastCursor = event.cursor;
  await persistAcknowledgedCursor(lastCursor);
  publishToUi(event);
}

Events are persisted in cursor order. Lifecycle events, structured output, usage, generated artifacts, and agent trace parts share the same cursor; no provider stream ID is required for recovery.

Control execution

MethodValid states and behavior
cancel(reason?)Commits cancellation for active work, then aborts a local model signal when present. Repeated cancellation returns durable state.
retry()Appends a retry attempt for failed or cancelled work and schedules it according to execution mode.
resume()Appends a resume attempt for waiting or orphaned queued/running work. An unexpired worker lease cannot be stolen.
resolve(input)Validates a task-specific resolution, commits it with a new attempt, and resumes the same logical generation.

Invalid transitions throw GenerationStateError. chat.generate() translates waiting to GenerationTaskRequiredError, failure to GenerationError, and cancellation to GenerationCancelledError.

Typed tasks

KindRequestResolution
planProposed ordered stepsapprove, or revise with feedback
questionPrompt plus optional choicesAn explicit answer
permissionProposed effect and permissionsallow or deny, with optional note

Permission tasks may include a normalized sandbox or tool action. Secret values are omitted from the proposal. Resolving approval reuses the proposed action's idempotency identity so a completed external effect is not silently repeated.

Agent trace events

Trace parts use four event phases:

  • part.started assigns a stable part ID, type, and position;
  • part.delta appends incremental content for that ID;
  • part.completed stores the typed final part on the assistant message;
  • part.failed records a safe error and retryability without creating a final message part.

Supported final message-part types are text, status, reasoning-summary, file-read, file-edit, search, command, tool-call, error, and usage. Completed file-edit parts distinguish create, update, delete, and move operations. The legacy write operation remains part of the readable contract for older persisted messages.

Outbound delivery

deliverEvents({ sink, after?, limit?, retry?, signal? }) sends one ordered page through a configured sink, persisting claims, receipts, retry schedule, and dead-letter state. It returns delivered records, the safe resume cursor, hasMore, dead letters, and the earliest retry time.

redriveOutboundEvent({ sink, cursor }) explicitly makes one dead letter eligible again. Delivery failure never changes generation state. Receivers must deduplicate stable event IDs because a remote endpoint may accept a request before the sender observes a transport failure.

Generated-source quality gates

Configure provider-neutral quality commands when generated source must install, typecheck, test, or build before it becomes an immutable version:

const viby = createViby({
  framework: "farmjs",
  model,
  sandbox,
  preview: {
    prepare: [{ command: "pnpm", args: ["install", "--prefer-offline"] }],
    start: { command: "pnpm", args: ["dev", "--host", "0.0.0.0"] },
    port: 3000,
  },
  generation: {
    workspace: { preview: "eager" },
    quality: {
      prepare: [{ id: "install", command: "pnpm", args: ["install", "--prefer-offline"] }],
      checks: [
        { id: "typecheck", command: "pnpm", args: ["typecheck"] },
        { id: "build", command: "pnpm", args: ["build"] },
      ],
      checkConcurrency: 2,
    },
  },
});

With workspace.preview: "eager", Viby opens the immutable base version immediately, starts preview preparation while the model works, and emits workspace.started, workspace.prepared, preview.ready, or preview.failed through the normal resumable generation cursor. The preview URL does not wait for typecheck or production build. Viby synchronizes the final candidate into the same sandbox, runs preparation, and executes up to checkConcurrency independent checks concurrently. The durable preview record is then retargeted to the committed version without reinstalling or restarting its server.

Without an eager workspace, quality gates continue to use a fresh sandbox. In either mode, Viby commits only after every check succeeds. Failures end the attempt without persisting a partial version. Command output is deliberately not persisted because it may contain secrets, and the configured command policy applies to every preview and quality command.

Worker execution

Set generation: { execution: "worker" }, then run a worker in any suitable process:

const worker = viby.worker({ id: "worker-1", concurrency: 4 });
await worker.run({ signal });

Claims are database-backed, scoped to compatible framework and model/engine identities, and fenced with lease tokens. The runtime provides at-least-once execution; provider effects must remain idempotent.