Ari Assistant¶
Status: Current | Last reviewed: 2026-10-20
Use the separate Ari app to validate an assistant experience against published Poppy agents. Read this page when operating the app, reviewing its trust boundaries, or updating its OpenMuse engine.
| Component | Location |
|---|---|
| Browser app and backend | apps/assistant |
| Vanguard address | https://assistant.vanguard.dev.trueserve.deloitte.com |
| Process | assistant, loopback HTTP port 3035 |
| Runtime | Node 22, Hono, React, OpenMuse TanStack engine |
| Storage | Separate trueserve_assistant PostgreSQL database |
| Authentication | Existing Studio users, active tenant membership and OAuth scopes |
| Company adapter | Separate Poppy Company gateway, port 3036 |
Desktop and phone presentation¶
Phone view applies to the entire signed-in app. It uses the canonical
PhoneFrame from shared/phone-emulator, with Ari already open. The surrounding
Studio-style sidebar, top bar and chat context panel are not mounted. Conversations,
Connections, Approvals and Settings use in-app navigation. Ari chat history is
searchable; Company messages retain their own list and direct-message controls.
Hash routes preserve the selected screen and conversation across reloads, and
browser Back restores the previous screen.
On a desktop viewport the shared handset scales to fit. At 600px and below, the same app fills the available screen without a second device frame. Settings → Desktop view returns to the full workspace. Protocol lab, HTTP activity, discovery JSON, client-profile selection, operation IDs and manual operation inspection are desktop tools. Phone approvals still show the exact business terms and proposal revision.
The standalone build imports the shared frame source and resolves its React imports to the Assistant's React instance. It does not import Studio or the emulator's call runtime. Ari's stylesheet is loaded inside the frame's shadow root. Build fingerprints keep the outer page and shadow-root stylesheet on the same version after an update.
Add a connection¶
Connections uses a URL entry and a live catalog from
GET /api/studio-exposures. The catalog refreshes when setup opens, when the
page becomes visible, every minute while mounted, and on explicit refresh.
Loading, empty and unavailable states remain distinct.
The setup flow posts {url, profileId} to POST /api/companies/setup and reads
SSE setup events:
| Stage | Completed work |
|---|---|
finding |
Setup started; discovery and issuer validation are in progress |
connecting |
Discovery is verified; session and DPoP token setup are in progress |
complete |
A usable session exists and the Company connection is saved |
error |
Setup could not finish; the UI keeps the address for retry |
The server validates the owner, origin and profile, and performs all external requests through the existing safe HTTP client. Tokens and keys never appear in setup events. A new Company is saved only after its session succeeds; failed setup removes its provisional credentials. Serializing setup per owner/profile/discovery URL lets retries reuse the existing connection when its issuer matches. Company account consent remains a separate owner action. Its sign-in callback returns to Connections in the saved desktop or phone display.
Request and identity boundaries¶
The browser receives an opaque HttpOnly session cookie. Studio sign-in uses
dynamic client registration, S256 PKCE, a browser-bound state value, exact
callback matching and issuer verification. The grant requests agents:read and
agents:debug, which correspond to the existing Run Agents permission. It does
not request write or publication authority.
Every workspace API request rechecks identity and tenant membership through
Studio's /api/v1/management/identity. Model tools and protocol runs also check
authorization between steps. Logout deletes the local session, revokes the
Studio grant and stops that owner's running assistant turn. Already accepted
Company actions are not cancelled by signing out.
The server derives record ownership from tenant UUID:user UUID; callers
cannot choose another owner. Each user has separate connections, chats, profiles,
approvals, traces and appearance within each tenant. Studio tokens, Company
tokens and private signing keys are encrypted with purpose/owner-bound AES-GCM.
Session handles and browser login state are stored hashed.
The default model path is:
Browser → Ari backend → Studio tenant model proxy → configured OpenAI integration
→ Poppy Company session → Company conversation or operation
The OpenMuse engine receives only the owner's selected Company and its
non-secret discovery. Its tools can contact that Company, read conversations,
read an operation and request an approval card. No model tool can confirm an
operation, publish an agent or claim sender=human. Company content is
untrusted input. AG-UI state events do not mutate application authority.
Studio retains provider credentials. Its model proxy requires agents:debug,
resolves integrations under the authenticated tenant, applies provider policy,
restricts model IDs, caps input/output and disallows provider-hosted tools or
stored-response references. An operator can opt into environment credentials
for a separate deployment; that changes model billing from tenant-specific to
service-wide.
Poppy behavior¶
| Area | Implemented behavior |
|---|---|
| Discovery | Poppy 0.2; verifies serving domain and issuer poppy_domains; limited discovery redirects |
| Network | HTTPS; public DNS addresses pinned to the socket; no credential-bearing redirects; bounded response sizes/timeouts |
| Personal Agent identity | Public client metadata and JWKS per immutable profile; ES256 client/session assertions |
| Company sessions | Separate DPoP key; issuer audience, fresh JWT IDs, nonce challenges, token hash and resource binding |
| Account connection | Direct Sign-In with PKCE, owner/state/issuer checks and existing session binding; account grant revocation |
| Conversations | Acceptance-only 201/202, paginated JSON events, immutable replay, direct children, handoff and close |
| Interrupted requests | Durable original message IDs and outbox; explicit retry after unknown delivery |
| Expired cursors | Reload available history, keep locally retained events and display a history-gap notice |
| Operations | Display exact terms/revision, owner-only confirm/cancel, changed-term rejection, duplicate-click claim, status reconciliation |
| Client profiles | Core-only or Operations v1; profiles keep their public identity when appearance changes |
The client retains the most recent 2,000 events per conversation. The gateway's authoritative ledger retains its full event history. The inspector redacts credentials but still contains test messages and Company payloads; use synthetic data and treat database backups as application data.
The browser receives app-state notifications over SSE. Company conversations use JSON polling; these are distinct transports. A model turn waits for a working Company within its bounded turn, and the selected Company conversation polls while visible. The model cannot promise a later background turn.
Owner actions refresh saved workspace state after either a successful or failed response. A failed approval decision also notifies other open sessions: a lost confirmation response must show the persisted unknown outcome and Check status, rather than leaving a stale confirming card.
Protocol lab¶
Each run records its Company, client profile, discovery snapshot, timestamps and individual pass/fail/not-exercised results. It tests signed sessions, key binding, event replay, duplicate message IDs, changed-payload conflicts, parent locks and idempotent close. Test conversations are closed at the end.
The run does not automatically consent to Company account access, confirm business operations, or claim a real human joined. Those scenarios appear as Not exercised. The approval UI and independent fixture tests cover Operations behavior separately. Before/after comparison and JSON export retain the distinction.
The gateway includes x-trueserve.agent_version as a nonstandard discovery
annotation. It is captured in run snapshots to identify the reviewed version.
External Companies need not provide this field.
Operations¶
See apps/assistant/README.md for complete
provisioning and environment settings. Both new services are optional and exit
without starting when their private local config is absent.
Keep the app database and encryption key together when backing up or restoring. Startup takes an exclusive database lease, marks unfinished chat/test turns interrupted and marks interrupted confirmations unknown. It never confirms again merely because a process restarted. The first release supports one Assistant process per application database.
On Vanguard, the small Procfiles can coexist with the current city stack:
overmind status -s .overmind-assistant.sock
overmind restart assistant -s .overmind-assistant.sock
overmind status -s .overmind-poppy.sock
The nginx template includes both new hosts, disables OAuth callback access logs,
limits request bodies and disables buffering for streams. The host certificate
must cover assistant.<city> and each poppy-<id>.<city> hostname.
Deployment manifests for official Dev/QA/production are not included in this pilot. Before expanding, add managed secret storage, deliberate retention, usage reporting, deployment/rollback automation and a distributed worker lease if multiple instances are required.
Upgrades and checks¶
The engine source, commit, hash, MIT license and local changes live in
apps/assistant/vendor/openmuse. Upgrade that source and its compatible package
versions together. Run type checking, HTTP/security tests, the independent
browser fixture, and the real hosted sandbox check. Re-run against a second
Company implementation before claiming interoperability beyond the tested
profile.
From apps/assistant, run:
pnpm typecheck
pnpm test
pnpm build
pnpm test:browser
For the real Vanguard sandbox, run
bash ../../.devcontainer/scripts/with-env.sh node --import tsx scripts/prepare-assistant-validation.ts
from apps/ai-manager, then pnpm test:hosted from apps/assistant.
Preparation creates a short-lived Studio validation session in a private
temporary file. The browser test signs out; remove
/tmp/assistant-validation-session.json afterwards.
Company Device Sign-In, SSE, OpenAPI/MCP execution, browser continuity and standing permissions are not implemented. The browser still shows the Company's advertisements in desktop view so a tester can identify the difference.