Skip to main content

Sessions

A session is a scratch-pad drafting canvas where the operator selects upstream items, attaches generation variants (each with its own preset, prompt, style and — optionally — its own LLM credential), and iterates through rounds until they have something worth exporting.

Sessions are independent of pipelines: any combination of items from any source can land in the same canvas. The session model is what the workbench's /sessions/[id] page renders.

Mental model

Each canvas is scoped to a single (organization, user) pair. Items, variants, rounds and exports all cascade-delete when the canvas is removed.

Entities

session_canvas

Top-level. Tracks lastActiveAt for ordering, name (nullable) and timestamps. Indexed by (organizationId, lastActiveAt desc) and (userId).

session_item

An immutable snapshot of an upstream item at the moment of selection. Stores:

  • sourceKindsocial / webpage / rss / custom
  • externalItemId — nullable, for resolving back to upstream
  • title, snippet, capturedAt
  • metrics (jsonb engagement data) and raw (jsonb original payload)

Critically, changes upstream don't propagate — the canvas keeps the title and metrics it captured at selection time. If you need fresh data, remove the item and add it again.

session_variant

A generation slot. Holds:

  • presetId — built-in slug (social-thread, blog-with-images, long-form, newsletter-section, tldr-brief, custom) or user-saved
  • userPresetId — uuid pointing to user_preset when presetId=user-saved
  • prompt, style, typeTag
  • statusidle / queued / streaming / done / error
  • errorMessage — populated on status=error
  • activeRoundIndex — 0-based; -1 means no rounds yet
  • llmCredentialId — nullable; overrides the workspace default LLM

variant_round

Append-only generation history per variant. Each row captures:

  • output (generated prose)
  • promptSnapshot and styleSnapshot (what the variant looked like when this round was generated)
  • llmProvider, llmModel (the LLM actually used)
  • generatedAt

Selecting an older round via session_select_round doesn't delete newer ones; it just moves activeRoundIndex.

user_preset

A user-saved (prompt, style, typeTag) tuple. Scoped per (org, user). Built-in presets are not stored as rows — they live as stable slugs in the codebase. UNIQUE(organizationId, userId, name) prevents duplicates.

session_export

A recorded export event: (sessionId, variantId, roundId?, format). Format today is markdown only. The row backs the EXPORTS 7D KPI on /console.

session_activity

Append-only log of session-affecting events:

  • clientui / claude-code / cursor / claude-desktop / cline / other-mcp
  • tool — dotted name (e.g. session.add_variant, ui:expand)
  • detail — human-readable context

Drives the /console NowFeed. Useful for "what was this session doing?" reads — both for humans and agents.

Lifecycle

  1. Createsession_create({ name? }) returns a canvas. No items, no variants. Optional name (can also be set later).
  2. Select itemssession_add_item snapshots an upstream social post / webpage / RSS entry. Pass an upstreamItemId to fetch from the workbench source registry, or pass the snapshot fields directly (title, snippet, capturedAt, metrics, raw).
  3. Attach variantssession_add_variant({ sessionId, presetId, prompt, style, typeTag, llmCredentialId? }). Variants start status=idle.
  4. Generatesession_generate_variant({ variantId, sessionId }) queues an LLM call. Each call appends a variant_round. session_generate_all({ sessionId }) fans out to every variant in the canvas.
  5. Compare roundssession_select_round({ variantId, roundIndex }) switches the visible round; the others stay in history. session_variant_rounds({ variantId }) lists the full history.
  6. Exportsession_export_variant({ sessionId, variantId, roundId?, format }) writes a session_export row. The actual markdown render is done by the caller (UI uses the export route; agents log via MCP).
  7. Save preset (optional)session_save_preset({ name, description, prompt, style, typeTag }) persists a variant config as a reusable user preset.
  8. Deletesession_delete({ sessionId }) cascade-deletes items, variants, rounds and exports. Irreversible.

API surface

MCP

18 tools live under apps/web/lib/mcp/tools/sessions.ts. See the MCP tools reference for the full signatures.

GroupTools
Lifecyclesession_create, session_list, session_get, session_update, session_delete
Selectionsession_add_item, session_remove_item, session_clear_selection
Variantssession_add_variant, session_update_variant, session_remove_variant, session_select_round, session_generate_variant, session_generate_all, session_variant_rounds
Exports + presetssession_export_variant, session_save_preset
Sourcessession_list_sources

Web (workbench + facade)

MethodPathUsed for
GET/api/sessions/activityLatest activity entries across the org's sessions. Backs /console NowFeed. Param ?limit (1–100, default 20).
POST/api/sessions/:sessionId/itemsServer-side bridge that mirrors session_add_item for in-app calls that prefer REST.
POST/api/sessions/:sessionId/variants/:variantId/regenerateTriggers variant regeneration (an LLM call producing a new round).
POST/api/sessions/sources/:id/refreshRefresh an upstream source asynchronously (kicks off an ingest run).

The rest of the session CRUD happens via the MCP transport (/api/mcp) or directly in the UI through server actions in apps/web/lib/session/.

What sessions are NOT

  • Not a pipeline — sessions don't ingest on a schedule. Adding an item is a one-shot capture.
  • Not a persistent destination — exports are events, not stored output. The workbench doesn't keep markdown files around (yet); the caller is responsible for taking the round text and doing something with it.
  • Not a refinement chain — sessions sit alongside the refinement model (which still lives on artifacts). A round is not an artifact.
  • Not shared — every canvas is (org, user)-scoped; teammates don't see each other's canvases. Cross-user surfaces (dashboards, pipelines) are separate.

See also