Imported from omnipotentchaos/RoundTable-AI (
AGENTS.md). Install upstream withnpx skills add omnipotentchaos/RoundTable-AI. Copyright stays with the author.
Agent Development Guide
This guide is for coding agents making changes in agent-quickstart-nextjs.
How to Load
This repository uses progressive disclosure documentation. Docs live under docs/ai/ in three levels.
- Read docs/ai/L0_repo_card.md to identify the repo.
- Load ALL 8 files in docs/ai/L1/. They are small — load all upfront.
- Follow L2 deep-dive links only when L1 isn't detailed enough. The index is at docs/ai/L1/L2/_index.md.
This repo declares Recipe Role: base in L0, so also read docs/ai/RECIPE.md when evaluating extension points, invariants, or stable contracts.
The sections below (Start Here, Patterns, Anti-Patterns, etc.) remain the canonical contributor handbook for hands-on work; the docs/ai/ tree is the structured summary used by AI agents.
Start Here
- Read README.md for setup, commands, verification, and deployment.
- Use docs/ai/RECIPE.md for the base quickstart recipe contract.
- Use docs/ai/L1/L2/from_scratch_bootstrap.md for the baseline implementation map.
- Use docs/ai/L1/L2/transcript_pipeline.md for transcript and RTM behavior.
- For layout and responsibilities inside
components/,app/api/, andlib/, use docs/ai/L1/03_code_map.md and docs/ai/L1/02_architecture.md.
Current System Shape
- App shell: Next.js 16 App Router, React 19, and TypeScript
- Client RTC:
agora-rtc-reacthooks overagora-rtc-sdk-ng - Messaging:
agora-rtmfor transcripts, agent state, metrics, and error events - Toolkit core:
agora-agent-client-toolkitforAgoraVoiceAI, transcript helpers, and turn status - UI components:
agora-agent-uikitfor visualizer, transcript, and mic controls - Server SDK:
agora-agentsfor managed agent session startup - Product APIs: company interviews, signed invitations, sessions, artifacts, assessment release, MCP, and Agora webhooks live in
app/api - Voice pipeline: Agora-managed STT/TTS with an authenticated RoundTable custom LLM/controller endpoint
- Persistence and auth: Supabase in production; process-local memory is development/test fallback only
- Workspaces: Monaco code and React Flow canvas checkpoints; server-selected E2B execution
Supported Modes
Local Development
- Run from the repo root with
npm run dev. - Next.js serves the app and the route handlers at
http://localhost:3000. - Local credentials are read from
.env.local, usually written byagora project env write .env.local.
Vercel Deployment
- Deploy the repository as a single Next.js app.
- Set
NEXT_PUBLIC_AGORA_APP_IDandNEXT_AGORA_APP_CERTIFICATEin the deployment target. - Keep
NEXT_AGORA_APP_CERTIFICATEserver-side only.
Routing / Ownership
- UI and RTC/RTM client lifecycle live in
components. - Browser-facing API routes live in
app/api. - Shared constants and transcript normalization live in
lib. - If a workflow, request contract, or ownership boundary changes, update
README.md,AGENTS.md, and the relevantdocs/ai/files in the same change.
Key Files
app/api/generate-agora-token/route.ts: issues RTC + RTM tokens for the browser user.app/api/invite-agent/route.ts: starts the managed agent session; edit here for system prompt, VAD, model, or voice changes.app/api/stop-conversation/route.ts: stops the agent session.app/api/ai/chat/completions/route.ts: per-session authenticated OpenAI-compatible adaptive controller used by Agora.app/api/interviews/: company definition, plan, version, publication, and status routes.app/api/invitations/: public invitation preview and consent-gated guest session bootstrap.app/api/sessions/: authenticated lifecycle, event, artifact, tool, results, and release routes.app/api/mcp/[grant]/route.ts: session-scoped Streamable HTTP workspace tools.app/api/webhooks/agora/route.ts: signed lifecycle reconciliation and finalization.components/LandingPage.tsx: session bootstrap, RTM setup, provider wiring, and conversation lifecycle.components/ConversationComponent.tsx: RTC join, mic publication,AgoraVoiceAIinit, transcript state, and renewals.components/QuickstartConversationLayout.tsx: in-call header, transcript rail, and controls dock.components/QuickstartPipelineMetrics.tsx: per-stage latency chips fromAGENT_METRICS.components/QuickstartTranscriptPanel.tsx: live transcript rail.lib/agora.ts: shared agent UID defaults.lib/agora-server.ts: combined RTC/RTM token generation and managed interview-agent lifecycle.lib/interview-controller.ts: all-role evidence evaluation and deterministic next-speaker rules.lib/interview-store.ts: Supabase persistence with development/test memory fallback.lib/assessment.ts: structured evidence-only final assessment.lib/conversation.ts: transcript normalization and visualizer state mapping.env.local.example: local environment template.scripts/verify-api-contracts.ts: route contract verification.
Patterns
StrictMode Guard (isReady)
Both useJoin and useLocalMicrophoneTrack are gated by isReady to prevent double initialization in React StrictMode dev mode. The cleanup fires synchronously before any setTimeout, so only the real second mount's timer fires.
const [isReady, setIsReady] = useState(false);
useEffect(() => {
let cancelled = false;
const id = setTimeout(() => {
if (!cancelled) setIsReady(true);
}, 0);
return () => {
cancelled = true;
clearTimeout(id);
setIsReady(false);
};
}, []);
const { isConnected: joinSuccess } = useJoin(config, isReady);
const { localMicrophoneTrack } = useLocalMicrophoneTrack(isReady);
Hook Ownership
useJoinownsclient.leave(); never call it manually.useLocalMicrophoneTrackowns track lifecycle; do not manually call.close().usePublishowns publish state; mute withtrack.setEnabled()and do not manually unpublish.
AgoraVoiceAI Init
Initialize AgoraVoiceAI from agora-agent-client-toolkit inside ConversationComponent, gated on isReady && joinSuccess.
useEffect(() => {
if (!isReady || !joinSuccess) return;
// AgoraVoiceAI.init() is called here exactly once.
}, [isReady, joinSuccess]);
isReady becomes true only after the StrictMode fake-unmount cycle completes. Once isReady is true, React does not double invoke the effect for later dependency changes such as joinSuccess becoming true.
Transcript and UI Mapping
- Manage
transcriptandagentStatethroughuseStateplusai.on(TRANSCRIPT_UPDATED, ...)andai.on(AGENT_STATE_CHANGED, ...). - The toolkit uses
uid="0"as a sentinel for the local user's speech. Remap that value toclient.uidbefore passing messages intoQuickstartTranscriptPanel, or user speech renders on the agent side. - Include
INTERRUPTEDturns inmessageList; filter onlyIN_PROGRESS. If the agent's first turn is interrupted and omitted,messageListstays empty and the transcript panel never shows that first turn.
Tokens and Styling
- RTM token access must come from
RtcTokenBuilder.buildTokenWithRtm; a standard RTC-only token does not grant RTM access. - Tailwind must scan uikit classes with
./node_modules/agora-agent-uikit/dist/**/*.{js,mjs}intailwind.config.ts.
Working Rules
- Prefer the smallest change that keeps the quickstart copyable and production-style.
- Keep RTC client creation StrictMode-safe with
useRef, notuseMemo. - Keep token generation on
RtcTokenBuilder.buildTokenWithRtm. - Keep transcript UID remapping aligned with the toolkit sentinel behavior.
- Do not require third-party vendor API keys unless the code actually introduces a BYOK provider path.
- Keep README, AGENTS, and
docs/ai/aligned with implementation changes.
Commands
From the repo root:
npm install
npm run doctor
npm run dev
npm run verify
Useful narrower checks:
npm run lint
npm run typecheck
npm run verify:api
npm run build
Verification Safety
- Safe without live Agora credentials:
npm run lintnpm run typechecknpm run verify:apinpm run build
- Requires local env setup but not a live Agora session:
npm run doctornpm run verify
- Often blocked inside restricted sandboxes because of port binding or process spawning:
npm run dev
Anti-Patterns / What NOT To Do
- Do not call
client.leave()manually; it breaksuseJoincleanup. - Do not call
localMicrophoneTrack.close()manually; it breaks hook ownership. - Do not remove the
isReadyguard. - Do not set
reactStrictMode: falseas a workaround. - Do not use the deprecated
turnDetection.type: 'agora_vad'flat API; useturnDetection.config.start_of_speechandturnDetection.config.end_of_speech. - Do not replace
RtcTokenBuilder.buildTokenWithRtmwith an RTC-only token builder. - Do not hide SDK requirements only in
CLAUDE.md; all agent-facing guidance belongs inAGENTS.md. - Do not restore browser-owned candidate scores, role selection, or prompt updates; authoritative interview state is server-owned and versioned.
- Do not accept caller-supplied system prompts or model IDs at the custom LLM boundary.
- Do not publish transcript, answers, artifacts, scores, or assessment tables through Realtime; publish only
company_session_status. - Do not use resume claims as evidence. Resume text may only seed verification questions.
- Do not emit automatic hire/reject decisions; every final assessment must keep
humanReviewRequired: true.
Done Criteria
Before finishing a change:
- Run the narrowest relevant verification command.
- For shipped app/runtime changes, ensure
npm run verifypasses. When live credentials are unavailable, run every offline subcommand and record the blocked doctor/live gates explicitly. - If you changed files in
components/orapp/api/, verify thatREADME.md, this file, and the relevantdocs/ai/files still match the implementation. - Update root README and affected docs when workflow, request contracts, architecture, or environment guidance changes.
- If the change touches workflows, interfaces, gotchas, or security details, update the matching file under docs/ai/L1/ and bump
Last Reviewedin docs/ai/L0_repo_card.md.
Git Conventions
Commit messages — conventional commits
- Format:
type: descriptionortype(scope): description - Types:
feat:(new feature),fix:(bug fix),chore:(maintenance, version bumps),test:(test additions/changes),docs:(documentation) - Scoped variant:
feat(scope):,fix(scope):— e.g.feat(api): add stop-conversation status flag - Lowercase after prefix —
feat: add feature, notfeat: Add feature - Present tense — "add feature", not "added feature"
- PR number appended —
feat: add feature (#123)
Branch names
- Format:
type/short-description— lowercase, hyphen-separated - Types match commit types:
feat/,fix/,chore/,test/,docs/ - Examples:
feat/agent-metrics,fix/transcript-uid,docs/progressive-disclosure
General rules
- No AI tool names — never mention claude, cursor, copilot, cody, aider, gemini, codex, chatgpt, or gpt-3/4 in commit messages or PR descriptions.
- No Co-Authored-By trailers — omit AI attribution lines.
- No
--no-verify— let git hooks run normally. - No git config changes — do not modify
user.nameoruser.email.
Doc Commands
| Command | When to use |
|---|---|
| generate docs | No docs/ai/ directory exists yet |
| update docs | Code changed since the Last Reviewed date in L0 |
| test docs | Verify docs give agents the right context (writes docs/ai/test-results.md) |
| fix docs | Close findings from a docs review or test run |
The generator and tester live in the AgoraIO-Community/ai-devkit skill set. See the progressive disclosure standard for the full specification.
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.