feat(portal): Client Portal frontend — chat, sessions, search, voice, files (trinity-enterprise#78)#1498
Conversation
…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>
…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>
…(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>
|
Resolve by running |
…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>
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 (
Safe to land. |
…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>
vybe
left a comment
There was a problem hiding this comment.
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>
Client Portal frontend for epic trinity-enterprise#78 — the full client-facing surface on the OSS bundle (gated by the
client_portalentitlement).Scope
voice_availableis true.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.