viby

Chats and projects

A chat is the durable project boundary. It owns messages, generations, versions, attachments, environment variables, provider histories, tool selections, and application metadata.

ChatCollection

Access the collection through viby.forUser(scope).chats.

MethodReturnsBehavior
create(input?)ChatCreates an empty chat with optional title and JSON metadata. No generation starts.
import(input)ChatValidates files, a ZIP, or a configured source adapter and creates the chat plus its first immutable version atomically.
get(id)ChatLoads an active in-scope chat. Deleted, missing, or cross-scope records are not exposed.
list(options?)CursorPage<Chat>Lists active chats in stable order with an opaque cursor and optional nested metadata filter.
snapshot(id, options?)chat, messages, and versionsLoads one consistent detail-view snapshot with independently paginated message and version windows.
restore(id)ChatClears a soft-delete tombstone while the record still exists.
purgeDeleted({ limit? })numberPermanently removes eligible deleted chats and their scoped durable resources in bounded batches.
const chat = await user.chats.create({
  title: "Customer portal",
  metadata: { workspace: "acme", template: "saas" },
});

const page = await user.chats.list({
  limit: 20,
  after: savedCursor,
  metadata: { workspace: "acme" },
});

Metadata values are JSON. Filters match the requested nested structure and never bypass tenant/user scope. Cursor strings are opaque; do not parse or synthesize them.

Prompt-derived titles

Use the portable titleFromPrompt() helper when a product should show a concise sidebar title as soon as the first prompt is submitted. It is deterministic and does not contact a model, so it adds no provider cost or latency to chat creation:

import { titleFromPrompt } from "@viby/sdk/core";

const prompt = "Build a polished SaaS analytics dashboard with revenue charts";
const chat = await user.chats.create({
  title: titleFromPrompt(prompt),
  metadata: { titleSource: "prompt" },
});

await chat.start({ prompt });

The default result contains at most seven words and 48 characters. The helper removes common request language and implementation detail clauses, preserves meaningful casing such as SaaS or AI, and returns New project when no usable prompt text remains. Automatic titles remain application-owned: products can later call chat.update({ title }) after a user rename or an optional model-assisted refinement.

snapshot() uses one aggregate PostgreSQL query when available. Custom persistence adapters can provide readChatSnapshot; adapters without it use the portable fallback with the same ownership checks and cursor semantics.

Import files or a ZIP

const fromFiles = await user.chats.import({
  title: "Imported app",
  filePolicy: { locked: ["package.json", "farm.config.ts"] },
  source: {
    type: "files",
    files: [
      { path: "src/main.tsx", content: source },
      { type: "artifact", path: "public/logo.png", bytes, mediaType: "image/png" },
    ],
  },
});

const fromZip = await user.chats.import({
  source: { type: "zip", bytes: uploadedZip },
});

Paths are normalized and traversal is rejected. Text must be valid UTF-8. Binary entries are stored through the configured artifact store. Locked paths cannot be changed by generation, workspace tools, direct patches, moves, or deletes in descendant versions.

Chat

Properties

PropertyMeaning
idOpaque chat identifier.
titleCurrent user-facing title.
metadataCurrent application-owned JSON metadata snapshot.
frameworkFramework identifier fixed when the chat is created or imported.
createdAt / updatedAtDurable lifecycle timestamps.
environmentChat-scoped environment-variable collection when environment support is configured.
toolSourcesExplicit durable tool-source selection for this chat.

Lifecycle and generation

MethodBehavior
update({ title?, metadata? })Replaces supplied mutable fields and returns a refreshed handle. Omitted fields are preserved.
delete({ retentionMs? })Soft-deletes the chat and returns deletedAt plus nullable purgeAfter. It does not perform an immediate provider-side cleanup.
start(input)Persists a logical generation and first attempt, schedules according to execution mode, and returns an addressable handle.
generate(input)Convenience method that waits for success and returns a version. It throws on failure, cancellation, or required tasks.
getGeneration(id)Loads a generation belonging to this chat.
startFromVersion(input, version)Starts a durable generation from an exact version snapshot.
generateFromVersion(input, version)Synchronous convenience around startFromVersion() and wait().

GenerateInput accepts prompt, a configured model alias, host instructions, categorized skills, JSON metadata, and up to the configured attachment limits. Attachments are immutable and checksum-verified before later attempts reuse them.

Versions and messages

MethodBehavior
latestVersion()Returns the newest version or null when the chat has no source yet.
getVersion(id)Loads one version from this chat.
listVersions({ limit?, after? })Returns a cursor page of immutable versions.
listMessages({ limit?, after? })Returns durable user and assistant messages in stable order.
getMessage(id)Loads one message by ID without scanning pages.
getAttachment(id)Reads scoped attachment bytes and verified metadata from the artifact store.

Assistant messages include an optional provider-neutral finish reason and ordered typed parts such as text, status, reasoning summary, file activity, search, command, tool calls, errors, and usage. Incomplete trace parts never become final message parts.

Repository and deployment history

MethodBehavior
repositoryLinks()Lists durable links between the chat and remote repositories.
repositoryPushes()Lists version-bound push and pull-request outcomes, including failures and conflicts.
deploymentProjects()Lists durable links between the chat and provider projects.
deployments()Lists deployment lifecycle records across all chat versions.

These methods return Viby records and work after a restart. They do not query providers unless an explicit provider operation is invoked.

Project environment variables

Enable the feature with environment: {} or an application-provided store.

await chat.environment.set({
  environment: "preview",
  name: "PUBLIC_API_ORIGIN",
  value: "https://api.example.com",
});

await chat.environment.set({
  environment: "preview",
  name: "SERVICE_TOKEN",
  value: token,
  secret: true,
});

const records = await chat.environment.list({ environment: "preview" });
await chat.environment.delete({ environment: "preview", name: "SERVICE_TOKEN" });

Public values are returned through value. Secret records always return value: null; bytes remain behind storage.secrets. Values resolve only when a sandbox, preview, build, or deployment explicitly selects the environment.

Deletion and retention

The default retention is 30 days. Configure retention.deletedChatsMs, override it on one delete, and run purgeDeleted() from a host-owned schedule. null retains indefinitely. Purge is destructive and cannot be undone; products should make that distinction explicit in their UI.