Skip to content

Editor assistant

Status: Current | Last reviewed: 2026-10-12

Agent draft reads include current authoring defaults and redacted tenant authentication readiness. Protected member information implies supported native Customer Authentication, preserving configured identity requirements and caller consent. Routine ending and summaries belong in the main prompt; startup lookup and authentication use their native configuration rather than extra procedures. The assistant asks only about unresolved material choices.

The Assistant surface inside the Agent and Workflow editors: a chat with a Claude Code or OpenAI agent about the currently open agent/workflow — questions, debugging past executions, and edits that direct-apply to the draft with the open editor updating live. Extends the AI generation architecture (same runner, same MCP endpoint, same triple gating) into an interactive multi-turn surface.

Select the engine in AI Generation settings. Each conversation retains its runner and integration; changing settings affects new conversations. OpenAI authoring describes the managed session bridge, saved context recovery, function-call receipts, model selection and deletion. The CLI workspace details below describe the Claude Code path.

The conversational designer reuses this architecture for live four-type draft creation. Designer sessions have their own protocol, structured output and revision-checked saves; the existing creation/editor experience remains available.

Big picture

AssistantPanel (floating popup for agents and workflows)
  → POST /api/assistant/sessions {kind,targetId}    create/restore (gating + 7d token + runner dispatch)
  → POST /api/assistant/sessions/{id}/messages      STREAMS the turn (NDJSON) + tees into messages jsonb
runner POST /v1/assistant/sessions(/{id}/turns)     persistent workspace as-<id>; `claude -p … [--resume sid]`
CLI → /api/mcp/generation (union auth)              shared tools + debug tools; save_* → resetCollabRoom

Three differences from generation: sessions are interactive multi-turn (claude --resume <cli_session_id> carries context; ai-manager persists the id and sends it per turn), turns stream back synchronously (no callback channel, no notifications), and saves reset the collab room per save (not just on completion) so the user's open editor updates the moment the assistant edits.

Gating

Identical to generation — NEXT_PUBLIC_FEATURE_AI_GENERATION flag + tenant ai_generation setting + active runner integration (resolveAiGenerationAvailability server-side, useAiGenerationAvailability client-side). The Assistant entry point is hidden entirely when unavailable; the session-create route re-checks server-side. Disabling the tenant setting blocks new editor sessions; existing editor sessions finish their lifecycle. Designer turns recheck availability before each new turn.

Data model (migration 193)

assistant_sessions: one active row per (tenant, created_by, kind, target_id, access_mode, surface) — enforced by a partial unique index; "New chat" closes the old row first. Carries cli_session_id (claude stream-json init id, for --resume), api_token_id (per-session scoped tsf_ token, 7d TTL, revoked on close/expiry), messages jsonb (UI-restore history only, capped ~200 entries — the CLI's --resume carries the real context), workspace_state (pending|ready|lost), sliding expires_at. RLS tenant-isolated. Helpers in lib/db-assistant-sessions.ts.

Runner side

assistant.py — third workspace lifecycle beside chat (ephemeral per turn) and generation (deleted on terminal status): assistant workspaces (as-<session-id>) persist across turns, are deleted on DELETE or by an idle-TTL sweep (ASSISTANT_WORKSPACE_TTL_SECONDS, 24h). Seeded like generation (.mcp.json with the per-session token, docs bundle, conversational AGENTS.md protocol: always get_current_draft live, cite specifics when debugging, minimal validated edits, no ask_user/lifecycle tools — every turn is a normal reply).

POST /v1/assistant/sessions/{id}/turns {message, cli_session_id} streams NDJSON (init{cli_session_id} / text / thought / tool_call / tool_result / error / done{cli_session_id}). The CLI command mirrors generation (-p --output-format stream-json --append-system-prompt-file AGENTS.md --mcp-config .mcp.json --strict-mcp-config --allowedTools "Read,Glob,Grep,LS,mcp__trueserve__*", prompt on STDIN, --resume iff cli_session_id non-empty). The CLI's terminal result event is dropped when not an error — it duplicates the already-streamed reply text. Per-session asyncio lock (409 on concurrent turns), ASSISTANT_TURN_WALL_MS (30 min) wall clock, ASSISTANT_CONCURRENCY (2) semaphore. Designer builds share this 30-minute turn budget. Set the same override on ai-manager and the runner; the internal stream's idle timeout allows one extra minute for cleanup.

ai-manager owns the resume decision (it persists cli_session_id); the runner registry is just a cache. A runner restart (tmpfs workspace gone) returns 404 on the turn → ai-manager mints a fresh token (api_tokens.name is unique — recovery names include the session id + timestamp), re-dispatches the session (idempotent create), clears cli_session_id, and retries once with a ~10-message recap prefix. UI history survives regardless (it's in the DB).

MCP toolset

The same /api/mcp/generation endpoint serves both token populations: auth tries getGenerationJobByToken then getAssistantSessionByToken; tools/list and tools/call switch on which matched. lib/generation-mcp/tools.ts factors the shared discovery/draft/validate/save cases into callSharedTool(target) (pure extraction — generation behavior unchanged); lib/assistant-mcp/tools.ts layers the assistant surface:

  • Shared (from generation): searches, service defs, node manifests, integrations, get_current_draft, validate_*_config, save_*_draft_config (draft only, never publish). Reference discovery includes existing same-tenant agents/workflows, capabilities, published visuals, focused configuration schemas and optional Library demo transcripts. The runner also supplies public research and detailed capability guides; see generation research. Existing OpenAI conversations retain their provider catalog; New chat picks up new tools and protocol instructions.
  • Removed: report_plan/progress, ask_user, complete/fail_generation, apply_agent_template (a chat needs no lifecycle; asking happens in the reply).
  • New debug tools (read-only, target-scoped — RLS is tenant-level, so each verifies the row belongs to the session's target): list_agent_conversations / get_conversation_transcript (messages + events, truncated: 300 msgs / 2k chars each / 100 events) for agents; list_workflow_runs / get_workflow_run_trace (per-node input/output/error, 2k-char truncation, ≤200 nodes) for workflows. Row caps 25.
  • Per-save collab reset: after a successful save_*_draft_config, callAssistantTool calls resetCollabRoom(kind, target_id, tenant_id) — the gateway re-seeds the live Y.Doc from the DB (authoritative replace) and the open editor updates immediately. Hazard (documented, accepted): a user's unflushed in-flight keystrokes at the reset instant are replaced.

ai-manager routes

  • GET/POST /api/assistant/sessions — restore / create (gating re-check, close-prior, token mint, dispatch). Normal withAnyAuth (Edit Agents/Workflows by kind) — no allowlist entries needed.
  • POST /api/assistant/sessions/{id}/messages — the streaming proxy (application/x-ndjson, X-Accel-Buffering: no, force-dynamic/nodejs): pipes the runner stream to the browser while teeing text → one assistant message, tool_calls → compact tool entries, init/done → cli_session_id. On client disconnect it keeps draining so the CLI turn completes (its MCP saves land) and the reply persists for restore. Creator-only.
  • DELETE /api/assistant/sessions/{id} — close + revoke + runner cleanup ("New chat").
  • Hourly TTL sweep in instrumentation.ts (ASSISTANT_SESSION_SWEEPER=0 to disable).

UI

components/assistant/AssistantPanel.tsx — restore-on-mount, fetch-reader NDJSON streaming, friendly tool-activity chips, "Assistant updated the draft" toast on save, New chat. Assistant replies are markdown, rendered with MessageResponse from the shadcn.io message component (components/ai/message.tsx, backed by the streamdown package — handles incomplete markdown gracefully mid-stream; its stylesheet + @source scan are registered in app/globals.css). In both Agent and Workflow editors, AssistantChatPopup.tsx mounts it in a lower-right chat window, independent of Preview; the round chat button opens it and the header minus button collapses it. The panel remains mounted while collapsed (CSS hidden), so an in-flight stream finishes and the restored chat state is retained. Full-screen Preview hides only the popup chrome and does not unmount it. The collapsed chip floats with extra bottom clearance (mb-16) and the fixed container is pointer-events-none — at the bare corner offset the chip sat exactly on the Preview chat's Send button. The expanded popup measures the shared toolbar height and leaves one Preview control row clear. This keeps Preview close/maximize reachable when the header wraps on shorter screens; resizing updates the available popup height.

Both editors use this popup independently of their Preview panels. Never conditionally unmount AssistantPanel in response to ordinary open/collapse state or opening and closing Preview.

See also

  • external-authoring-mcp.md — the externalized authoring path (user's own coding tool over a tenant token); reuses this page's resetCollabRoom-per-save pattern.
  • ai-generation.md — the shared architecture (gating, runner, MCP, collab reconciliation, model pinning).
  • conversation transcripts and workflow runs — the data the debug tools read.