Skip to content

feat(portal): Client Portal frontend — chat, sessions, search, voice, files (trinity-enterprise#78)#1498

Merged
dolho merged 8 commits into
devfrom
feature/78-portal-chat-ui
Jul 8, 2026
Merged

feat(portal): Client Portal frontend — chat, sessions, search, voice, files (trinity-enterprise#78)#1498
dolho merged 8 commits into
devfrom
feature/78-portal-chat-ui

Conversation

@dolho

@dolho dolho commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Client Portal frontend for epic trinity-enterprise#78 — the full client-facing surface on the OSS bundle (gated by the client_portal entitlement).

Scope

  • Chat + document exchange (original): portal-session chat drawer over the roster-scoped gated endpoint; Files drawer (download shared files, upload to the agent inbox).
  • Chat sessions: per-agent history switcher + "New chat"; resumes the most-recent thread on open; search results open on their thread.
  • Cross-chat search: a search box on the portal home searches the client's conversations (title + message content) across all rostered agents; results replace the roster and open the matching thread.
  • Voice mode: 🔊 speaks replies (ElevenLabs TTS) and 🎤 dictates input — browser Web Speech API where available (Chrome/Edge/Safari), MediaRecorder → server Scribe STT fallback for Firefox. Shown only when the agent's voice_available is true.
  • Polish: larger, clickable agent cards; a "How do files work?" note in the Files drawer.

Dependency / merge order

Frontend-only. The backend endpoints (roster/chat/documents/sessions/search/tts/stt) live in trinity-enterprise#106. Do not merge ahead of #106, and an OSS submodule-pointer bump is needed after #106 merges so the deployed backend actually serves these routes.

Verified live end-to-end against the running stack (sessions CRUD + threading, search with snippets, TTS MP3 playback, Firefox ogg/opus STT round-trip). Prod frontend build green.

dolho and others added 2 commits July 7, 2026 11:06
…nity-enterprise#78)

Wire the public /portal roster to the gated, roster-scoped chat endpoint
(trinity-enterprise#104), so a verified-email client — with no platform account —
can actually talk to the agents shared with them. The roster card's "Chat — soon"
placeholder becomes a live "Chat" button opening the existing chat drawer.

- `stores/clientPortal.js`: `sendPortalChat(agentName, message)` → POST
  `/api/enterprise/client-portal/agents/{name}/chat` with the portal-session
  auth header (same `{response, cost}` shape as the OSS chat).
- `views/enterprise/PortalChat.vue`: add a pluggable `sendMessage` prop
  (defaults to the operator OSS chat, so the operator preview page is unchanged);
  the public page passes the portal-session send.
- `views/Portal.vue`: enable the Chat button → open the drawer with the portal
  send fn.

Verified live (through the frontend proxy): sign in at /portal with a verified
email → open a rostered agent → the agent replies. A non-rostered agent is a 404
at the backend (the portal token is scoped to the roster).

Related to trinity-enterprise#78. Depends on trinity-enterprise#104 (endpoint).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…enterprise#78)

Add a "Files" affordance to the /portal roster: each card opens a documents
drawer listing the files that agent has shared (FILES-001), with per-file
download buttons.

- `stores/clientPortal.js`: `fetchDocuments(agentName)` → the gated documents
  endpoint (trinity-enterprise#104) with the portal-session auth header.
- `views/enterprise/PortalDocuments.vue`: slide-over drawer — filename, size,
  date, MIME-based icon, Download link (the `?sig=` URL from the backend; the
  download route is public so no portal auth rides on it). Loading/empty/error
  states, dark mode.
- `views/Portal.vue`: "Files" button next to "Chat" on each card.

Download direction only (agent → client); upload (client → agent) is a later
slice. Verified live: the endpoint returns the roster-scoped document list
(empty until an agent shares a file); non-rostered → 404. SFCs compile clean.

Related to trinity-enterprise#78. Depends on trinity-enterprise#104.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho dolho changed the title feat(portal): chat with a rostered agent over the portal session (trinity-enterprise#78) feat(portal): chat + document download for rostered agents (trinity-enterprise#78) Jul 7, 2026
…prise#78)

Add an upload control to the portal documents drawer: a client can send a file
TO a rostered agent (lands in the agent's inbox), completing document exchange
alongside download.

- `stores/clientPortal.js`: `uploadDocument(agentName, file)` → multipart POST to
  the gated endpoint (trinity-enterprise#104); the browser sets the multipart
  boundary, only the portal auth header is added.
- `views/enterprise/PortalDocuments.vue`: a "Send a file to {agent}" file input
  at the top of the drawer with sending/success/error feedback.

Verified live (through the frontend proxy): a portal client uploads a file →
lands in `~/inbox/<client-email>/` in the agent workspace. SFC compiles clean.

Related to trinity-enterprise#78. Depends on trinity-enterprise#104.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho dolho changed the title feat(portal): chat + document download for rostered agents (trinity-enterprise#78) feat(portal): chat + document exchange (download & upload) for rostered agents (trinity-enterprise#78) Jul 7, 2026
vybe and others added 2 commits July 7, 2026 09:38
…(trinity-enterprise#78)

The Files drawer now has two sections: "Files you sent" (the client's uploads to
the agent inbox, via the new /uploads endpoint) and "Files from {agent}"
(downloads). After a successful upload the sent-files list refreshes so the file
appears immediately.

- `stores/clientPortal.js`: `fetchUploads(agentName)`.
- `views/enterprise/PortalDocuments.vue`: two-section layout; upload refreshes the
  sent list; the upload error message (e.g. "the agent isn't running") is shown
  from the backend `detail` (fixes the earlier silent-failure-on-stopped-agent).

Pairs with the backend (trinity-enterprise#104) that also injects an inbox
manifest into each chat turn so the agent can actually act on uploaded files.

Verified live: upload → the file shows under "Files you sent" with today's date.

Related to trinity-enterprise#78. Depends on trinity-enterprise#104.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

⚠️ Nightly unit-suite check skipped — merge conflict against dev.

Resolve by running git merge dev locally and pushing the result. The next nightly run will re-test once the conflict is gone.

…erprise#78)

The portal chat drawer now loads the persisted conversation on open, so a client
sees their prior messages after a refresh / re-sign-in instead of a blank thread.

- `stores/clientPortal.js`: `fetchHistory(agentName)` → the gated
  `/agents/{name}/history` endpoint (trinity-enterprise#104).
- `views/enterprise/PortalChat.vue`: optional `loadHistory` prop — when provided,
  the drawer restores messages on mount. Omitted on the operator preview page, so
  that path is unchanged.
- `views/Portal.vue`: passes `store.fetchHistory` as the loader.

Verified live: chat two turns → close/reopen (or refresh) → the conversation is
still there. SFCs compile clean.

Related to trinity-enterprise#78. Depends on trinity-enterprise#104.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho

dolho commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Code review (high effort)

Reviewed directly (not via the multi-agent workflow, per a standing preference). Verdict: solid, low-risk frontend — no correctness bugs. XSS is handled (renderMarkdown/DOMPurify on assistant content; user text is whitespace-pre-wrap). Minor notes, none blocking:

  1. PortalChat.vueloadingHistory is set but never surfaced. On open it flips true→false around the history fetch, but the template renders no loading state, so history restore shows a blank flash on slow loads. Wire it to a spinner or drop the ref. (The sessions follow-up already adds an indicator.)
  2. PortalDocuments.vue onUpload — no client-side size/type pre-check. A >25 MiB or blocked-extension file uploads in full before the backend 413/415s. A cheap file.size/extension guard saves the round-trip. UX only.
  3. Portal.vuechatAgent and docsAgent are independent v-if drawers. Both can be mounted at once (same fixed overlay). Not reachable in the normal flow, but consider mutual exclusion.

Safe to land.

@dolho
dolho requested a review from vybe July 8, 2026 10:56
…trinity-enterprise#78)

Builds on the portal chat drawer:
- Chat sessions: per-agent history switcher + "New chat"; resumes the most-recent
  thread on open; search results open on their thread.
- Cross-chat search: a search box on the portal home searches the client's
  conversations (title + message content) across all rostered agents; results
  replace the roster and open the matching thread.
- Voice mode: 🔊 speaks replies (ElevenLabs TTS) and a 🎤 mic dictates input —
  browser Web Speech API where available (Chrome/Edge/Safari), MediaRecorder →
  server Scribe STT fallback for Firefox. Gated on the agent's `voice_available`.
- Polish: larger, clickable agent cards on the roster; a "How do files work?"
  note in the Files drawer.

Backed by the enterprise endpoints in trinity-enterprise#106 (sessions/search/
tts/stt). Frontend-only; no OSS submodule bump here (lands after #106 merges).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho dolho changed the title feat(portal): chat + document exchange (download & upload) for rostered agents (trinity-enterprise#78) feat(portal): Client Portal frontend — chat, sessions, search, voice, files (trinity-enterprise#78) Jul 8, 2026

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the frontend code (validated via /validate-pr: all required checks green, no secrets/PII, scope focused on the gated client_portal surface).

⛔ DO NOT MERGE YET — held on the merge-order dependency stated in the PR body: backend endpoints live in trinity-enterprise#106 (still OPEN), and an OSS submodule-pointer bump is needed after #106 lands. This approval clears the code review only; merge once #106 is in and the submodule is bumped.

…d (trinity-enterprise#78)

Point src/backend/enterprise at feature/78-portal-history-hardening (630cca9),
which carries the client-portal sessions/search/voice endpoints this frontend
(#1498) calls. Forward move from f1bafee. Re-bump to the merged commit once
trinity-enterprise#106 lands on enterprise main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dolho
dolho enabled auto-merge July 8, 2026 12:49
@dolho
dolho merged commit 2cd064e into dev Jul 8, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants