feat(frontend): agent playground sessions, files, and inspector#5306
feat(frontend): agent playground sessions, files, and inspector#5306ardaerzin wants to merge 113 commits into
Conversation
Ports the parked session FE (@agenta/entities/session Fern+zod wrappers, the records-replay adapter transcriptToMessages, loadSession, + regression test) onto fe-chore/request-optimizations-onbig (= big-agents + FE/sdk/runner improvements). AgentChatPanel was fully rewritten on that base (frame-split; the per-session useChat moved to AgentConversation.tsx), so the records-hydration hook is re-wired there as a follow-up, not carried from the old AgentChatPanel.
…tion Re-attaches the records-replay hydration to req-opt's frame-split structure (the per-session useChat moved from AgentChatPanel to AgentConversation). Cache-first: on open with an empty local message cache, loadSessionMessages (queryRecords → v6 messages) seeds the conversation; own-run sessions with a warm cache skip it. Stamps seenIdsRef + restoredIdsRef so restored history renders settled, and arms the bottom pin.
…d/utils ElicitationWidget imports partitionElicitationDraft from the utils barrel, but the elicitation-m1-followups change added it to elicitation.ts + the widget without adding it to index.ts — breaking the next-dev build. Present on big-agents too (base bug); this unblocks the local build.
…ig-agents-add-sessions
…ig panel Tool rows in the agent template's Tools section now surface resolution problems without opening each drawer: a catalog action that 404s (renamed/ removed) or a missing connection shows a red Unresolved tag; a connection with is_valid=false shows an amber Reconnect tag. Collapsed provider groups roll up their worst child status, and the section header tooltip reflects it. The probe reuses the drawer's action-detail query family (shared cache, 5-min staleTime) with a low-priority fetch so playground load is unaffected; 404s skip retries and transient errors never mark a row. GatewayToolsBridge gains connectionsErrored so a failed connections fetch reads as unknown rather than "all connections removed".
…y clients - add session liveness primitive (deriveStreamNest / deriveSessionLifecycle / refineLifecycleWithSandbox) with 11 unit tests - add killSession (DELETE /sessions/streams) to the session API - thread lowPriority through querySessionRecords / fetchSessionStream / querySessionStreams; add per-resource sessions/mounts clients (normal + low-priority) - zod boundary: interaction status as string, add interaction created_at
…session - records replay: split assistant turns on `done`; hydrate empty-cache sessions from records with a transcript skeleton; guard brand-new sessions from the empty query - SWR revalidate-on-open: refetch records low-priority, adopt only if server strictly ahead - liveness badge in the tab dot (local run-state, else backend liveness) from ONE shared project-wide is_alive query; low-priority hydration + liveness - End session (kill) in the history menu, shown only when the session is alive
…ctions status - StreamsTab uses deriveStreamNest (deletes local nest.ts) + shows resumable/reattachable - InteractionsTab reads status via the validated package query (string, not object.code)
Records what's done/pending/blocked for the agent-chat FE integration, the backend asks (durable archive/delete cascade, session naming), and FOLLOWUP(sessions,*) code markers.
…ivation - add queryMountFiles / readMountFile (mounts file ops) with zod schemas - deriveMountRows: fold the flat whole-tree listing into a one-level folder/file view, plus mountBreadcrumbs (7 unit tests)
Upgrade the Mounts tab from a flat metadata list to a navigable folder/file tree with text preview (drill-in client-side via deriveMountRows). Shows a clear notice when the mount object store isn't configured. Doc updated (browser done, read-write follow-up, store-config gotcha).
Replace the fixed-width TurnInspector with one adaptive right panel beside the chat: - nested antd Splitter, persisted width, min/max + a chat-width floor so the panel can't squeeze the transcript; collapses to 0 when closed (chat never remounts) - refined Turn|Session segmented switch; Turn = the migrated turn inspector (live messages), Session = session-scoped content (Mounts/State/Interactions, reusing the durable endpoints) - rightPanelAtom replaces turnInspectorAtom; session view is valid in chat mode too
Group the left config panel: draftable config sections (model/instructions/tools/mcp/ skills/advanced) then an 'Operations' divider for sections that never enter a revision — Triggers (moved out) and a new Mounts stub (placeholder for agent-level mounts, which don't exist yet; a conversation's live files are in the chat session panel).
Triggers and Mounts render as regular sections; the labeled divider didn't match any existing header style.
…on header Replace the removed uppercase divider with an 'Operations' heading that matches the config panel header's typography (13px semibold), so the two regions read as peers.
Configuration, Triggers, and Mounts are now three peer regions with identical header styling (the panel header's 13px semibold). Triggers and Mounts leave the accordion: their headers carry the count/add-trigger and coming-soon affordances, bodies render directly (Subscriptions/Schedules sub-sections come from TriggerManagementSection).
…-style bars Full-bleed 48px tinted bars with the bottom hairline — a visual copy of the panel's sticky Configuration header — instead of inline text headings.
…r Triggers The Triggers region bar drops its + (count only); its body now renders two ConfigAccordionSection sections — Subscriptions and Schedules — with the same icon/count/header-+ /collapse treatment as the template config sections. Each section's + and empty-state link open its own create drawer (subscription picker / schedule drawer), default-bound to the agent.
Slide the panel in/out (220ms width transition on the splitter panes) instead of snapping. The transition is enabled only around open-state flips so divider dragging stays free of rubber-banding, and the panel content is retained through the close so it slides out instead of blanking.
The transition class was gated behind an effect-driven flag, so it landed a commit after the width change and never animated. The panes now carry the transition whenever the divider isn't being dragged (onResizeStart/End flip it synchronously), and the close-hold is derived during render so content is retained in the same commit that starts the collapse.
…uration Move them out of AgentTemplateControl (where they sat inside the Configuration section's body, under its sticky header) into a new AgentOperationsSections rendered at the panel level. Each section wraps its own sticky Configuration-style header bar, so the panel is three vertically stacked sections and scrolling swaps the pinned header per region.
AgentConfigSkeleton drops its Triggers row (no longer a config section); new AgentOperationsSkeleton renders the real Triggers/Mounts header bars over pulsing bodies below the Configuration section while hydration is pending or agent-ness is unknown — the panel holds its final three-section shape from first paint, and no trigger queries fire for maybe-prompt apps.
Rename the Mounts region to Storage and give it config-style inner sections like Triggers has: App drive (the agent's durable folder, #5215 — coming soon) and Session drive (per-conversation working folder, browsable from the session panel). 'Drive' over 'mount' — mounts name the mechanism, not the thing. Skeleton updated to match.
The Session drive section now browses the ACTIVE conversation's files: the app layer slots a SessionDriveContent body (chat scope + active tab -> the shared mount file browser) into AgentOperationsSections' new sessionDrive prop, keeping entity-ui decoupled from chat state. The section is controlled-open so the browser and its queries mount only when expanded (zero cold-load requests); no open conversation shows an explanatory empty state, and MountsTab's empty copy drops the mount jargon.
…ector - Session hydration no longer latches a StrictMode-broken ref (blank transcript in dev); loadSessionMessages catches fetch failures and returns null per its documented contract. - Scope session-inspector query invalidation to the current session via a shared invalidateSessionInspector helper (was refetching every open session). - useSessionDrive.errored now surfaces agent-mount failures. - CSV preview truncation banner off-by-one (parse one row past the cap). - a11y: inert on the hidden session tab bar, arrow-key pager ignores focused inputs, keyboard zoom on image preview. - Inspector drawer closes instead of unmounting when the session id vanishes. - Trigger accordions auto-expand when the first subscription/schedule is added.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
web/oss/src/components/AgentChatSlice/components/Inspector/invalidate.ts (1)
3-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCondense the JSDoc comment.
As per coding guidelines, keep in-code comments to one short line maximum unless a genuinely surprising constraint requires a brief exception. While the reason for scoping by
sessionIdis helpful, it can be expressed more concisely.♻️ Proposed refactor
-/** - * Refresh only ONE session's inspector queries. - * - * Session-inspector queries are keyed `["session-inspector", kind, projectId, sessionId]` (see - * `StreamsTab`/`StatesTab`). A bare `["session-inspector"]` prefix invalidates every open session's - * State/Stream tabs — and the Inspector can be mounted for several sessions at once (docked panel + - * compare-column drawer) — so scope by `sessionId` to avoid refetching unrelated sessions. - */ +/** Refresh only ONE session's inspector queries (avoids refetching unrelated sessions). */Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 1374247e-e975-4add-bd0c-7193a410566a
📒 Files selected for processing (15)
api/oss/src/core/mounts/service.pyapi/oss/src/core/store/dtos.pyapi/oss/src/core/store/storage.pyapi/oss/tests/pytest/unit/test_mounts_file_ops.pyweb/oss/src/components/AgentChatSlice/AgentChatPanel.tsxweb/oss/src/components/AgentChatSlice/AgentConversation.tsxweb/oss/src/components/AgentChatSlice/assets/loadSession.tsweb/oss/src/components/AgentChatSlice/components/Inspector/Inspector.tsxweb/oss/src/components/AgentChatSlice/components/Inspector/invalidate.tsweb/oss/src/components/Drives/FilesDrawer.tsxweb/oss/src/components/Drives/renderers.tsxweb/oss/src/components/Drives/useSessionDrive.tsweb/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsxweb/oss/src/components/SessionInspector/tabs/StreamsTab.tsxweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
- web/oss/src/components/AgentChatSlice/assets/loadSession.ts
- api/oss/src/core/mounts/service.py
- web/oss/src/components/SessionInspector/PanelSessionInspectorButton.tsx
- web/oss/src/components/AgentChatSlice/components/Inspector/Inspector.tsx
- web/oss/src/components/Drives/useSessionDrive.ts
- web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/TriggerManagementSection.tsx
- web/oss/src/components/Drives/renderers.tsx
- web/oss/src/components/Drives/FilesDrawer.tsx
- web/oss/src/components/SessionInspector/tabs/StreamsTab.tsx
- web/oss/src/components/AgentChatSlice/AgentConversation.tsx
- web/oss/src/components/AgentChatSlice/AgentChatPanel.tsx
|
@coderabbitai review |
✅ Action performedReview finished.
|
The Files section's only browse-all affordance was a blue "View all files" link that showed only past 5 files, while the sibling Triggers header already carried a count. Move it into the Files header: it shows the count and opens the drive drawer (rows still open it preselected on a file). The header content is slotted from the app layer via a new storageHeader prop; header and body share one drawer request through a per-revision atom.
…333 A global bare-`h1 { color:#333 }` rule (editor-theme.css) leaks into the markdown renderer, and preflight is off so nothing normalises it, rendering headings dark/low-contrast on dark surfaces (the drive markdown preview). Headings now use `text-inherit` so they follow the block's own colour and out-specify the global rule, which also respects a caller/ancestor recolour. Also style <mark>/<kbd> HTML passthrough so no element falls back to jarring UA defaults.
…eview Split the drive surfaces so the heavy body (tree, renderers, pdfjs, markdown, thumbnails) loads only when a drawer opens and unmounts on close: - Thin EnhancedDrawer shells (DriveDrawer, FilesDrawer) next/dynamic-import their bodies (DriveExplorer, FilesDrawerBody); FilesDrawer also gates its drive query on open so the always-mounted host is idle when closed. - New light helper modules (driveKinds, driveIcons) so file rows/thumbnails no longer drag the renderer graph into the config panel/chat pane. Visual fixes that share these files: - Grid tiles no longer blow up: text thumbnails clip to the 4:3 box, and long paths can't stretch a grid item (min-w-0). - Gif thumbnails render via the authenticated blob (a raw <img> 401s on header-auth); image cap 2->8MB. - Hide the runner-internal agents/ namespace (skills + sessions) on every surface. - Metadata block: fixed-width label tracks so the right column no longer shifts horizontally between files. - Crossfade the preview body only when the renderer kind changes (same-type switches stay reconciled).
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
web/oss/src/components/Drives/configDrive.ts (1)
1-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffCondense multiline comments to adhere to coding guidelines.
As per coding guidelines, keep in-code comments to one short line maximum unless a genuinely surprising constraint requires a brief exception. Consider condensing the following blocks:
web/oss/src/components/Drives/configDrive.ts#L1-L7: Condense this module description to a single line.web/oss/src/components/Drives/configDrive.ts#L32-L37: Condense this hook description to a single line.web/oss/src/components/Drives/driveKinds.ts#L1-L6: Condense this module description to a single line.web/oss/src/components/Drives/driveKinds.ts#L21-L22: Condense this inline comment.web/oss/src/components/Drives/driveIcons.tsx#L1-L5: Condense this module description to a single line.web/oss/src/components/Drives/driveIcons.tsx#L20-L22: Condense this function description.web/oss/src/components/Drives/StorageFilesHeader.tsx#L1-L8: Condense this component description to a single line.web/oss/src/components/Drives/FilesDrawer.tsx#L7-L16: Condense this component description to a single line.web/oss/src/components/Drives/FilesDrawer.tsx#L63-L64: Condense this inline comment if it does not describe a genuinely surprising constraint.web/oss/src/components/Drives/FilesDrawer.tsx#L72-L73: Condense this inline comment.web/oss/src/components/Drives/FilesDrawer.tsx#L91-L92: Condense this inline comment if it does not describe a genuinely surprising constraint.Source: Coding guidelines
web/oss/src/components/Drives/StorageFilesHeader.tsx (1)
20-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer Tailwind utility classes over inline styles.
As per coding guidelines, avoid using inline
style={{...}}objects. Use standard Tailwind utility classes instead to maintain consistency.
web/oss/src/components/Drives/StorageFilesHeader.tsx#L20-L20: Replacestyle={{width: 44, height: 14}}with Tailwind classes (e.g.,className="h-[14px] w-[44px]").web/oss/src/components/Drives/FilesDrawer.tsx#L198-L198: Replacestyles={{body: {padding: 0, display: "flex", minHeight: 0}}}withclassNames={{body: "flex min-h-0 p-0"}}using Ant Design'sclassNamesprop.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7ceedeba-7312-46a5-85c0-e9537b721a4f
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (21)
services/runner/src/tracing/otel.tsweb/oss/package.jsonweb/oss/src/components/AgentChatSlice/assets/markdown.tsxweb/oss/src/components/Drives/DriveDrawer.tsxweb/oss/src/components/Drives/DriveExplorer.tsxweb/oss/src/components/Drives/DriveFileCard.tsxweb/oss/src/components/Drives/DriveFileRow.tsxweb/oss/src/components/Drives/FileThumb.tsxweb/oss/src/components/Drives/FilesDrawer.tsxweb/oss/src/components/Drives/FilesDrawerBody.tsxweb/oss/src/components/Drives/FilesWindow.tsxweb/oss/src/components/Drives/StorageFilesHeader.tsxweb/oss/src/components/Drives/StorageSection.tsxweb/oss/src/components/Drives/configDrive.tsweb/oss/src/components/Drives/driveIcons.tsxweb/oss/src/components/Drives/driveKinds.tsweb/oss/src/components/Drives/driveTree.tsweb/oss/src/components/Drives/fileMeta.tsxweb/oss/src/components/Drives/renderers.tsxweb/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsxweb/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentOperationsSections.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
- web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentOperationsSections.tsx
- web/oss/package.json
- web/oss/src/components/Drives/DriveFileCard.tsx
- web/oss/src/components/Drives/FileThumb.tsx
- services/runner/src/tracing/otel.ts
- web/oss/src/components/AgentChatSlice/assets/markdown.tsx
- web/oss/src/components/Drives/fileMeta.tsx
- web/oss/src/components/Playground/Components/PlaygroundVariantConfig/index.tsx
- web/oss/src/components/Drives/FilesWindow.tsx
- web/oss/src/components/Drives/DriveFileRow.tsx
- web/oss/src/components/Drives/driveTree.ts
| rootLabel={rootLabel} | ||
| touchedAt={selected?.touchedAt} | ||
| size={selected?.size ?? undefined} | ||
| onNavigateRoot={() => setSelectedPath(drive.recents[0]?.path ?? null)} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Make the Home breadcrumb navigate to the root state.
The House button currently reselects the most-recent file, so it does not navigate away from the preview.
Proposed fix
- onNavigateRoot={() => setSelectedPath(drive.recents[0]?.path ?? null)}
+ onNavigateRoot={() => setSelectedPath(null)}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onNavigateRoot={() => setSelectedPath(drive.recents[0]?.path ?? null)} | |
| onNavigateRoot={() => setSelectedPath(null)} |
…CodeQL) CodeQL flagged `/\/+$/` (and the combined `/^\/+|\/+$/g`) in the session/drive path helpers: the end-anchored `\/+$` backtracks quadratically on a backend-supplied path with many '/'. Replace all six instances with a single linear-scan trim (stripLeading/stripTrailing/trimSlashes in the entities session core; an exported cleanPath in Drives), internal slashes preserved. Fixes the two flagged sites (fileActivity, mountBrowser) plus the four siblings CodeQL would flag next (driveTree x2, useSessionDrive, agentDrive).
…ion build passes
The drive PDF thumbnail set workerSrc via `new URL("pdfjs-dist/build/pdf.worker.min.mjs",
import.meta.url)`. `next build` (webpack) externalizes that ESM worker and fails ("ESM
packages need to be imported"); `next dev` uses turbopack, which tolerated it, so the break
only ever showed in CI's production build. Copy the worker into public/ at dev + build time
(gitignored, no vendored blob) and point workerSrc at the same-origin /pdf.worker.min.mjs —
kept local, no CDN, so self-hosted deployments keep working.
Railway Preview Environment
|
…820px
antd Drawer's `size` prop only accepts "default" | "large"; a numeric pixel width
belongs in `width`. `size={820}` was ignored, so the deliveries drawer opened at the
default ~378px instead of the intended 820. (CodeRabbit review item.)
Context
This branch builds the agent playground's session experience on top of big-agents. It adds multi-session chat, a durable Files/drive surface for what the agent reads and writes, a records-backed Inspector, and the supporting backend/runner pieces those surfaces depend on (file recency, durable-replay trace ids, resume-after-kill). Base is
big-agents; the branch has been re-aligned with it several times.What this adds
Sessions. Multi-session chat: a tab bar in build mode, a rail plus history popover in chat mode. Sessions persist to localStorage and hydrate durable content from the record log. New sessions autofocus the composer. Never-run empty "husk" sessions are pruned so history stays clean. Per-session drive/quick-look state is keyed by session id, so a backgrounded tab never clobbers the active one.
Drive / Files. One Files surface across the config panel, the chat context rail, and a full drawer. It aggregates the session cwd mount and the agent's durable per-artifact mount (folded under
agent-files/), tags each file Agent vs Session (with a grid filter that appears only when both exist), orders newest-first via the object store's mtime, renders type-aware previews and thumbnails, and hides runner-internal artifacts (Pi transcripts underagents/sessions/,.agenta-*markers). File names show the basename with the full path on a tooltip. List changes animate.Unified Inspector. One records-backed panel replaces the old Turn and Session panels, with Timeline, Context, and Runtime lenses. While the panel is open you can click any assistant turn to focus it (gated on the panel being open, not on a turn already being selected). The Streams and State tabs live-revalidate during a run.
Backend / runner.
MountFile.mtimeis threaded from the object store's LastModified, so "newest first" works no matter how a file was created (bash script, Write tool, upload). Previously recency came only from Write/Edit tool records, so bash-created files fell back to alphabetical.trace_idon the terminaldoneevent, so a reloaded transcript links a replayed turn back to its trace.session_streams(excluding soft-deleted rows) lets a resumed session mint a fresh stream row after a kill instead of colliding with the dead one.Robustness.
AbortError, so they stop logging as real failures.Tests / notes
tscandpnpm lint-fixare clean across the touched packages (no new type errors vs the big-agents baseline).doneevent).NEXT_PUBLIC_AGENT_CHAT_STOP_KILLS_SESSION(Stop also kills the session) is behind a flag and still in progress; the resume-after-kill migration lands here to support it.SessionInspector/StreamsTab.tsxhas a pre-existingSessionStream.statustype error.What to QA
+, switch tabs, then use the history clock to reopen a closed one. A new session focuses the composer; abandoning an empty one leaves no residue in history.agents/sessions/pi/...,.agenta-*) must NOT appear.