Design prototype mode (ai-manager)¶
Status: Current | Last reviewed: 2026-09-24
A run mode of Agent Studio for designers: the real Next.js app with zero
backend services. All /api/* calls, the collab WebSocket, Janus signalling,
the Preview call's media-proxy offer, and the transcript/test SSE streams are
intercepted in the browser by an MSW service worker; data comes from JSON
fixtures captured off a live environment, and saves persist to localStorage.
Because the prototype is the unmodified component tree, a designer's UI edits
merge back as ordinary diffs.
Running¶
cd apps/ai-manager
npm install # standalone — @trueserve/* deps are committed tarballs
npm run dev:prototype
dev:prototype sets NEXT_PUBLIC_PROTOTYPE_MODE=1 (plus
NEXT_PUBLIC_ASYNC_AGENT_URL=/mock/async-agent for the workflow tester and
the *_SWEEPER/TOOL_HEALTH_SCHEDULER=0 gates so the server boots without
Postgres/Redis). The designer handoff doc — what works, what not to edit, how
to hand changes back — is apps/ai-manager/prototype/README.md.
How it hangs together¶
Everything lives in apps/ai-manager/prototype/; exactly four real files
carry a guarded touch point:
| File | Touch point |
|---|---|
proxy.ts |
early NextResponse.next() when the flag is on (the session-cookie gate is the one server-side check MSW can't intercept) |
app/layout.tsx |
<PrototypeGate> wraps AuthProvider — the worker must be intercepting before auth-context's mount-time GET /api/auth/session fires |
next.config.js |
distDir: '.next-prototype' when the flag is on (Next 16 refuses two dev servers sharing one .next) |
package.json |
the dev:prototype / prototype:capture scripts + msw devDependency |
Mechanics worth knowing before touching it:
- Store (
prototype/store/): reads resolve localStorage-overlay → captured fixture (public/prototype-fixtures/, keyed by URL pathname); mutations write whole resolved bodies back to the overlay. The generic fallback handler answers unknown GETs from fixtures and logs[prototype] unhandled …— that warning loop is the coverage mechanism. Mock handlers must never return 401 (auth-context treats an own-API 401 as session expiry and redirects to /login). - Collab (
prototype/collab/loopback-gateway.ts): MSWws.link()intercepts/collab/ws/*; per room a browser-side Y.Doc is hydrated from the store via the app's ownpipeline-shape.ts/workflow-shape.tsconverters. Incoming updates wait for hydration before they are applied, so startup cannot overwrite the first edit. The room speaks the same y-protocols handshake asapps/collab-gateway/src/rooms.ts, and flushes to both/api/pipelines/{id}and/api/pipelines/{id}:draftoverlays (the room keys on the base id, the page fetches the draft row). The autosave chip is fed throughflush-registry.tsby the mocked/api/auth/collab-flush(-state)routes. - Phone Preview:
/api/proxy/webrtc/offeris answered by a real RTCPeerConnection in the same page (silent oscillator track + the pre-negotiatedvideodata channel id 0 — itsonclosehangs the call up), so ICE/DTLS come up on loopback and the call UI behaves normally. Janus signalling is loopbacked to "registered" (prototype/phone/janus-loopback.ts) because the agent editor auto-registers SIP on mount and a dead server toasts on every open. Transcripts stream fromprototype/phone/call-script.tsover mocked SSE. - Workflow tester:
/test, the/mock/async-agent/sse/executions/…stream, and the authoritativetest-tracepoll are all generated from one per-execution schedule (prototype/workflow/execution-script.ts) so SSE animation and the trace poll agree; the tracerunrow must carryid(the panel rendersrun.id.slice(0,8)).
Distributing to a designer (no monorepo access)¶
The persistent designer source is the private Deloitte-US/agent-studio-design
repository, embedded as the studio/design submodule. Designers can clone it
independently. The framework pins its revision; shared UI changes are reviewed
and imported separately into apps/ai-manager. See
repository workflow for checkout, publishing,
three-way sync and importing designer changes.
bash apps/ai-manager/prototype/scripts/export-repo.sh builds
agent-studio-prototype-export.zip in the repository root: a standalone single-commit git repo holding
apps/ai-manager + the shared/*.tgz tarball deps at the same paths, so
her commits apply back to the monorepo without rewriting
(git diff <seed-commit>..HEAD / format-patch → am). It exports the
working tree (gitignore-filtered — the branch's interdependent WIP means bare
HEAD may not build), strips the fixtures that name real people
(users/groups/session/recents/favorites/notifications/conversations — the
auth mock falls back to a built-in "Design Prototype" user), and prints a
review list of email-like strings remaining in demo fixtures before you send
it anywhere.
ZIP generation is for disposable snapshots. It refuses to overwrite a submodule,
clone, dirty export or unrelated nonempty directory. --no-archive creates only
the snapshot tree for the repository sync helper. Never export directly over
studio/design.
The export incorporates the designer's root launcher: npm install runs
npm ci --package-lock=true inside apps/ai-manager, and npm run dev,
npm start or npm run prototype starts the mock app on port 3100.
npm run dev -- --port 3101 forwards an alternate port. The root .npmrc
disables an unnecessary root lockfile; the app's lockfile remains authoritative.
Use npm run deps:update for intentional dependency updates.
Use Node.js 22+ and npm 10.5.1+ to meet the current app dependencies' requirements.
prototype/export/package.json
is the reusable launcher template. Export generation removes the copied app's
predev monorepo hook and routes dev/dev:only to dev:prototype, preserving
the current dependencies and prototype flags. These adjustments apply only to
the exported copy; main-app startup remains unchanged. Root guidance and README
describe the same commands. Import UI changes against the export's baseline,
not by copying its package/startup files over the live application's versions.
Agent guidance in the export¶
The exporter installs
prototype/designer-claude.md
as the exported repository's root CLAUDE.md, with an AGENTS.md dispatcher
for other coding agents. The source template has a different filename so it
does not activate designer-specific rules in normal development. Framework guides defer to this policy inside studio/design; the copied app
guide describes current production behavior without blocking design changes.
The designer directs visual and behavioral changes, including experiments that remove capabilities, simplify required inputs or need a new production contract. Current implementation invariants are reference material, not a veto. Agents implement the requested prototype and extend local mock APIs as needed; missing backend support does not require approval or a disabled control.
When preparing a commit, the coding agent updates
docs/design-change-log.md
inside the exported app with the actual change, potential production impact,
engineering follow-up and verification. The file travels with the commit.
Unknown impact, backend gaps and failing checks are recorded without blocking a
design commit. There is no required waiver, engineering signoff or blocking hook.
Complex contracts can link to docs/prototype-design-decisions.md; visual-only
changes need a short note. The designer does not maintain a checklist.
The root guidance, dispatcher and app guide explain this scope consistently.
Framework instructions defer to the prototype workflow inside studio/design;
production imports retain the normal validation, documentation and release rules.
Review shared UI, mock changes and impact notes together before production import.
A mock remains a simulation even when the requested interaction works locally.
The export script resolves its source repository from its own location. Use the
main apps/ai-manager exporter for new handoffs: the current app now includes
the conversational preview behind FEATURE_CONVERSATIONAL_DESIGN (default off
in regular deployments, enabled by dev:prototype). The older studio/real
copy remains a historical design reference. See Studio rollout.
Shared conversational design UI¶
/studio and /studio/build/[briefId] always render LiveDesignHome and
LiveDesignWorkspace. The output tabs, Thinking activity, composer and reference
controls are shared with the real app. Do not add prototype-only page branches;
mock an API contract instead so designer edits apply to the actual UI.
prototype/msw/handlers/studio.ts implements design list/create/read,
NDJSON assistant turns and reference upload/download/delete. prototype/studio/
contains the local state, deterministic reply schedule, draft adapters and
IndexedDB attachment store. Current sessions use the resettable tsf-proto:v1:
overlay; files use the tsf-proto-design-references IndexedDB database. The reset
badge clears both. Legacy StudioBrief storage is left untouched, but those IDs
are not migrated or opened as current design sessions.
Creation is idempotent by creation key. Each turn records its request ID and schedule before streaming; polling or reloading projects progress from that saved schedule and completes the local draft once. Duplicate requests return 409 rather than replaying. References honor the current file-type, size and count limits; download uses a browser fetch so both the real API and MSW can serve the same control, with failures shown in the workspace.
prototype/studio/timing.ts defaults AI turns to 30,000 ms. Thinking updates
arrive at roughly 0.6, 5.4, 10.8, 18 and 24 seconds (plus reference metadata at
14.4 seconds when present). Text streams from 64% to 94% of the schedule and
completion occurs at its end. This is simulated UX timing, not measured
production performance; the real application never uses these delays.
NEXT_PUBLIC_PROTOTYPE_AI_LATENCY_MS overrides total mock duration, bounded to
1,000–120,000 ms, with non-positive/non-finite values falling back to 30,000.
Restart the prototype after changing this client-side setting. Existing pending
turns retain the schedule saved at acceptance.
The four types create actual local mock draft rows with matching base/draft
aliases and version metadata. Voice/Chat use the app's stack builder, Translation
uses its translation defaults, and Background starts with an inactive blank
workflow. Example turns update descriptions and conversational prompts while
preserving other editor settings. Overview data is sample presentation content:
it does not compile specialists/procedures, interpret attachments, connect a
system or run tests. Reference sharing is simulated and validation stays
not_run. The prototype badge, sample replies and designer guidance explain
these limitations without substituting a different screen.
The mock draft writer reconciles updates into any existing collab Y.Doc as well as stored rows. The gateway flushes pending edits and releases a document when its last client leaves. Reopening hydrates the current stored draft, so a later design turn cannot be overwritten by a cached editor document.
Run npm run test:prototype against a running export (default port 3100), or set
E2E_BASE_URL for another port. The separate Playwright config needs no database
or auth setup. It checks all four editor handoffs, streamed replies, reload,
reference round-trips and limits, idempotent turns, reset and mobile panels.
For repeated checks, launch the prototype with
NEXT_PUBLIC_PROTOTYPE_AI_LATENCY_MS=1000. Also run the opt-in elapsed-time test
against a default-timing server:
E2E_REALISTIC_LATENCY=1 npm run test:prototype -- --grep '30-second'.
No new production backend capability is required for this visual parity change;
the live APIs already own these operations. See conversational design
and the user guide.
Fixture capture¶
npm run prototype:capture -- --base-url <env> --login <user> <pass> (or
--cookie "ai_manager_session=…"). Curated endpoint list + per-id expansion
(agents list rows key on agent_id, and the script follows
redirectToDraft → captures the {id}:draft rows the editor actually
fetches). Scrubbing redacts secret-shaped keys and token-shaped substrings
inside values (a custom_headers JSON string holding Bearer tsf_… was the
live case), then hard-fails the run if any token pattern survives. Filenames
replace : with ~ (Windows); index.json maps real pathnames to files.