Skip to main content
Review edits programmed survey content with an AI SDK coding agent (default openai/gpt-5.6-luna, reasoning medium). The Monaco buffer is the document source of truth. Tip revisions are created only when the user explicitly saves.

Model

Document tools are declared without execute so the browser runs them against Monaco. Survey file tools run on the server against object storage (file writes persist immediately; survey source still requires explicit Save). This path does not use survey.content_edit / WorkflowAgent turn drafts. That workflow remains available for callers that want server-side document tools and tip finalize; Review does not.

Conversation setup

Create (or reuse) a conversation, then load persisted UIMessages into the chat client.

Send a message

  • Response: text/event-stream (UIMessage SSE)
  • Message ids must be UUIDs (clients should use generateId: () => crypto.randomUUID())
  • Inbound messages (including client tool results on continuations) are upserted before the model streams
  • The completed assistant message(s) are upserted when the stream ends
  • No content revision is created

Composer attachments

The Review composer supports multi-file attachments (paperclip → upload local files or pick ready survey files). Submit stays disabled while any attachment is still preparing/uploading. Images are not auto-written into the file manager. If the user asks to keep a copy, the agent may call write_survey_file / spreadsheet write tools.

Survey file tools

Questionnaire tip tools

Users often attach a DOCX/PDF/text file and ask the agent to treat it as the next questionnaire. Tip assertion reuses the same PUT /v1/surveys/{id}/questionnaire rules (ready file_id → immutable revision pointer). The agent should call set_questionnaire_revision when the user clearly intends an attached/referenced file to be the questionnaire (e.g. “use this as the questionnaire”, “updated Word doc”). It should not promote incidental screenshots or sidecars unless the user says that file is the questionnaire.

Questionnaire audit tool

audit_against_questionnaire is a client tool (no server execute): the browser starts POST /v1/surveys/{id}/audits pinned to the last saved content tip (does not auto-save a dirty Monaco draft) and awaits the World run until terminal. Each issue is { id, code, message, start_line?, end_line? } (1-based lines in the pinned content revision when the model can locate them). The checklist shows a clickable line link that switches to the Code tab and reveals that range. After detect, the agent patches the current editor buffer (unsaved draft is fine) with apply_document_patch, then calls update_audit_finding to PATCH each finding resolved or dismissed. It does not start POST .../remediations. Save is a separate action when the user wants a new content tip. Fix actions that start a new chat turn wait until the current turn is idle; Dismiss and Fix both collapse the issue in place (expand to reopen). Dismiss is local and always available.

Scenario discovery + Behavior Review auto-walk

discover_test_scenarios is a client tool (no server execute): the browser starts survey.scenario_discovery against the tip questionnaire only (never Monaco/program source) and awaits the World run until terminal. Scenarios form a DAG via depends_on. Test all (and Test on a leaf, which auto-queues unmet deps) starts survey.scenario_test:
  1. Topological order; scenarios whose deps did not pass are marked blocked and do not open a browser
  2. Ready scenarios create a Behavior Review session + Browserbase/local CDP browser, then hand that provisioned session to a separate Luna ToolLoopAgent with a narrow toolset (browser_act, get_interview_state, submit_verdict, finish_scenario). Navigation observations are recorded automatically on page-key change. Accepted axes mint suite edges/goldens so the Regressions tab suite grows automatically.
Outcomes: passed | failed | blocked | unreachable | error (blocked is not collapsed into failed).

Explicit save

Agent edits stay in the editor until Save. Chat reloads from GET …/messages. The unsaved Monaco buffer is kept in browser localStorage (keyed by survey + tip revision) so refresh keeps agent/human edits; Save clears that draft and advances tip. If tip advanced elsewhere, the stale local draft is discarded. When the survey is linked (integration_id + upstream_survey_id), Save pushes to the platform first, then writes tip. Push failures return 502 upstream_push_failed (tip unchanged) and surface in the editor with a Fix with AI action.

Editor tabs

The Review editor pane has Preview | Code | Files:

Unreviewed diff stacking

Decorations compare a stable reviewed base (initially tip) to the live buffer. That base does not move when the agent applies further patches across user messages, so highlights accumulate. Accept advances the reviewed base to the current buffer (clears decorations without publishing tip). Discard restores the buffer to the reviewed base. User Save (including ⌘S / Ctrl+S) is always available — it accepts any stacked unreviewed hunks, then opens the save flow to publish tip / push upstream. Agent save_survey_content publishes tip (and refreshes Preview) without accepting the stack — only the user can dismiss unreviewed decorations.
  • Content edit — WorkflowAgent path with server draft + tip finalize (not used by Review UI)