A turn via app.turn() returns a standard response envelope and records user–assistant entry pairs; variants like proactive openers and triggered turns control message visibility through the hidden flag to separate actual user input from system-generated prompts. Inbound failures (files, delivery) are stored durably as system entries with metadata but excluded from the reply text and model context, preserving the appearance of a direct user–assistant exchange.
An end-to-end DM turn via app.turn() returns { status: "ok", sessionId, reply } and records exactly a ["user", "assistant"] entry sequence in the session.[1]
A proactive-opener turn with empty user text records the seed entry as hidden: true so no surface renders it as a user message; the reply still generates normally.[1] A proactive-opener turn that carries real user text keeps the user entry visible (hidden is NOT set to true), even when the proactiveOpener flag is present.[1] A triggered (background job) turn records its synthetic wake prompt with hidden: true so no surface renders it as a user message.[1] Without the hidden flag set to true, every session entry is treated as part of the visible user–assistant conversation.
Inbound file problems (e.g., too many files) are stored as a durable file_event system entry with direction: "in" and an issues array naming the dropped files; they do NOT appear in the reply text and are NOT sent to the LLM in the model context.[1]
A cron-delivered digest lands as a delivery event with provenance (sourceSessionId, fireKey, sourceThreadRef) but does NOT inject a fake assistant row into the recipient's transcript — recordPrincipalDelivery creates the recipient DM session and leaves it assistant-entry-free.[1] The cron source session does NOT appear in listSessions for the sender — background cron runs are never surfaced as human conversations for the owner.[1]
plugins/web-ui/src/chat.ts is the single authoritative owner of approval continuation state in the web UI; split ownership across core-bridge.ts and individual card components has been removed. When an approved run is resumed, its output routes into the currently visible chat pane rather than a background pane. core-bridge.ts documents the web chat retry protocol; test/turn-idempotency-route.test.ts specifies the server-side idempotent re-submission contract. Failed outbound sends in the web chat UI retain the user message as visible with a retry affordance rather than discarding it; the message object carries a failure-state representation. In the web UI, message dispatch passes through an intermediate queue; integrations and tests asserting send timing must account for this queued state rather than assuming direct, synchronous delivery order.
Sources