Skip to content

feat: add daemon-backed terminal interface - #1462

Open
randomradio wants to merge 16 commits into
kenn-io:mainfrom
randomradio:feat/go-tui-parity
Open

randomradio wants to merge 16 commits into
kenn-io:mainfrom
randomradio:feat/go-tui-parity

Conversation

@randomradio

@randomradio randomradio commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Adds agentsview tui, a full-screen Bubble Tea interface backed by the existing daemon HTTP API and SSE stream rather than direct archive access. It covers session browsing and transcripts, dashboards, usage, activity, trends, insights, pins, trash, recent edits, settings, mutations, exports and publishing, persistent filters, localized labels, and terminal-safe rendering. Shared report services also expand the MCP surface for analytics, activity, trends, pins, insights, and recent edits.

Startup serves the client before the initial archive sync completes. Report panels stream independently, session details load concurrently, and transcript and filter-editor work stays bounded by the viewport and render caches. Concurrent sync is normal active status, empty trends remain an input state, and sidebar filters open an explicit keyboard editor with cursor and paste handling.

The implementation deliberately reuses daemon and service contracts instead of adding a second SQLite path. Fixed keybindings and colon commands remain the terminal-specific tradeoff; sidebar text filters are free-form rather than metadata pickers. The main review surfaces are internal/tui, cmd/agentsview/tui.go, the shared report and message-window changes, and docs/tui.md.

@roborev-ci

roborev-ci Bot commented Aug 19, 2026

Copy link
Copy Markdown

roborev: Combined Review (0d94129)

Changes need revisions: four medium-severity issues remain; no critical or high findings were reported.

Medium

  • internal/service/reports.go:57, internal/tui/client.go:289 — Custom activity ranges use incompatible date/filter contracts across HTTP, TUI, and direct/PG transports. Use an activity-specific builder with RFC3339 bounds and consistent filtering semantics.

  • internal/tui/client.go:614, internal/tui/model.go:1007 — Imports buffer entire archives in memory, while long-running mutations have a fixed ten-minute timeout. Stream multipart uploads and use cancellation without a fixed deadline.

  • internal/tui/view.go:141, internal/tui/model.go:1114 — At widths 80–119, navigation is hidden but remains focusable, causing blind navigation. Show it when focused or remove it from the focus cycle and expose a visible route selector.

  • internal/tui/view.go:804 — Fallback session IDs reach the terminal without sanitization, allowing control sequences. Sanitize them with terminaltext.Sanitize.


Reviewers: 2 done | Synthesis: codex, 10s | Total: 10m19s

@mariusvniekerk mariusvniekerk self-assigned this Aug 21, 2026
@roborev-ci

roborev-ci Bot commented Aug 21, 2026

Copy link
Copy Markdown

roborev: Combined Review (47ae0ce)

Seven medium-severity issues remain, including terminal escape injection, broken authentication/session workflows, and reporting and navigation regressions.

Medium

  • Terminal escape injection bypasses sanitization — internal/tui/view.go:366, internal/tui/view.go:820
    Glamour decodes HTML entities after sanitization, allowing encoded control sequences such as OSC 52 to become active terminal commands. The session-ID title fallback is also unsanitized. Sanitize after entity decoding while preserving only trusted renderer styling, sanitize the fallback ID, and test direct and entity-encoded OSC payloads.

  • Authentication token is not propagated to the session backend — internal/tui/client.go:551
    Enabling require_auth updates c.token, but c.sessions retains its original token, causing session, search, transcript, and usage requests to return 401 until restart. Update or recreate the backend when the token changes and synchronize concurrent token access.

  • open-session always sends an invalid request — internal/tui/client.go:589
    The command posts an empty body despite the server requiring a valid opener_id, so it fails with opener "" not found. Resolve an opener through /api/v1/openers, accept one as an argument, or open the session directory locally.

  • Direct activity reports cannot use custom ranges consistently — internal/service/reports.go:66
    ResolveQuery requires RFC3339 bounds, while reportDBFilter requires the same values to be date-only. The reused filter also excludes one-shot sessions by default, unlike the HTTP activity endpoint. Build an independent activity filter and align its visibility semantics with the HTTP endpoint.

  • Long transcript content is inaccessible — internal/tui/view.go:231
    Rendering always begins at line zero without intra-message scrolling, permanently truncating long messages and tool output and potentially hiding the selected message. Track a rendered-line viewport offset and keep the selection visible.

  • Report-list scrolling uses item indexes as line offsets — internal/tui/model.go:811
    Multi-line insights, pins, and recent edits cause the selected item to move off-screen while navigation continues invisibly. Calculate actual rendered-line offsets or use a virtualized viewport.

  • Archive imports buffer entire files in memory — internal/tui/client.go:615
    Large ChatGPT or Claude exports can exhaust memory before upload begins. Stream multipart encoding through an io.Pipe and propagate encoder errors asynchronously.


Reviewers: 2 done | Synthesis: codex, 15s | Total: 14m26s

@mariusvniekerk

Copy link
Copy Markdown
Collaborator

I've tested this out locally on my large agentsview corpus. As it stands the performance of this TUI is not of a sufficient bar to be included in agentsview.

@mariusvniekerk mariusvniekerk added the wontfix This will not be worked on label Aug 24, 2026
@roborev-ci

roborev-ci Bot commented Aug 25, 2026

Copy link
Copy Markdown

roborev: Combined Review (20f4ce8)

Review found five medium-severity functional issues; no concrete security vulnerabilities were identified.

Medium

  • internal/tui/client.go:763 — :open-session always fails: The command sends an empty body, but the endpoint requires a valid opener_id, and no registered opener has an empty ID. Load /api/v1/openers and allow opener selection, or provide a documented server-side default.

  • internal/tui/client.go:723 — Authentication token is not propagated: Enabling authentication updates only Client.token; the service.SessionService in Client.sessions retains the old token, causing subsequent session, transcript, search, and usage calls to fail. Recreate or update the session backend whenever the token changes, including during startup when adopting a locally returned settings token.

  • internal/tui/client.go:263 — Custom activity dates use the wrong format: The TUI sends date-only From and To values to an endpoint requiring RFC3339 instants, so the documented custom-range workflow returns HTTP 400. Convert calendar dates to timezone-aware RFC3339 bounds with an exclusive end boundary.

  • internal/service/reports.go:57 — Activity reporting differs by transport: The direct implementation validates RFC3339 custom bounds through a date-only filter and applies filters differently from the HTTP endpoint. Identical MCP calls can therefore fail or return different results under PostgreSQL and daemon transports. Introduce activity-specific range mapping and align supported filter semantics across the service, MCP schema, and HTTP endpoint.

  • internal/tui/model.go:869 — Selection can scroll outside the viewport: Report pages use item indexes as line anchors even though some items render multiple lines. Track rendered line offsets or separate selection and detail rendering before applying windowLines.


Reviewers: 2 done | Synthesis: codex, 21s | Total: 18m40s

@roborev-ci

roborev-ci Bot commented Aug 26, 2026

Copy link
Copy Markdown

roborev: Combined Review (418cd83)

Verdict: 6 medium-severity issues require attention before merging.

Medium

  • Stale :find results can load the wrong transcript window — internal/tui/model.go:104-108, 279-293, 1130-1136
    Results are keyed only by page generation; changing sessions increments a separate generation, allowing previous-session results to apply to the newly selected session. Validate the searched session ID and session-load generation.

  • Timezone inconsistency in TUI session filtering — internal/tui/model.go:160-172
    The initial session ListFilter omits the configured timezone, causing date filtering to default to UTC while reports use the configured timezone. Set Timezone: opts.Timezone.

  • Activity-report filters are silently ignored over HTTP — internal/service/reports.go:117-124, internal/server/huma_routes_activity.go:34-48, 311-317
    Filters including model, min_user_messages, include_one_shot, and include_automated are sent by the client but not accepted or applied by the endpoint, producing inconsistent direct and HTTP results. Align the endpoint and client behavior.

  • Custom activity ranges use incompatible date formats — internal/tui/client.go:269-285, internal/tui/model.go:551-564, internal/mcp/tools.go:714-716
    The API requires RFC3339 timestamps, while TUI and MCP documentation reuse from/to as YYYY-MM-DD dates. Add RFC3339-specific fields and validation or convert date-only ranges to explicit boundaries.

  • Configured SSH remotes may not resolve correctly — internal/tui/client.go:730-737
    :sync-remote sends only the host, so remotes with non-default users or ports are not matched and may fall back to default credentials. Send a stable remote ID or include user and port.

  • Session-derived fallback IDs can inject terminal control sequences — internal/tui/view.go:329-345, 846-854
    The fallback session ID is rendered without sanitization, potentially allowing terminal spoofing or state modification through malicious daemon data. Sanitize it and audit all daemon-provided metadata at the rendering boundary.


Reviewers: 2 done | Synthesis: codex, 14s | Total: 21m34s

@randomradio randomradio changed the title feat: add a daemon-backed terminal interface feat: add daemon-backed terminal interface Aug 26, 2026
@roborev-ci

roborev-ci Bot commented Aug 26, 2026

Copy link
Copy Markdown

roborev: Combined Review (eed83b9)

Verdict: Multiple medium-severity correctness issues remain in the TUI and activity-report paths.

Medium

  • internal/tui/client.go:763-764 — open-session sends an empty request body even though the server requires a valid opener_id, causing the command to always fail.
  • internal/tui/client.go:263-285 — Custom activity ranges pass YYYY-MM-DD values directly, but the API expects RFC3339 timestamps.
  • internal/service/reports.go:66-82 — Direct ActivityReport calls inherit ExcludeOneShot, excluding one-shot sessions by default unlike the HTTP activity route.
  • internal/tui/model.go:279-293,1130-1136 — Pending find results are validated only by page generation, allowing results from a previous session to apply to the newly selected transcript.
  • internal/tui/model.go:1105-1119 — Repeated transcript-page requests before completion reuse the same ordinal and can append duplicate pages.

Reviewers: 2 done | Synthesis: codex, 9s | Total: 21m1s

@randomradio
randomradio marked this pull request as ready for review September 1, 2026 10:07
@roborev-ci

roborev-ci Bot commented Sep 1, 2026

Copy link
Copy Markdown

roborev: Combined Review (86586e4)

Verdict: Five medium-severity issues require attention; no critical or high-severity findings were identified.

Medium

  • internal/tui/client.go:204,753-758 — Enabling authentication updates c.token, but the delegated c.sessions backend retains the original token. The triggered reload uses stale credentials and receives 401 responses. Refresh or recreate the backend when the token changes, with synchronized access.

  • internal/mcp/tools.go:714-786, internal/service/reports.go:57-70,177-193 — Activity custom ranges have inconsistent contracts: MCP documents from/to as YYYY-MM-DD, while activity.ResolveQuery requires RFC3339. Without preset:"custom" the range is ignored; with it, documented values are rejected. Define, validate, and normalize the fields consistently.

  • internal/service/reports.go:117-124; internal/server/huma_routes_activity.go:34-49,310-316 — Shared filters such as model, min_user_messages, and include_one_shot are serialized by the HTTP report backend but are not accepted or applied by the activity endpoint. Add support or stop advertising unsupported filters.

  • internal/service/reports.go:66-77,193 — Direct activity reports default to ExcludeOneShot: true, while the HTTP activity endpoint includes one-shot sessions. PostgreSQL-backed and daemon-backed results therefore differ. Align the default one-shot visibility semantics.

  • internal/tui/model.go:1235-1239,1285-1292 — Referenced-session and search-match transcript windows use Around without a Limit, producing a zero page size. Consequently, pressing n cannot load additional transcript rows. Track a positive page size or derive continuation state for around-window loads.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 18m30s

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

wontfix This will not be worked on

Development

Successfully merging this pull request may close these issues.

2 participants