@happyvertical/smrt-chat
Rooms, direct messages, threads, persona-bound conversations, tool loops, the AssistantDock, helper preferences, and voice gateway turns.
What it gives you
Rooms, direct messages, threads, persona-bound conversations, tool loops, the AssistantDock, helper preferences, and voice gateway turns.
- 01AssistantDock with its own mounted SvelteKit routes
- 02Assistant turns that suspend for browser tools and resume with their results
- 03Runtime report, overview, and data-surface tools for the persona extraTools seam
- 04Two recipes: chat.assistant places the dock in the shell header; chat.rooms brings the room models
Chat is a closed service facade
ChatService keeps its collections and agent-reply bridge private, so callers use the supported conversation operations instead of bypassing membership or lifecycle checks. All seven models expose only generated list and get. allowedTools is fail-closed: an absent or empty allowlist grants no agent tool access. The agent authors as its resolved bot Profile, recorded on the session as agentProfileId, never as the agentId slug.
The tool loop and streams have bounded entry points
runToolLoop and runPersonaConversationTurn use the manifest tool catalog with DEFAULT_MAX_STEPS. Non-manifest extraTools pass the same allow-list; createDataSurfaceTools supplies principal-bound discovery and reads. runChatConversationStream and createChatStreamHandler provide token SSE through SmrtChatBackend. Voice sessions use createVoiceChatSession and handleVoiceGatewayTurn; validate their tenant, actor, persona, and conversation binding because a gateway bearer token is not user authentication. captureChatFeedback records thumbs and can feed learning reinforcement.
The AssistantDock and its routes
AssistantDock reads the mounted descriptors of a host data-surface registry; with none it is plain chat. mountAssistantRoutes from the /sveltekit subpath serves the dock from one catch-all route. The principal comes from event.locals, and threads only from the actor keyed session room. runAssistantTurn runs one turn over the tool loop with server tools and declared browser tools. A browser tool call suspends the turn; the browser resumes it with results, which reach the model marked untrusted. Destructive browser calls always wait for the person.
Report and overview tools for the assistant
createRuntimeReportTools returns reports.runtime.sources, define, list, and run for the persona extraTools seam. The model supplies only a declarative RuntimeReportSpec from smrt-reports, and sources are a server-owned allow-list. A report is saved only after a person confirms the exact spec; without a confirmation host, apply returns an awaiting_confirmation proposal and stores nothing. createOverviewTools returns overviews.describe, apply, and undo over a host-opened overview page.
Questions the assistant asks are data
An assistant turn that asks the person to choose attaches an assistant-question object to its message. Its options come from code that knows the domain, never from model text. AssistantDock renders it as AssistantQuestionForm: a radio group, a select, or checkboxes, with the default preselected. On submit, the transport's optional answerQuestion continues the turn in code with no model round-trip. A transport without it receives the answer as an ordinary message, and a typed reply is matched against the options. parseAssistantQuestion and matchAssistantQuestionText come from the /svelte subpath.
Helper preferences are presentation policy
HelperPreferencesService on the package root loads, saves, and resets a per-profile helper choice under a host-supplied authorize and policy. It is never an identity, persona, or tool authority. Browser code imports the DTO, parser, and field list from the /helper-preferences subpath, which ships no server adapter. HelperControlPanel keeps a local draft apart from the applied snapshot, and FloatingAssistant accepts the saved bottom-corner placement.
Package UI
Components in this package
Import these public components directly. When the package also exports playground entries, the next tab renders its real components with package-owned example data.
Package previews
Try the working views
These previews are owned by the package and use its public components. Mock mode is intended for safe browsing; live mode appears only where the package supports it.
Generated interface
REST
The exact route set comes from each model's @smrt({ api }) policy
and the application manifest—not from a second controller definition.
GET /api/messages
GET /api/messages/:id
POST /api/messages
PATCH /api/messages/:idWhat stays consistent
- Tenant scope and operation permissions run before model access.
- OpenAPI and generated clients use the same manifest fields.
- Packages can expose read-only or custom action sets.
Generated interface
MCP
AI tools are generated from the methods you explicitly include. Their inputs are validated against the same model metadata used by REST and CLI.
messages_list({ where, limit, orderBy })
messages_get({ id })
messages_create({ data })
messages_update({ id, data })Agent-safe by construction
- Tool availability can be narrower than the HTTP API.
- Principal execution intersects RBAC, agent ceilings, and persona tools.
- Descriptions and schemas are emitted from source metadata.
Browser-native agent interface
WebMCP
The generated web definitions carry the same tool names, descriptions, and JSON Schema
inputs as MCP. smrt-web registers them with document.modelContext.
import { webMcpToolDefinitions } from '@happyvertical/smrt-virt-web';
import { registerWebMcpTools } from '@happyvertical/smrt-web';
// Omitting an exposure policy registers read-only tools only.
const dispose = registerWebMcpTools(webMcpToolDefinitions);The page keeps its security boundary
- Tools execute through generated REST fetchers as the signed-in page user.
- Authentication, tenant gates, permissions, and field policy stay server-side.
- Feature detection makes registration a safe no-op outside supported browsers.
Generated interface
CLI
The app CLI turns manifest resources into predictable operator commands while keeping package-specific actions close to their models.
smrt messages list --limit 25 --order-by created_at:desc
smrt messages get <id>
smrt messages create --from-file ./record.jsonMade for automation
- Machine-readable output and file-based input.
- The same include/exclude policy as other surfaces.
- Migration, introspection, dispatch, and knowledge commands stay in smrt-cli.