RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/bytedance-deer-flow-github-copilot-instructions ↔ bytedance-deer-flow-frontend-agents

Comparison

A · Copilot instructions · bytedance/deer-flowB · AGENTS.md · bytedance/deer-flow
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections015130%
Commands5121019%
Section tags64250%

What each file covers

Sections

0 shared · 15 only in A · 13 only in B
  • − Copilot Onboarding Instructions for DeerFlow
  • − 1) Repository Summary
  • − 2) Runtime and Toolchain Requirements
  • − 3) Build/Test/Lint/Run - Verified Command Sequences
  • − A. Bootstrap and install
  • − B. Backend CI-equivalent validation
  • − C. Frontend validation
  • − D. Run locally (all services)
  • − E. Config bootstrap
  • − 4) Command Order That Minimizes Failures
  • − 5) Project Layout and Architecture (High-Value Paths)
  • − 6) Pre-Checkin / Validation Expectations
  • − 7) Non-Obvious Dependencies and Gotchas
  • − 8) Root Inventory (quick reference)
  • − 9) Instruction Priority
  • + AGENTS.md
  • + Project Overview
  • + Core dependencies
  • + Commands
  • + Architecture
  • + Source Layout (`src/`)
  • + Data Flow
  • + Key Patterns
  • + Interaction Ownership
  • + Code Style
  • + Environment
  • + Resources
  • + Contributing

Commands

5 shared · 12 only in A · 10 only in B
  • − make check
  • − make install
  • − make lint
  • − make test
  • − make stop
  • − make config
  • − make docker-*
  • − ruff check .
  • − uv sync --group dev
  • − docker/docker-compose-dev.yaml
  • − docker/
  • − pnpm install
  • + pnpm dev
  • + pnpm lint:fix
  • + pnpm format
  • + pnpm format:write
  • + pnpm test
  • + pnpm test:e2e
  • + tsc --noEmit
  • + pnpm start
  • + playwright.config.ts
  • + pnpm perf:check
  •   pnpm lint
  •   pnpm typecheck
  •   make dev
  •   pnpm build
  •   pnpm check

Section tags

6 shared · 4 only in A · 2 only in B
  • − build
  • − git-pr
  • − monorepo
  • − agent-behaviour
  • + testing-strategy
  • + ui
  •   setup
  •   test
  •   lint-format
  •   code-style
  •   architecture
  •   dependencies

Line diff

+148 added−165 removed49 unchanged22.9% identical
bytedance/deer-flow · .github/copilot-instructions.md
@@ −1 @@
1# Copilot Onboarding Instructions for DeerFlow
2 
3Use this file as the default operating guide for this repository. Follow it first, and only search the codebase when this file is incomplete or incorrect.
4 
5## 1) Repository Summary
6 
7DeerFlow is a full-stack "super agent harness".
8 
9- Backend: Python 3.12, LangGraph + FastAPI gateway, sandbox/tool system, memory, MCP integration.
10- Frontend: Next.js 16 + React 19 + TypeScript + pnpm.
11- Local dev entrypoint: root `Makefile` starts backend + frontend + nginx on `http://localhost:2026`.
12- Docker dev entrypoint: `make docker-*` (mode-aware provisioner startup from `config.yaml`).
13 
14Current repo footprint is medium-large (backend service, frontend app, docker stack, skills library, docs).
15 
16## 2) Runtime and Toolchain Requirements
 
 
 
17 
18Validated in this repo on macOS:
19 
20- Node.js `>=22` (validated with Node `23.11.0`)
21- pnpm (repo expects lockfile generated by pnpm 10; validated with pnpm `10.26.2` and `10.15.0`)
22- Python `>=3.12` (CI uses `3.12`)
23- `uv` (validated with `0.7.20`)
24- `nginx` (required for `make dev` unified local endpoint)
 
 
 
 
 
 
 
25 
26Always run from repo root unless a command explicitly says otherwise.
27 
28## 3) Build/Test/Lint/Run - Verified Command Sequences
29 
30These were executed and validated in this repository.
31 
32### A. Bootstrap and install
33 
341. Check prerequisites:
35 
36```bash
37make check
38```
39 
40Observed: passes when required tools are installed.
41 
422. Install dependencies (recommended order: backend then frontend, as implemented by `make install`):
43 
44```bash
45make install
46```
47 
48### B. Backend CI-equivalent validation
49 
50Run from `backend/`:
51 
52```bash
53make lint
54make test
55```
 
 
 
 
 
 
 
 
 
 
56 
57Validated results:
58 
59- `make lint`: pass (`ruff check .`)
60- `make test`: pass (`277 passed, 15 warnings in ~76.6s`)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61 
62CI parity:
63 
64- `.github/workflows/backend-unit-tests.yml` runs on pull requests.
65- CI executes `uv sync --group dev`, then `make lint`, then `make test` in `backend/`.
66 
67### C. Frontend validation
68 
69Run from `frontend/`.
70 
71Recommended reliable sequence:
72 
73```bash
74pnpm lint
75pnpm typecheck
76BETTER_AUTH_SECRET=local-dev-secret pnpm build
77```
78 
79Observed failure modes and workarounds:
80 
81- `pnpm build` fails without `BETTER_AUTH_SECRET` in production-mode env validation.
82- Workaround: set `BETTER_AUTH_SECRET` (best) or set `SKIP_ENV_VALIDATION=1`.
83- Even with `SKIP_ENV_VALIDATION=1`, Better Auth can still warn/error in logs about default secret; prefer setting a real non-default secret.
84- `pnpm check` currently fails (`next lint` invocation is incompatible here and resolves to an invalid directory). Do not rely on `pnpm check`; run `pnpm lint` and `pnpm typecheck` explicitly.
85 
86### D. Run locally (all services)
 
 
 
87 
88From root:
89 
90```bash
91make dev
92```
93 
94Behavior:
95 
96- Stops existing local services first.
97- Starts Gateway (`8001`, with the embedded LangGraph-compatible runtime), Frontend (`3000`), nginx (`2026`). There is no standalone LangGraph service.
98- Unified app endpoint: `http://localhost:2026`.
99- Logs: `logs/gateway.log`, `logs/frontend.log`, `logs/nginx.log`.
 
 
 
 
 
 
 
 
 
 
 
 
 
100 
101Stop services:
102 
103```bash
104make stop
105```
 
 
 
 
 
 
 
 
 
 
 
 
106 
107If tool sessions/timeouts interrupt `make dev`, run `make stop` again to ensure cleanup.
108 
109### E. Config bootstrap
 
 
 
 
110 
111From root:
112 
113```bash
114make config
115```
 
 
 
116 
117Important behavior:
118 
119- This intentionally aborts if `config.yaml` (or `config.yml`/`configure.yml`) already exists.
120- Use `make config` only for first-time setup in a clean clone.
121 
122## 4) Command Order That Minimizes Failures
123 
124Use this exact order for local code changes:
 
 
 
125 
1261. `make check`
1272. `make install` (if frontend fails with proxy errors, rerun frontend install with proxy vars unset)
1283. Backend checks: `cd backend && make lint && make test`
1294. Frontend checks: `cd frontend && pnpm lint && pnpm typecheck`
1305. Frontend build (if UI changes or release-sensitive changes): `BETTER_AUTH_SECRET=... pnpm build`
131 
132Always run backend lint/tests before opening PRs because that is what CI enforces.
133 
134## 5) Project Layout and Architecture (High-Value Paths)
 
 
 
 
135 
136Root-level orchestration and config:
137 
138- `Makefile` - main local/dev/docker command entrypoints
139- `config.example.yaml` - primary app config template
140- `config.yaml` - local active config (gitignored)
141- `docker/docker-compose-dev.yaml` - Docker dev topology
142- `.github/workflows/backend-unit-tests.yml` - PR validation workflow
143 
144Backend core:
145 
146- `backend/packages/harness/deerflow/agents/` - lead agent, middleware chain, memory
147- `backend/app/gateway/` - FastAPI gateway API
148- `backend/packages/harness/deerflow/sandbox/` - sandbox provider + tool wrappers
149- `backend/packages/harness/deerflow/subagents/` - subagent registry/execution
150- `backend/packages/harness/deerflow/mcp/` - MCP integration
151- `backend/langgraph.json` - graph entrypoint (`deerflow.agents:make_lead_agent`)
152- `backend/pyproject.toml` - Python deps and `requires-python`
153- `backend/ruff.toml` - lint/format policy
154- `backend/tests/` - backend unit and integration-like tests
155 
156Frontend core:
157 
158- `frontend/src/app/` - Next.js routes/pages
159- `frontend/src/components/` - UI components
160- `frontend/src/core/` - app logic (threads, tools, API, models)
161- `frontend/src/env.js` - env schema/validation (critical for build behavior)
162- `frontend/package.json` - scripts/deps
163- `frontend/eslint.config.js` - lint rules
164- `frontend/tsconfig.json` - TS config
165 
166Skills and assets:
167 
168- `skills/public/` - built-in skill packs loaded by agent runtime
169 
170## 6) Pre-Checkin / Validation Expectations
171 
172Before submitting changes, run at minimum:
173 
174- Backend: `cd backend && make lint && make test`
175- Frontend (if touched): `cd frontend && pnpm lint && pnpm typecheck`
176- Frontend build when changing env/auth/routing/build-sensitive files: `BETTER_AUTH_SECRET=... pnpm build`
177 
178If touching orchestration/config (`Makefile`, `docker/*`, `config*.yaml`), also run `make dev` and verify the four services start.
179 
180## 7) Non-Obvious Dependencies and Gotchas
181 
182- Proxy env vars can silently break frontend network operations (`pnpm install`/registry access).
183- `BETTER_AUTH_SECRET` is effectively required for reliable frontend production build validation.
184- Next.js may warn about multiple lockfiles and workspace root inference; this is currently a warning, not a build blocker.
185- `make config` is non-idempotent by design when config already exists.
186- `make dev` includes process cleanup and can emit shutdown logs/noise if interrupted; this is expected.
187 
188## 8) Root Inventory (quick reference)
189 
190Important root entries:
191 
192- `.github/`
193- `backend/`
194- `frontend/`
195- `docker/`
196- `skills/`
197- `scripts/`
198- `docs/`
199- `README.md`
200- `CONTRIBUTING.md`
201- `Makefile`
202- `config.example.yaml`
203- `extensions_config.example.json`
204 
205## 9) Instruction Priority
206 
207Trust this onboarding guide first.
208 
209Only do broad repo searches (`grep/find/code search`) when:
210 
211- you need file-level implementation details not listed here,
212- a command here fails and you need updated replacement behavior,
213- or CI/workflow definitions have changed since this file was written.
214 
bytedance/deer-flow · frontend/AGENTS.md
@@ +1 @@
1# AGENTS.md
2 
3This file provides guidance to AI coding agents (Claude Code, Codex, and others) when working with the DeerFlow frontend. It is the source of truth; the sibling `CLAUDE.md` imports it via `@AGENTS.md`.
4 
5## Project Overview
6 
7DeerFlow Frontend is a Next.js 16 web interface for an AI agent system. It communicates with a LangGraph-based backend to provide thread-based AI conversations with streaming responses, artifacts, and a skills/tools system.
8 
9**Stack**: Next.js 16, React 19, TypeScript 5.8, Tailwind CSS 4, pnpm 10.26.2. Requires Node.js 22+ and pnpm 10.26.2+.
 
 
 
10 
11### Core dependencies
12 
13- **LangGraph SDK** (`@langchain/langgraph-sdk` ^1.5.3) — Agent orchestration and streaming
14- **LangChain Core** (`@langchain/core` ^1.1.15) — Fundamental AI building blocks
15- **TanStack Query** (`@tanstack/react-query` ^5.90.17) — Server state management
16- **UI**: Shadcn UI, MagicUI, React Bits, and Vercel AI SDK elements (generated from registries — see Code Style)
17 
18## Commands
19 
20| Command | Purpose |
21| ---------------- | ------------------------------------------------- |
22| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) |
23| `pnpm build` | Production build |
24| `pnpm check` | Lint + type check (run before committing) |
25| `pnpm lint` | ESLint only |
26| `pnpm lint:fix` | ESLint with auto-fix |
27| `pnpm format` | Prettier check (`pnpm format:write` to apply) |
28| `pnpm test` | Run unit tests with Rstest |
29| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) |
30| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) |
31| `pnpm start` | Start production server |
32 
33Unit tests live under `tests/unit/` and mirror the `src/` layout (e.g., `tests/unit/core/api/stream-mode.test.ts` tests `src/core/api/stream-mode.ts`). Powered by Rstest; import source modules via the `@/` path alias.
34 
35Rstest runs them as two projects (`rstest.config.ts`). `*.test.ts` / `*.test.tsx` run in a plain **node** environment — that is nearly the whole suite, and it is the default for anything that is pure logic. `*.dom.test.ts` / `*.dom.test.tsx` run in **happy-dom**, for tests that need a document: hooks driven through `renderHook` from `@testing-library/react`, and components. Keep the split — a DOM environment costs roughly 3x the runtime of the node suite, so tests that do not render should not opt into it. A hook whose behavior only exists under real React (effect ordering, cleanup on unmount, re-render on store change) belongs in a `.dom.test.*` file rather than a node test that mocks `react` itself.
36 
37E2E tests live under `tests/e2e/` and use Playwright with Chromium. They mock all backend APIs via `page.route()` network interception and test real page interactions (navigation, chat input, streaming responses). Config: `playwright.config.ts`.
38 
39## Architecture
40 
 
 
 
 
41```
42Frontend (Next.js) ──▶ LangGraph SDK ──▶ LangGraph Backend (lead_agent)
43 ├── Sub-Agents
44 └── Tools & Skills
 
 
 
 
45```
46 
47The frontend is a stateful chat application. Users create **threads** (conversations), send messages, set thread-scoped `/goal` completion conditions, and receive streamed AI responses. The backend orchestrates agents that can produce **artifacts** (files/code), **todos**, and goal state updates.
48 
49### Source Layout (`src/`)
50 
51- **`app/`** — Next.js App Router. Routes include `/` (landing), `/workspace/chats/[thread_id]` (chat), `/workspace/agents/[agent_name]` and `/workspace/agents/new` (custom agents), `/blog/…`, the `(auth)/{login,setup,auth/callback}` flow, `/[lang]/docs/…`, and `/api/…` route handlers (e.g. `/api/memory`).
52- **`components/`** — React components:
53 - `ui/` — Shadcn UI primitives (auto-generated, ESLint-ignored)
54 - `ai-elements/` — Vercel AI SDK elements (auto-generated, ESLint-ignored)
55 - `workspace/` — Chat page components (messages, artifacts, settings)
56 - `landing/` — Landing page sections
57 - `docs/` — Docs / MDX rendering components
58- **`core/`** — Business logic, the heart of the app. Domains include `threads/` (creation, streaming, state), `api/` (LangGraph client singleton), `agents/` (custom agents), `auth/` (authentication), `artifacts/`, `channels/` (IM connections), `integrations/` (managed third-party integration status/install clients such as Lark CLI), `i18n/` (en-US, zh-CN), `settings/`, `memory/`, `skills/`, `messages/`, `mcp/`, `models/`, `input-polish/` (pre-send draft rewrite API), `voice-input/` (browser speech-recognition helpers), `suggestions/`, `tasks/`, `todos/`, `tools/`, `workspace-changes/` (run-scoped changed-file summaries and diff fetching), `config/`, `notification/`, `blog/`, plus rendering helpers (`rehype/`, `streamdown/`) and `utils/`.
59- **`hooks/`** — Shared React hooks
60- **`lib/`** — Utilities (`cn()` from clsx + tailwind-merge)
61- **`content/`** — MDX content (blog posts, docs) rendered by the app
62- **`styles/`** — Global CSS with Tailwind v4 `@import` syntax and CSS variables for theming
63- **`typings/`** — Ambient TypeScript declarations
64- Root files: `env.js` (env validation), `mdx-components.ts` (MDX component map)
65 
66### Data Flow
67 
681. Optional composer helpers such as `core/input-polish` can rewrite the local draft before submission, and `core/voice-input` can transcribe browser microphone input into that same local draft; confirmed user input then flows to thread hooks (`core/threads/hooks.ts`) → LangGraph SDK streaming
692. Stream events update thread state (messages, artifacts, todos, goal). The main thread stream uses the LangGraph SDK's `throttle: true` mode so updates received in the same macrotask coalesce before React is notified; do not replace it with a numeric delay without validating the SDK's trailing-debounce behavior on a continuous stream.
70 File-tool artifact auto-open work must run in an effect with timer cleanup; never schedule timers while rendering streamed `write_file` or `str_replace` updates.
71 `ThreadState.artifacts` remains the authoritative artifact list. The artifacts provider persists only thread-scoped panel UI state (`open`, selected path, and a refresh bootstrap cache) in session storage; an initial empty stream value must not overwrite that restored state before history finishes loading.
72 Formal artifact content is refreshed once when the run finishes; transient `write-file:` previews remain message-driven.
73 The detail view exposes explicit editing only for an already-opened formal UTF-8 text artifact under `/mnt/user-data/outputs`. Drafts stay in provider memory until Save so switching right-side panels cannot discard them, render in Markdown/HTML preview, and are protected from remote refreshes by the loaded SHA-256 revision. Saving is disabled during an active run; a changed revision preserves the draft and surfaces a conflict instead of overwriting agent output.
74 Regular artifact text loads request at most the first 1 MiB through an HTTP
75 byte range. A truncated preview must stay lightweight and expose an explicit
76 full-file action; do not mount CodeMirror for that artifact until the user
77 requests and receives the complete content. The Gateway retains range
78 ownership and returns 206/416 through `FileResponse`.
793. `useThreadHistory` loads persisted conversation pages from `GET /api/threads/{id}/messages/page`, preserving the backend's thread-global event `seq`; rendering overlays checkpoint/live copies at their matching canonical identities (a summarized checkpoint may contain a protected early input plus a recent tail). Context-compaction rescue diffs every retained visible identity rather than slicing at the first anchor, and keeps a run-scoped ledger of committed visible messages so replacement updates and repeated rolling checkpoint windows cannot erase an already displayed step. The resolver suppresses checkpoint/transient prefixes whose canonical position is still behind an unloaded cursor page instead of collapsing that unknown gap before a recent anchor, then adds optimistic messages without timestamp re-sorting. History invalidation preserves already-loaded pages so their established ordering positions are not discarded. Dynamic context re-keys the submitted user message from `X` to `X__user`; UI identity matching normalizes that reserved suffix only for human messages so the submitted frame and checkpoint replacement remain one visible turn. A locally submitted turn also records its pre-submit identity baseline: if `messages-tuple` publishes new AI/tool steps before `values` publishes that turn's human message, render ordering moves only those non-baseline visible steps behind the new human while leaving history, hidden controls, and reconnected runs untouched. Keep that local order anchor through finish, stop, and stream error because the SDK's settled frame can retain transient event order; replace it on the next local submit and clear it on thread switch or replay-gap recovery.
804. Stop actions call the LangGraph SDK stream stop path; `core/threads/hooks.ts` invalidates current-thread, thread-history, token-usage, and sidebar/search caches immediately and schedules one follow-up refetch because SDK stop may finish via abort + fire-and-forget cancel before backend title finalization commits
815. TanStack Query manages server state; localStorage stores user settings. The
82 Settings > Tools MCP switch calls the targeted `PATCH /api/mcp/config`
83 mutation, disables switches until that mutation's success refetch completes,
84 displays the backend error `detail` through a toast, and invalidates
85 `["mcpConfig"]` only after success.
866. Components subscribe to thread state and render updates
87 
88The chat header's context-window control is intentionally persistent: while `context_usage` is unavailable, `ContextUsageBadge` renders a gauge placeholder rather than unmounting; once data arrives, the same position shows the percentage. `useThreadTokenUsage` retains placeholder data only when the response `thread_id` still matches the active route, so same-thread refetches do not flicker and cross-thread navigation never displays the previous chat's usage.
89 
90Run duration is run-scoped UI metadata even though the compatibility field `additional_kwargs.turn_duration` is repeated on historical AI messages. `core/messages/run-duration.ts` folds those copies into one display anchored after the run's last visible message group. `MessageList` owns the temporary client-side duration for a just-completed live turn until authoritative history arrives. The duration is total run wall-clock time, not per-message reasoning time; reasoning disclosure and run activity/duration are rendered separately.
 
91 
92The workspace-change card follows the same rule: it is resolved from `(threadId, runId)` alone, so every AI message of a run would render an identical copy. A run ends in more than one terminal assistant bubble whenever the model emits answer text that never gains a tool call, so `core/messages/workspace-change-anchor.ts` picks the run's last assistant bubble and `MessageListItem` renders the badge only for that anchor (#4555). Any future run-scoped display belongs in the same place — do not hang one off every message. The two anchor helpers deliberately differ in which group types they accept as a run's last position, because an anchor is only useful where the display is actually rendered: run duration is emitted by `MessageList` around every group, so it accepts any type, while the workspace-change card comes from `MessageListItem` and so restricts to `assistant`. Keep a new helper's candidate set matched to its own render site rather than unifying them.
93 
94Composer drafts are tab-scoped browser state. `core/threads/composer-draft.ts` stores only text plus the selected slash-skill name in `sessionStorage`, keyed by user, agent, and logical conversation scope. New-chat pages pass the stable scope `"new"` because their runtime `threadId` is a fresh UUID on every reload; established conversations use their real thread ID. `InputBox` waits for enabled skills before restoring a skill chip, degrades a missing/disabled skill back to editable slash text, and clears the stored draft through `SendMessageOptions.onSent` only after the send passes the in-flight guard. Attachments, sidecar quotes, voice state, and polish undo state are not persisted.
95 
96Auth UI note: the login page's "keep me signed in" option submits only `remember_me` to the Gateway and may persist only the email address through `core/auth/remember-login.ts`. Passwords and tokens must never be stored in frontend storage; the `HttpOnly access_token` and readable `csrf_token` cookies remain Gateway-owned.
97 
98`/goal` and `/compact` are built-in composer commands, not skill activations. `src/components/workspace/input-box.tsx` intercepts `/goal`, `/goal clear`, and `/goal <condition>` before normal chat submission, calling Gateway `GET/PUT/DELETE /api/threads/{thread_id}/goal`. Setting `/goal <condition>` also submits the condition text as the next user task so the agent starts running immediately; status and clear do not start a run. Goal and compact requests are tied to the current `threadId` with an `AbortController`, so switching threads or unmounting the composer aborts in-flight requests and stale responses cannot update the new thread's composer state. The chat pages render `GoalStatus` above the composer from `AgentThreadState.goal`, with local optimistic state until the next stream `values` update arrives. `/compact` calls `POST /api/threads/{thread_id}/compact` to summarize older active context while leaving the full visible chat history intact; it is skipped on new/empty threads and blocked server-side while a run is in flight. Thread rename uses the same serialized state-write route; the rename dialog stays open and surfaces the server error when an active run returns 409.
 
 
 
 
99 
100The `/` skill list stays reachable after a skill is selected: typing `/` in the editable text beside the chip reopens it, and picking an entry swaps the chip rather than adding a second one, because the wire format carries exactly one leading `/skill`. That list offers skills only while a chip is selected — a builtin command owns the whole composer line, so `/goal` behind a selected skill would submit as chat text instead of running the command. The trigger itself is unchanged: a slash only opens the list at the start of the input (`getLeadingSlashSkillQuery`), pinned by `tests/e2e/chat.spec.ts`.
101 
102Human input requests are a structured message protocol layered on normal chat history. The backend writes request payloads to `ToolMessage.artifact.human_input`, `src/core/messages/human-input.ts` owns the runtime validators/types, and `src/components/workspace/messages/human-input-card.tsx` renders the reusable card. The protocol is versioned on the request side only: v1 covers `free_text` / `choice_with_other`, and v2 adds `form` (typed fields — text/textarea/number/select/multi_select/checkbox/date — with required-field validation in the card). Replies deliberately stay on the v1 response protocol: the form card submits a `response_kind: "text"` reply whose value is the human-readable summary plus one JSON block keyed by stable field names (`buildHumanInputFormSubmissionValue` — the readable part alone is ambiguous because labels/values may contain the separators), so the model can reconstruct the submitted mapping without a structured response kind. The validators reject unknown versions/modes (and field names colliding with JS `Object.prototype` members) so future protocol bumps degrade to the plain-text ToolMessage fallback rather than rendering a broken card. Form values are read through own-property access only (`readHumanInputFormValue`); select fields stay controlled from their empty-string placeholder state through selection; checkbox fields are native `<input type="checkbox">` controls seeded to an explicit `false` (`buildInitialHumanInputFormValues`) so an untouched checkbox submits as "no" while a `required` checkbox keeps must-agree semantics (no HTML `required` attribute — native constraint validation would intercept the custom submit path), and form controls carry label/`htmlFor`, `aria-required` plus a visually-hidden localized "required" marker, and `aria-invalid`/error associations whose error node stays mounted while any field is still invalid. Composer-bypass closure: `deriveHumanInputThreadState` treats a visible plain human message as answering the latest unanswered request opened before it (only the latest — nothing guarantees a single outstanding request across runs, and closing all would silently swallow older decisions; an older request left open simply becomes the active card again). This lets current users bypass a structured form through the normal composer and preserves compatibility with old v1-only frontends that degrade a v2 request to plain text. `MessageList` owns answered/latest/pending state for visible cards, but derives answered responses from raw `thread.messages` because replies are hidden; pending cards clear when the hidden reply appears, when dispatch is dropped, or when a new `thread.error` reports an async stream failure. Page-level card submit callbacks must send a normal human message and put `hide_from_ui: true` plus the response payload in the fourth `sendMessage(..., options)` argument as `options.additionalKwargs`; the third argument remains run context such as `{ agent_name }`. Composer entry points remain enabled while a human-input request is open; a normal visible message intentionally bypasses the card and starts the next run without structured response metadata.
 
 
 
103 
104Tool-calling AI messages can contain user-visible text as well as `tool_calls`. `core/messages/utils.ts` keeps these turns in an `assistant:processing` group, and `components/workspace/messages/message-group.tsx` must render the visible text as a processing step instead of treating the message as only tool metadata. This preserves provider text such as error explanations or "trying another approach" notes during tool-heavy runs.
105While the current turn is still loading, a content-only AI message after the latest visible human input also stays in that processing group until the turn settles: a provider may append tool-call chunks to the same message later, and classifying it as a final assistant bubble too early makes the text jump into the steps panel. `MessageGroup` therefore renders processing text even before the first tool call arrives.
106The same rule applies after an earlier tool call: a later content-only AI message remains visible after the current last tool-call step while streaming, because that message may itself gain another tool call before the turn settles.
107Because the same message is rendered by two different components over its lifetime, reasoning must sit above the answer text in both. `MessageListItem` paints the settled bubble's `<Reasoning>` disclosure above its content, so `MessageGroup` puts the trailing reasoning disclosure above the assistant text that follows it and `convertToSteps` emits a message's reasoning step before its content step — otherwise the two swap places the instant the turn settles (#4576). Assistant text emitted _before_ that reasoning keeps its earlier position; only the answer the reasoning produced moves below it.
108 
109Edit-and-rerun is deliberately latest-turn-only. `core/messages/utils.ts::getLatestEditableTurn()` exposes a human turn only when the transcript is idle and the most recent visible turn ends in a terminal assistant message. `core/threads/hooks.ts::editAndRegenerateMessage()` calls `POST /api/threads/{id}/runs/edit-regenerate/prepare`, submits the returned replacement message/checkpoint/metadata through the same LangGraph stream path as regenerate, optimistically hides the superseded message ids, and clears the optimistic replacement once the persisted replacement arrives.
110 
111`MessageGroup` builds its tool-result and browser-preview lookups once per processing group before converting messages to steps. The lookup preserves the first non-empty result and first screenshot-bearing browser view for each tool-call ID, matching the streamed-message display semantics without repeatedly scanning the full group for every tool call.
 
 
112 
113### Key Patterns
114 
115- **Server Components by default**, `"use client"` only for interactive components
116- **Static root boundary** — `src/app/layout.tsx` must not read cookies or import
117 chat-only KaTeX/Streamdown styles. Auth and workspace layouts own the cookie-derived
118 locale provider; docs derive locale from their route, and blog owns its preference
119 cookie. Public server routes load one dictionary at a time through
120 `core/i18n/translations.ts`; the interactive auth/workspace client provider owns both
121 formatter-bearing dictionaries because functions cannot cross the RSC boundary.
122 Keep public `/` static and keep rich-content CSS on the routes that render it.
123- **Thread hooks** (`useThreadStream`, `useSubmitThread`, `useThreads`) are the primary API interface
124- **Thread routes** — construct Web UI chat paths through `core/threads/utils.ts::pathOfThread()`, which percent-encodes both custom agent names and thread IDs before inserting them into route segments
125- **LangGraph client** is a singleton obtained via `getAPIClient()` in `core/api/`
126- **Run stream options** are sanitized by `core/api/stream-mode.ts`: the Gateway-supported set is `values`, `messages-tuple`, `updates`, `debug`, `tasks`, `checkpoints`, and `custom`; any request containing an unsupported mode throws before HTTP instead of being partially forwarded or silently defaulting to `values`. `streamResumable` is retained by thread hooks only for SDK-side reconnect bookkeeping but stripped before the HTTP request because the Gateway does not accept that request option; actual replay uses the SSE `Last-Event-ID` cursor. Keep this boundary aligned with the backend request schema; `messages` and `events` are not supported and must not be forwarded.
127- **SSE replay gaps** are handled in `core/api/api-client.ts`, which wraps both initial and joined run streams because the upstream SDK ignores unknown event names. An id-less backend `gap` control frame clears stale reconnect metadata, emits an internal `stream_replay_gap` custom event, reloads durable thread values, and rejoins after the server-provided retained tail, with up to five recovery rejoins after the original stream (six total stream calls on an all-gap exhaustion path). The wrapper remains a lazy async iterable because the SDK consumes it with `for await`. `core/threads/hooks.ts` clears optimistic/transient/subtask state, invalidates durable history caches, and shows the localized recovery warning; never let a gap fall through as a normal stream finish or cancel the still-running backend run.
128- **Streaming Markdown rendering** is owned by `core/streamdown`: Streamdown's `animated` / `isAnimating` API handles incremental word animation, while the shared `streamdownRenderingPlugins` config registers the named code-highlighting and Mermaid plugins required by Streamdown 2.5. Keep wrappers and derived configs wired to that shared object; do not reintroduce a rehype plugin that wraps every word, because reparsing a growing block remounts old words and replays their animation.
129- Citation links in message and artifact Markdown must derive their `citation:` label from the full `ReactNode` children tree, since Streamdown may provide element or array children during streaming rather than a plain string.
130- **Environment validation** uses `@t3-oss/env-nextjs` with Zod schemas (`src/env.js`). Skip with `SKIP_ENV_VALIDATION=1`
131- **Subtask step history and runtime metadata** (`core/tasks/`) — the subtask card shows a subagent's full step timeline (#3779): its assistant reasoning turns interleaved with the tools it ran. `Subtask.steps[]` is accumulated live from `task_running` events (appended via `mergeSteps`, not overwritten) and backfilled on expand for historical runs by `fetchSubtaskSteps`, which pages the events endpoint scoped to one task (GET `/runs/{runId}/events?event_types=subagent.step&task_id=…&after_seq=…`) until a short page, so the run-wide limit can't truncate the timeline. `task_started` carries the effective `model_name`; `task_running` carries a cumulative usage snapshot after each completed LLM call. `core/tasks/lifecycle.ts` normalizes these additive events, and `computeNextSubtask` keeps the largest cumulative total so replayed or late SSE frames cannot double-count or roll the folded card backward. Terminal ToolMessage metadata (`subagent_model_name` / `subagent_token_usage`) restores the same values from normal history after reload; no per-card event fetch is needed. `core/tasks/steps.ts` is the pure step model: `messageToStep` (live), `eventsToSteps` (reload), `mergeSteps` (dedup by `message_index`), and `stepsForDisplay` (what the card renders — keeps tool steps + AI steps with text, drops the trailing final-answer AI step when completed since it's shown as `result`). `core/tasks/context.tsx`'s `useUpdateSubtask` applies updates against a `tasksRef` mirroring the latest state (not a closure snapshot), so a late-resolving `fetchSubtaskSteps` backfill merges into current state instead of clobbering SSE steps or sibling subtasks that arrived meanwhile. The owning `run_id` is carried onto history content messages in `buildVisibleHistoryMessages` so the card can resolve the events endpoint.
132 
133### Interaction Ownership
134 
135- `src/app/workspace/chats/[thread_id]/page.tsx` owns composer busy-state wiring.
136- `src/app/workspace/chats/[thread_id]/page.tsx` owns branch-from-turn submission and navigation; sidecar `MessageList` instances do not receive the branch action.
137- `src/app/workspace/chats/[thread_id]/page.tsx` and `src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx` own edit-and-rerun submission wiring because the page must preserve normal/custom-agent run context; `MessageList` only detects the latest editable user turn and renders the inline editor.
138- `src/app/workspace/chats/[thread_id]/page.tsx` gates the Workspace Browser trigger and browser right panel on `/api/features -> browser_control.enabled`; default/failed feature discovery hides the browser control so optional backend installs do not show a dead Live socket.
139- `src/app/workspace/chats/[thread_id]/page.tsx` and `src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx` own active-goal display state for their composer overlays.
140- `src/components/workspace/messages/message-list.tsx` owns human-input card answered/latest/pending gating; entry pages only translate a submitted card response into `sendMessage` calls.
141- `src/components/workspace/browser-view/browser-view-panel.tsx` forwards each physical pointer click as one `click` input; do not also emit `down`/`up` for the same gesture because the remote Playwright click would run twice.
142- `src/components/workspace/browser-view/use-browser-stream.ts` requests binary JPEG
143 frames with `frame_format=binary`; status, URL, tabs, and navigation rejection
144 messages remain JSON. `LatestBrowserFrameBuffer` keeps only the newest pending
145 frame, publishes through `useSyncExternalStore` at most once per animation
146 frame, and owns object-URL revocation. Keep the Gateway's legacy JSON/base64
147 frame path for older clients.
148- `src/core/threads/hooks.ts` owns pre-submit upload state and thread submission.
149- `src/components/workspace/chats/chat-box.tsx` owns the desktop right-panel layout, and **all three** right panels (artifacts, sidecar, browser) share one `ResizablePanelGroup` — do not fork a non-resizable branch per panel kind, which is how the artifacts divider silently lost its drag handle (#4465). Open/close is `collapse()` / `resize()` on the side panel's imperative handle, not conditional rendering, so the width can animate. Three constraints hold that together: the size transition is applied from the group as `[&>[data-panel]]:transition-[flex-grow]` because the sized flex item is the library's own `[data-panel]` element rather than the child `className` lands on; it is applied only while an open/close is in flight, so a drag is not interpolated frame by frame; and during the animation the panel content is held at its final width in `cqw` and clipped, because a reflowing message list re-runs its scroll-to-bottom (pinned by `tests/e2e/sidecar-chat.spec.ts`'s no-animated-scroll test) and a re-wrapping composer changes which responsive labels it shows. Because the panel is `collapsible`, the library can also collapse it to `0%` on its own when a drag crosses `minSize`, without going through the state that owns it. `onResize` records the last positive size while the pointer moves, but the owning `sidecar` / `browserView` / `artifactsOpen` state must only mirror a final `0%` layout from `onLayoutChanged`, after pointer release; closing on the first `0%` resize frame breaks a continuous drag that reaches the edge and then reverses before release.
150 
151## Code Style
152 
153- **Imports**: Enforced ordering (builtin → external → internal → parent → sibling), alphabetized, newlines between groups. Use inline type imports: `import { type Foo }`.
154- **Unused variables**: Prefix with `_`.
155- **Class names**: Use `cn()` from `@/lib/utils` for conditional Tailwind classes.
156- **Path alias**: `@/*` maps to `src/*`.
157- **Components**: `ui/` and `ai-elements/` are generated from registries (Shadcn, MagicUI, React Bits, Vercel AI SDK) — don't manually edit these.
158 
159## Environment
160 
161Backend API URLs are optional; an nginx proxy is used by default:
162 
163```
164NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8001
165NEXT_PUBLIC_LANGGRAPH_BASE_URL=http://localhost:8001/api
166```
167 
168Leave these unset for the standard `make dev` / Docker flow, where nginx serves the public `/api/langgraph/*` prefix and rewrites it to Gateway's native `/api/*` routes.
169 
170To reach a dev server on anything other than localhost — a LAN address, or a proxied hostname — list the host in `DEER_FLOW_DEV_ALLOWED_ORIGINS` (comma-separated; a full URL is reduced to its host). It feeds Next's `allowedDevOrigins`, which gates `/_next/*`, fonts, and HMR. Without it those requests get a 403 and the page renders server-side but never hydrates, so nothing on it — including the login form — responds. Development only; production builds ignore it.
 
171 
172## Resources
173 
174- [LangGraph Documentation](https://langchain-ai.github.io/langgraph/)
175- [LangChain Core Concepts](https://js.langchain.com/docs/concepts)
176- [TanStack Query Documentation](https://tanstack.com/query/latest)
177- [Next.js App Router](https://nextjs.org/docs/app)
178 
179## Contributing
 
 
 
 
180 
181When adding features:
182 
1831. Follow the established `src/` structure
1842. Add TypeScript types and proper error handling
1853. Write unit tests under `tests/unit/` (`pnpm test`) and E2E tests under `tests/e2e/` (`pnpm test:e2e`)
1864. Run `pnpm check` before committing
1875. Update this `AGENTS.md` when architecture, commands, or conventions change
188 
189Route asset budgets are enforced with `pnpm perf:check`. The command measures
190`/login` from a normal production build, then builds in static-demo mode for the
191fixture-backed workspace routes. It starts the production server on temporary local
192ports, measures the unique JavaScript and CSS files referenced by representative
193routes, writes the detailed result to `.next/performance-results.json`, and compares
194totals with `performance-budgets.json`. Fix route ownership or split points when a
195budget fails; do not raise a ceiling without documenting and reviewing the measured
196regression.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197 
@@ −1 +1 @@
1−# Copilot Onboarding Instructions for DeerFlow
1+# AGENTS.md
22  
3−Use this file as the default operating guide for this repository. Follow it first, and only search the codebase when this file is incomplete or incorrect.
3+This file provides guidance to AI coding agents (Claude Code, Codex, and others) when working with the DeerFlow frontend. It is the source of truth; the sibling `CLAUDE.md` imports it via `@AGENTS.md`.
44  
5−## 1) Repository Summary
5+## Project Overview
66  
7−DeerFlow is a full-stack "super agent harness".
7+DeerFlow Frontend is a Next.js 16 web interface for an AI agent system. It communicates with a LangGraph-based backend to provide thread-based AI conversations with streaming responses, artifacts, and a skills/tools system.
88  
9−- Backend: Python 3.12, LangGraph + FastAPI gateway, sandbox/tool system, memory, MCP integration.
10−- Frontend: Next.js 16 + React 19 + TypeScript + pnpm.
11−- Local dev entrypoint: root `Makefile` starts backend + frontend + nginx on `http://localhost:2026`.
12−- Docker dev entrypoint: `make docker-*` (mode-aware provisioner startup from `config.yaml`).
9+**Stack**: Next.js 16, React 19, TypeScript 5.8, Tailwind CSS 4, pnpm 10.26.2. Requires Node.js 22+ and pnpm 10.26.2+.
1310  
14−Current repo footprint is medium-large (backend service, frontend app, docker stack, skills library, docs).
11+### Core dependencies
1512  
16−## 2) Runtime and Toolchain Requirements
13+- **LangGraph SDK** (`@langchain/langgraph-sdk` ^1.5.3) — Agent orchestration and streaming
14+- **LangChain Core** (`@langchain/core` ^1.1.15) — Fundamental AI building blocks
15+- **TanStack Query** (`@tanstack/react-query` ^5.90.17) — Server state management
16+- **UI**: Shadcn UI, MagicUI, React Bits, and Vercel AI SDK elements (generated from registries — see Code Style)
1717  
18−Validated in this repo on macOS:
18+## Commands
1919  
20−- Node.js `>=22` (validated with Node `23.11.0`)
21−- pnpm (repo expects lockfile generated by pnpm 10; validated with pnpm `10.26.2` and `10.15.0`)
22−- Python `>=3.12` (CI uses `3.12`)
23−- `uv` (validated with `0.7.20`)
24−- `nginx` (required for `make dev` unified local endpoint)
20+| Command | Purpose |
21+| ---------------- | ------------------------------------------------- |
22+| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) |
23+| `pnpm build` | Production build |
24+| `pnpm check` | Lint + type check (run before committing) |
25+| `pnpm lint` | ESLint only |
26+| `pnpm lint:fix` | ESLint with auto-fix |
27+| `pnpm format` | Prettier check (`pnpm format:write` to apply) |
28+| `pnpm test` | Run unit tests with Rstest |
29+| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) |
30+| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) |
31+| `pnpm start` | Start production server |
2532  
26−Always run from repo root unless a command explicitly says otherwise.
33+Unit tests live under `tests/unit/` and mirror the `src/` layout (e.g., `tests/unit/core/api/stream-mode.test.ts` tests `src/core/api/stream-mode.ts`). Powered by Rstest; import source modules via the `@/` path alias.
2734  
28−## 3) Build/Test/Lint/Run - Verified Command Sequences
35+Rstest runs them as two projects (`rstest.config.ts`). `*.test.ts` / `*.test.tsx` run in a plain **node** environment — that is nearly the whole suite, and it is the default for anything that is pure logic. `*.dom.test.ts` / `*.dom.test.tsx` run in **happy-dom**, for tests that need a document: hooks driven through `renderHook` from `@testing-library/react`, and components. Keep the split — a DOM environment costs roughly 3x the runtime of the node suite, so tests that do not render should not opt into it. A hook whose behavior only exists under real React (effect ordering, cleanup on unmount, re-render on store change) belongs in a `.dom.test.*` file rather than a node test that mocks `react` itself.
2936  
30−These were executed and validated in this repository.
37+E2E tests live under `tests/e2e/` and use Playwright with Chromium. They mock all backend APIs via `page.route()` network interception and test real page interactions (navigation, chat input, streaming responses). Config: `playwright.config.ts`.
3138  
32−### A. Bootstrap and install
39+## Architecture
3340  
34−1. Check prerequisites:
35− 
36−```bash
37−make check
3841 ```
39− 
40−Observed: passes when required tools are installed.
41− 
42−2. Install dependencies (recommended order: backend then frontend, as implemented by `make install`):
43− 
44−```bash
45−make install
42+Frontend (Next.js) ──▶ LangGraph SDK ──▶ LangGraph Backend (lead_agent)
43+ ├── Sub-Agents
44+ └── Tools & Skills
4645 ```
4746  
48−### B. Backend CI-equivalent validation
47+The frontend is a stateful chat application. Users create **threads** (conversations), send messages, set thread-scoped `/goal` completion conditions, and receive streamed AI responses. The backend orchestrates agents that can produce **artifacts** (files/code), **todos**, and goal state updates.
4948  
50−Run from `backend/`:
49+### Source Layout (`src/`)
5150  
52−```bash
53−make lint
54−make test
55−```
51+- **`app/`** — Next.js App Router. Routes include `/` (landing), `/workspace/chats/[thread_id]` (chat), `/workspace/agents/[agent_name]` and `/workspace/agents/new` (custom agents), `/blog/…`, the `(auth)/{login,setup,auth/callback}` flow, `/[lang]/docs/…`, and `/api/…` route handlers (e.g. `/api/memory`).
52+- **`components/`** — React components:
53+ - `ui/` — Shadcn UI primitives (auto-generated, ESLint-ignored)
54+ - `ai-elements/` — Vercel AI SDK elements (auto-generated, ESLint-ignored)
55+ - `workspace/` — Chat page components (messages, artifacts, settings)
56+ - `landing/` — Landing page sections
57+ - `docs/` — Docs / MDX rendering components
58+- **`core/`** — Business logic, the heart of the app. Domains include `threads/` (creation, streaming, state), `api/` (LangGraph client singleton), `agents/` (custom agents), `auth/` (authentication), `artifacts/`, `channels/` (IM connections), `integrations/` (managed third-party integration status/install clients such as Lark CLI), `i18n/` (en-US, zh-CN), `settings/`, `memory/`, `skills/`, `messages/`, `mcp/`, `models/`, `input-polish/` (pre-send draft rewrite API), `voice-input/` (browser speech-recognition helpers), `suggestions/`, `tasks/`, `todos/`, `tools/`, `workspace-changes/` (run-scoped changed-file summaries and diff fetching), `config/`, `notification/`, `blog/`, plus rendering helpers (`rehype/`, `streamdown/`) and `utils/`.
59+- **`hooks/`** — Shared React hooks
60+- **`lib/`** — Utilities (`cn()` from clsx + tailwind-merge)
61+- **`content/`** — MDX content (blog posts, docs) rendered by the app
62+- **`styles/`** — Global CSS with Tailwind v4 `@import` syntax and CSS variables for theming
63+- **`typings/`** — Ambient TypeScript declarations
64+- Root files: `env.js` (env validation), `mdx-components.ts` (MDX component map)
5665  
57−Validated results:
66+### Data Flow
5867  
59−- `make lint`: pass (`ruff check .`)
60−- `make test`: pass (`277 passed, 15 warnings in ~76.6s`)
68+1. Optional composer helpers such as `core/input-polish` can rewrite the local draft before submission, and `core/voice-input` can transcribe browser microphone input into that same local draft; confirmed user input then flows to thread hooks (`core/threads/hooks.ts`) → LangGraph SDK streaming
69+2. Stream events update thread state (messages, artifacts, todos, goal). The main thread stream uses the LangGraph SDK's `throttle: true` mode so updates received in the same macrotask coalesce before React is notified; do not replace it with a numeric delay without validating the SDK's trailing-debounce behavior on a continuous stream.
70+ File-tool artifact auto-open work must run in an effect with timer cleanup; never schedule timers while rendering streamed `write_file` or `str_replace` updates.
71+ `ThreadState.artifacts` remains the authoritative artifact list. The artifacts provider persists only thread-scoped panel UI state (`open`, selected path, and a refresh bootstrap cache) in session storage; an initial empty stream value must not overwrite that restored state before history finishes loading.
72+ Formal artifact content is refreshed once when the run finishes; transient `write-file:` previews remain message-driven.
73+ The detail view exposes explicit editing only for an already-opened formal UTF-8 text artifact under `/mnt/user-data/outputs`. Drafts stay in provider memory until Save so switching right-side panels cannot discard them, render in Markdown/HTML preview, and are protected from remote refreshes by the loaded SHA-256 revision. Saving is disabled during an active run; a changed revision preserves the draft and surfaces a conflict instead of overwriting agent output.
74+ Regular artifact text loads request at most the first 1 MiB through an HTTP
75+ byte range. A truncated preview must stay lightweight and expose an explicit
76+ full-file action; do not mount CodeMirror for that artifact until the user
77+ requests and receives the complete content. The Gateway retains range
78+ ownership and returns 206/416 through `FileResponse`.
79+3. `useThreadHistory` loads persisted conversation pages from `GET /api/threads/{id}/messages/page`, preserving the backend's thread-global event `seq`; rendering overlays checkpoint/live copies at their matching canonical identities (a summarized checkpoint may contain a protected early input plus a recent tail). Context-compaction rescue diffs every retained visible identity rather than slicing at the first anchor, and keeps a run-scoped ledger of committed visible messages so replacement updates and repeated rolling checkpoint windows cannot erase an already displayed step. The resolver suppresses checkpoint/transient prefixes whose canonical position is still behind an unloaded cursor page instead of collapsing that unknown gap before a recent anchor, then adds optimistic messages without timestamp re-sorting. History invalidation preserves already-loaded pages so their established ordering positions are not discarded. Dynamic context re-keys the submitted user message from `X` to `X__user`; UI identity matching normalizes that reserved suffix only for human messages so the submitted frame and checkpoint replacement remain one visible turn. A locally submitted turn also records its pre-submit identity baseline: if `messages-tuple` publishes new AI/tool steps before `values` publishes that turn's human message, render ordering moves only those non-baseline visible steps behind the new human while leaving history, hidden controls, and reconnected runs untouched. Keep that local order anchor through finish, stop, and stream error because the SDK's settled frame can retain transient event order; replace it on the next local submit and clear it on thread switch or replay-gap recovery.
80+4. Stop actions call the LangGraph SDK stream stop path; `core/threads/hooks.ts` invalidates current-thread, thread-history, token-usage, and sidebar/search caches immediately and schedules one follow-up refetch because SDK stop may finish via abort + fire-and-forget cancel before backend title finalization commits
81+5. TanStack Query manages server state; localStorage stores user settings. The
82+ Settings > Tools MCP switch calls the targeted `PATCH /api/mcp/config`
83+ mutation, disables switches until that mutation's success refetch completes,
84+ displays the backend error `detail` through a toast, and invalidates
85+ `["mcpConfig"]` only after success.
86+6. Components subscribe to thread state and render updates
6187  
62−CI parity:
88+The chat header's context-window control is intentionally persistent: while `context_usage` is unavailable, `ContextUsageBadge` renders a gauge placeholder rather than unmounting; once data arrives, the same position shows the percentage. `useThreadTokenUsage` retains placeholder data only when the response `thread_id` still matches the active route, so same-thread refetches do not flicker and cross-thread navigation never displays the previous chat's usage.
6389  
64−- `.github/workflows/backend-unit-tests.yml` runs on pull requests.
65−- CI executes `uv sync --group dev`, then `make lint`, then `make test` in `backend/`.
90+Run duration is run-scoped UI metadata even though the compatibility field `additional_kwargs.turn_duration` is repeated on historical AI messages. `core/messages/run-duration.ts` folds those copies into one display anchored after the run's last visible message group. `MessageList` owns the temporary client-side duration for a just-completed live turn until authoritative history arrives. The duration is total run wall-clock time, not per-message reasoning time; reasoning disclosure and run activity/duration are rendered separately.
6691  
67−### C. Frontend validation
92+The workspace-change card follows the same rule: it is resolved from `(threadId, runId)` alone, so every AI message of a run would render an identical copy. A run ends in more than one terminal assistant bubble whenever the model emits answer text that never gains a tool call, so `core/messages/workspace-change-anchor.ts` picks the run's last assistant bubble and `MessageListItem` renders the badge only for that anchor (#4555). Any future run-scoped display belongs in the same place — do not hang one off every message. The two anchor helpers deliberately differ in which group types they accept as a run's last position, because an anchor is only useful where the display is actually rendered: run duration is emitted by `MessageList` around every group, so it accepts any type, while the workspace-change card comes from `MessageListItem` and so restricts to `assistant`. Keep a new helper's candidate set matched to its own render site rather than unifying them.
6893  
69−Run from `frontend/`.
94+Composer drafts are tab-scoped browser state. `core/threads/composer-draft.ts` stores only text plus the selected slash-skill name in `sessionStorage`, keyed by user, agent, and logical conversation scope. New-chat pages pass the stable scope `"new"` because their runtime `threadId` is a fresh UUID on every reload; established conversations use their real thread ID. `InputBox` waits for enabled skills before restoring a skill chip, degrades a missing/disabled skill back to editable slash text, and clears the stored draft through `SendMessageOptions.onSent` only after the send passes the in-flight guard. Attachments, sidecar quotes, voice state, and polish undo state are not persisted.
7095  
71−Recommended reliable sequence:
96+Auth UI note: the login page's "keep me signed in" option submits only `remember_me` to the Gateway and may persist only the email address through `core/auth/remember-login.ts`. Passwords and tokens must never be stored in frontend storage; the `HttpOnly access_token` and readable `csrf_token` cookies remain Gateway-owned.
7297  
73−```bash
74−pnpm lint
75−pnpm typecheck
76−BETTER_AUTH_SECRET=local-dev-secret pnpm build
77−```
98+`/goal` and `/compact` are built-in composer commands, not skill activations. `src/components/workspace/input-box.tsx` intercepts `/goal`, `/goal clear`, and `/goal <condition>` before normal chat submission, calling Gateway `GET/PUT/DELETE /api/threads/{thread_id}/goal`. Setting `/goal <condition>` also submits the condition text as the next user task so the agent starts running immediately; status and clear do not start a run. Goal and compact requests are tied to the current `threadId` with an `AbortController`, so switching threads or unmounting the composer aborts in-flight requests and stale responses cannot update the new thread's composer state. The chat pages render `GoalStatus` above the composer from `AgentThreadState.goal`, with local optimistic state until the next stream `values` update arrives. `/compact` calls `POST /api/threads/{thread_id}/compact` to summarize older active context while leaving the full visible chat history intact; it is skipped on new/empty threads and blocked server-side while a run is in flight. Thread rename uses the same serialized state-write route; the rename dialog stays open and surfaces the server error when an active run returns 409.
7899  
79−Observed failure modes and workarounds:
100+The `/` skill list stays reachable after a skill is selected: typing `/` in the editable text beside the chip reopens it, and picking an entry swaps the chip rather than adding a second one, because the wire format carries exactly one leading `/skill`. That list offers skills only while a chip is selected — a builtin command owns the whole composer line, so `/goal` behind a selected skill would submit as chat text instead of running the command. The trigger itself is unchanged: a slash only opens the list at the start of the input (`getLeadingSlashSkillQuery`), pinned by `tests/e2e/chat.spec.ts`.
80101  
81−- `pnpm build` fails without `BETTER_AUTH_SECRET` in production-mode env validation.
82−- Workaround: set `BETTER_AUTH_SECRET` (best) or set `SKIP_ENV_VALIDATION=1`.
83−- Even with `SKIP_ENV_VALIDATION=1`, Better Auth can still warn/error in logs about default secret; prefer setting a real non-default secret.
84−- `pnpm check` currently fails (`next lint` invocation is incompatible here and resolves to an invalid directory). Do not rely on `pnpm check`; run `pnpm lint` and `pnpm typecheck` explicitly.
102+Human input requests are a structured message protocol layered on normal chat history. The backend writes request payloads to `ToolMessage.artifact.human_input`, `src/core/messages/human-input.ts` owns the runtime validators/types, and `src/components/workspace/messages/human-input-card.tsx` renders the reusable card. The protocol is versioned on the request side only: v1 covers `free_text` / `choice_with_other`, and v2 adds `form` (typed fields — text/textarea/number/select/multi_select/checkbox/date — with required-field validation in the card). Replies deliberately stay on the v1 response protocol: the form card submits a `response_kind: "text"` reply whose value is the human-readable summary plus one JSON block keyed by stable field names (`buildHumanInputFormSubmissionValue` — the readable part alone is ambiguous because labels/values may contain the separators), so the model can reconstruct the submitted mapping without a structured response kind. The validators reject unknown versions/modes (and field names colliding with JS `Object.prototype` members) so future protocol bumps degrade to the plain-text ToolMessage fallback rather than rendering a broken card. Form values are read through own-property access only (`readHumanInputFormValue`); select fields stay controlled from their empty-string placeholder state through selection; checkbox fields are native `<input type="checkbox">` controls seeded to an explicit `false` (`buildInitialHumanInputFormValues`) so an untouched checkbox submits as "no" while a `required` checkbox keeps must-agree semantics (no HTML `required` attribute — native constraint validation would intercept the custom submit path), and form controls carry label/`htmlFor`, `aria-required` plus a visually-hidden localized "required" marker, and `aria-invalid`/error associations whose error node stays mounted while any field is still invalid. Composer-bypass closure: `deriveHumanInputThreadState` treats a visible plain human message as answering the latest unanswered request opened before it (only the latest — nothing guarantees a single outstanding request across runs, and closing all would silently swallow older decisions; an older request left open simply becomes the active card again). This lets current users bypass a structured form through the normal composer and preserves compatibility with old v1-only frontends that degrade a v2 request to plain text. `MessageList` owns answered/latest/pending state for visible cards, but derives answered responses from raw `thread.messages` because replies are hidden; pending cards clear when the hidden reply appears, when dispatch is dropped, or when a new `thread.error` reports an async stream failure. Page-level card submit callbacks must send a normal human message and put `hide_from_ui: true` plus the response payload in the fourth `sendMessage(..., options)` argument as `options.additionalKwargs`; the third argument remains run context such as `{ agent_name }`. Composer entry points remain enabled while a human-input request is open; a normal visible message intentionally bypasses the card and starts the next run without structured response metadata.
85103  
86−### D. Run locally (all services)
104+Tool-calling AI messages can contain user-visible text as well as `tool_calls`. `core/messages/utils.ts` keeps these turns in an `assistant:processing` group, and `components/workspace/messages/message-group.tsx` must render the visible text as a processing step instead of treating the message as only tool metadata. This preserves provider text such as error explanations or "trying another approach" notes during tool-heavy runs.
105+While the current turn is still loading, a content-only AI message after the latest visible human input also stays in that processing group until the turn settles: a provider may append tool-call chunks to the same message later, and classifying it as a final assistant bubble too early makes the text jump into the steps panel. `MessageGroup` therefore renders processing text even before the first tool call arrives.
106+The same rule applies after an earlier tool call: a later content-only AI message remains visible after the current last tool-call step while streaming, because that message may itself gain another tool call before the turn settles.
107+Because the same message is rendered by two different components over its lifetime, reasoning must sit above the answer text in both. `MessageListItem` paints the settled bubble's `<Reasoning>` disclosure above its content, so `MessageGroup` puts the trailing reasoning disclosure above the assistant text that follows it and `convertToSteps` emits a message's reasoning step before its content step — otherwise the two swap places the instant the turn settles (#4576). Assistant text emitted _before_ that reasoning keeps its earlier position; only the answer the reasoning produced moves below it.
87108  
88−From root:
109+Edit-and-rerun is deliberately latest-turn-only. `core/messages/utils.ts::getLatestEditableTurn()` exposes a human turn only when the transcript is idle and the most recent visible turn ends in a terminal assistant message. `core/threads/hooks.ts::editAndRegenerateMessage()` calls `POST /api/threads/{id}/runs/edit-regenerate/prepare`, submits the returned replacement message/checkpoint/metadata through the same LangGraph stream path as regenerate, optimistically hides the superseded message ids, and clears the optimistic replacement once the persisted replacement arrives.
89110  
90−```bash
91−make dev
92−```
111+`MessageGroup` builds its tool-result and browser-preview lookups once per processing group before converting messages to steps. The lookup preserves the first non-empty result and first screenshot-bearing browser view for each tool-call ID, matching the streamed-message display semantics without repeatedly scanning the full group for every tool call.
93112  
94−Behavior:
113+### Key Patterns
95114  
96−- Stops existing local services first.
97−- Starts Gateway (`8001`, with the embedded LangGraph-compatible runtime), Frontend (`3000`), nginx (`2026`). There is no standalone LangGraph service.
98−- Unified app endpoint: `http://localhost:2026`.
99−- Logs: `logs/gateway.log`, `logs/frontend.log`, `logs/nginx.log`.
115+- **Server Components by default**, `"use client"` only for interactive components
116+- **Static root boundary** — `src/app/layout.tsx` must not read cookies or import
117+ chat-only KaTeX/Streamdown styles. Auth and workspace layouts own the cookie-derived
118+ locale provider; docs derive locale from their route, and blog owns its preference
119+ cookie. Public server routes load one dictionary at a time through
120+ `core/i18n/translations.ts`; the interactive auth/workspace client provider owns both
121+ formatter-bearing dictionaries because functions cannot cross the RSC boundary.
122+ Keep public `/` static and keep rich-content CSS on the routes that render it.
123+- **Thread hooks** (`useThreadStream`, `useSubmitThread`, `useThreads`) are the primary API interface
124+- **Thread routes** — construct Web UI chat paths through `core/threads/utils.ts::pathOfThread()`, which percent-encodes both custom agent names and thread IDs before inserting them into route segments
125+- **LangGraph client** is a singleton obtained via `getAPIClient()` in `core/api/`
126+- **Run stream options** are sanitized by `core/api/stream-mode.ts`: the Gateway-supported set is `values`, `messages-tuple`, `updates`, `debug`, `tasks`, `checkpoints`, and `custom`; any request containing an unsupported mode throws before HTTP instead of being partially forwarded or silently defaulting to `values`. `streamResumable` is retained by thread hooks only for SDK-side reconnect bookkeeping but stripped before the HTTP request because the Gateway does not accept that request option; actual replay uses the SSE `Last-Event-ID` cursor. Keep this boundary aligned with the backend request schema; `messages` and `events` are not supported and must not be forwarded.
127+- **SSE replay gaps** are handled in `core/api/api-client.ts`, which wraps both initial and joined run streams because the upstream SDK ignores unknown event names. An id-less backend `gap` control frame clears stale reconnect metadata, emits an internal `stream_replay_gap` custom event, reloads durable thread values, and rejoins after the server-provided retained tail, with up to five recovery rejoins after the original stream (six total stream calls on an all-gap exhaustion path). The wrapper remains a lazy async iterable because the SDK consumes it with `for await`. `core/threads/hooks.ts` clears optimistic/transient/subtask state, invalidates durable history caches, and shows the localized recovery warning; never let a gap fall through as a normal stream finish or cancel the still-running backend run.
128+- **Streaming Markdown rendering** is owned by `core/streamdown`: Streamdown's `animated` / `isAnimating` API handles incremental word animation, while the shared `streamdownRenderingPlugins` config registers the named code-highlighting and Mermaid plugins required by Streamdown 2.5. Keep wrappers and derived configs wired to that shared object; do not reintroduce a rehype plugin that wraps every word, because reparsing a growing block remounts old words and replays their animation.
129+- Citation links in message and artifact Markdown must derive their `citation:` label from the full `ReactNode` children tree, since Streamdown may provide element or array children during streaming rather than a plain string.
130+- **Environment validation** uses `@t3-oss/env-nextjs` with Zod schemas (`src/env.js`). Skip with `SKIP_ENV_VALIDATION=1`
131+- **Subtask step history and runtime metadata** (`core/tasks/`) — the subtask card shows a subagent's full step timeline (#3779): its assistant reasoning turns interleaved with the tools it ran. `Subtask.steps[]` is accumulated live from `task_running` events (appended via `mergeSteps`, not overwritten) and backfilled on expand for historical runs by `fetchSubtaskSteps`, which pages the events endpoint scoped to one task (GET `/runs/{runId}/events?event_types=subagent.step&task_id=…&after_seq=…`) until a short page, so the run-wide limit can't truncate the timeline. `task_started` carries the effective `model_name`; `task_running` carries a cumulative usage snapshot after each completed LLM call. `core/tasks/lifecycle.ts` normalizes these additive events, and `computeNextSubtask` keeps the largest cumulative total so replayed or late SSE frames cannot double-count or roll the folded card backward. Terminal ToolMessage metadata (`subagent_model_name` / `subagent_token_usage`) restores the same values from normal history after reload; no per-card event fetch is needed. `core/tasks/steps.ts` is the pure step model: `messageToStep` (live), `eventsToSteps` (reload), `mergeSteps` (dedup by `message_index`), and `stepsForDisplay` (what the card renders — keeps tool steps + AI steps with text, drops the trailing final-answer AI step when completed since it's shown as `result`). `core/tasks/context.tsx`'s `useUpdateSubtask` applies updates against a `tasksRef` mirroring the latest state (not a closure snapshot), so a late-resolving `fetchSubtaskSteps` backfill merges into current state instead of clobbering SSE steps or sibling subtasks that arrived meanwhile. The owning `run_id` is carried onto history content messages in `buildVisibleHistoryMessages` so the card can resolve the events endpoint.
100132  
101−Stop services:
133+### Interaction Ownership
102134  
103−```bash
104−make stop
105−```
135+- `src/app/workspace/chats/[thread_id]/page.tsx` owns composer busy-state wiring.
136+- `src/app/workspace/chats/[thread_id]/page.tsx` owns branch-from-turn submission and navigation; sidecar `MessageList` instances do not receive the branch action.
137+- `src/app/workspace/chats/[thread_id]/page.tsx` and `src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx` own edit-and-rerun submission wiring because the page must preserve normal/custom-agent run context; `MessageList` only detects the latest editable user turn and renders the inline editor.
138+- `src/app/workspace/chats/[thread_id]/page.tsx` gates the Workspace Browser trigger and browser right panel on `/api/features -> browser_control.enabled`; default/failed feature discovery hides the browser control so optional backend installs do not show a dead Live socket.
139+- `src/app/workspace/chats/[thread_id]/page.tsx` and `src/app/workspace/agents/[agent_name]/chats/[thread_id]/page.tsx` own active-goal display state for their composer overlays.
140+- `src/components/workspace/messages/message-list.tsx` owns human-input card answered/latest/pending gating; entry pages only translate a submitted card response into `sendMessage` calls.
141+- `src/components/workspace/browser-view/browser-view-panel.tsx` forwards each physical pointer click as one `click` input; do not also emit `down`/`up` for the same gesture because the remote Playwright click would run twice.
142+- `src/components/workspace/browser-view/use-browser-stream.ts` requests binary JPEG
143+ frames with `frame_format=binary`; status, URL, tabs, and navigation rejection
144+ messages remain JSON. `LatestBrowserFrameBuffer` keeps only the newest pending
145+ frame, publishes through `useSyncExternalStore` at most once per animation
146+ frame, and owns object-URL revocation. Keep the Gateway's legacy JSON/base64
147+ frame path for older clients.
148+- `src/core/threads/hooks.ts` owns pre-submit upload state and thread submission.
149+- `src/components/workspace/chats/chat-box.tsx` owns the desktop right-panel layout, and **all three** right panels (artifacts, sidecar, browser) share one `ResizablePanelGroup` — do not fork a non-resizable branch per panel kind, which is how the artifacts divider silently lost its drag handle (#4465). Open/close is `collapse()` / `resize()` on the side panel's imperative handle, not conditional rendering, so the width can animate. Three constraints hold that together: the size transition is applied from the group as `[&>[data-panel]]:transition-[flex-grow]` because the sized flex item is the library's own `[data-panel]` element rather than the child `className` lands on; it is applied only while an open/close is in flight, so a drag is not interpolated frame by frame; and during the animation the panel content is held at its final width in `cqw` and clipped, because a reflowing message list re-runs its scroll-to-bottom (pinned by `tests/e2e/sidecar-chat.spec.ts`'s no-animated-scroll test) and a re-wrapping composer changes which responsive labels it shows. Because the panel is `collapsible`, the library can also collapse it to `0%` on its own when a drag crosses `minSize`, without going through the state that owns it. `onResize` records the last positive size while the pointer moves, but the owning `sidecar` / `browserView` / `artifactsOpen` state must only mirror a final `0%` layout from `onLayoutChanged`, after pointer release; closing on the first `0%` resize frame breaks a continuous drag that reaches the edge and then reverses before release.
106150  
107−If tool sessions/timeouts interrupt `make dev`, run `make stop` again to ensure cleanup.
151+## Code Style
108152  
109−### E. Config bootstrap
153+- **Imports**: Enforced ordering (builtin → external → internal → parent → sibling), alphabetized, newlines between groups. Use inline type imports: `import { type Foo }`.
154+- **Unused variables**: Prefix with `_`.
155+- **Class names**: Use `cn()` from `@/lib/utils` for conditional Tailwind classes.
156+- **Path alias**: `@/*` maps to `src/*`.
157+- **Components**: `ui/` and `ai-elements/` are generated from registries (Shadcn, MagicUI, React Bits, Vercel AI SDK) — don't manually edit these.
110158  
111−From root:
159+## Environment
112160  
113−```bash
114−make config
161+Backend API URLs are optional; an nginx proxy is used by default:
162+ 
115163 ```
164+NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8001
165+NEXT_PUBLIC_LANGGRAPH_BASE_URL=http://localhost:8001/api
166+```
116167  
117−Important behavior:
168+Leave these unset for the standard `make dev` / Docker flow, where nginx serves the public `/api/langgraph/*` prefix and rewrites it to Gateway's native `/api/*` routes.
118169  
119−- This intentionally aborts if `config.yaml` (or `config.yml`/`configure.yml`) already exists.
120−- Use `make config` only for first-time setup in a clean clone.
170+To reach a dev server on anything other than localhost — a LAN address, or a proxied hostname — list the host in `DEER_FLOW_DEV_ALLOWED_ORIGINS` (comma-separated; a full URL is reduced to its host). It feeds Next's `allowedDevOrigins`, which gates `/_next/*`, fonts, and HMR. Without it those requests get a 403 and the page renders server-side but never hydrates, so nothing on it — including the login form — responds. Development only; production builds ignore it.
121171  
122−## 4) Command Order That Minimizes Failures
172+## Resources
123173  
124−Use this exact order for local code changes:
174+- [LangGraph Documentation](https://langchain-ai.github.io/langgraph/)
175+- [LangChain Core Concepts](https://js.langchain.com/docs/concepts)
176+- [TanStack Query Documentation](https://tanstack.com/query/latest)
177+- [Next.js App Router](https://nextjs.org/docs/app)
125178  
126−1. `make check`
127−2. `make install` (if frontend fails with proxy errors, rerun frontend install with proxy vars unset)
128−3. Backend checks: `cd backend && make lint && make test`
129−4. Frontend checks: `cd frontend && pnpm lint && pnpm typecheck`
130−5. Frontend build (if UI changes or release-sensitive changes): `BETTER_AUTH_SECRET=... pnpm build`
179+## Contributing
131180  
132−Always run backend lint/tests before opening PRs because that is what CI enforces.
181+When adding features:
133182  
134−## 5) Project Layout and Architecture (High-Value Paths)
183+1. Follow the established `src/` structure
184+2. Add TypeScript types and proper error handling
185+3. Write unit tests under `tests/unit/` (`pnpm test`) and E2E tests under `tests/e2e/` (`pnpm test:e2e`)
186+4. Run `pnpm check` before committing
187+5. Update this `AGENTS.md` when architecture, commands, or conventions change
135188  
136−Root-level orchestration and config:
137− 
138−- `Makefile` - main local/dev/docker command entrypoints
139−- `config.example.yaml` - primary app config template
140−- `config.yaml` - local active config (gitignored)
141−- `docker/docker-compose-dev.yaml` - Docker dev topology
142−- `.github/workflows/backend-unit-tests.yml` - PR validation workflow
143− 
144−Backend core:
145− 
146−- `backend/packages/harness/deerflow/agents/` - lead agent, middleware chain, memory
147−- `backend/app/gateway/` - FastAPI gateway API
148−- `backend/packages/harness/deerflow/sandbox/` - sandbox provider + tool wrappers
149−- `backend/packages/harness/deerflow/subagents/` - subagent registry/execution
150−- `backend/packages/harness/deerflow/mcp/` - MCP integration
151−- `backend/langgraph.json` - graph entrypoint (`deerflow.agents:make_lead_agent`)
152−- `backend/pyproject.toml` - Python deps and `requires-python`
153−- `backend/ruff.toml` - lint/format policy
154−- `backend/tests/` - backend unit and integration-like tests
155− 
156−Frontend core:
157− 
158−- `frontend/src/app/` - Next.js routes/pages
159−- `frontend/src/components/` - UI components
160−- `frontend/src/core/` - app logic (threads, tools, API, models)
161−- `frontend/src/env.js` - env schema/validation (critical for build behavior)
162−- `frontend/package.json` - scripts/deps
163−- `frontend/eslint.config.js` - lint rules
164−- `frontend/tsconfig.json` - TS config
165− 
166−Skills and assets:
167− 
168−- `skills/public/` - built-in skill packs loaded by agent runtime
169− 
170−## 6) Pre-Checkin / Validation Expectations
171− 
172−Before submitting changes, run at minimum:
173− 
174−- Backend: `cd backend && make lint && make test`
175−- Frontend (if touched): `cd frontend && pnpm lint && pnpm typecheck`
176−- Frontend build when changing env/auth/routing/build-sensitive files: `BETTER_AUTH_SECRET=... pnpm build`
177− 
178−If touching orchestration/config (`Makefile`, `docker/*`, `config*.yaml`), also run `make dev` and verify the four services start.
179− 
180−## 7) Non-Obvious Dependencies and Gotchas
181− 
182−- Proxy env vars can silently break frontend network operations (`pnpm install`/registry access).
183−- `BETTER_AUTH_SECRET` is effectively required for reliable frontend production build validation.
184−- Next.js may warn about multiple lockfiles and workspace root inference; this is currently a warning, not a build blocker.
185−- `make config` is non-idempotent by design when config already exists.
186−- `make dev` includes process cleanup and can emit shutdown logs/noise if interrupted; this is expected.
187− 
188−## 8) Root Inventory (quick reference)
189− 
190−Important root entries:
191− 
192−- `.github/`
193−- `backend/`
194−- `frontend/`
195−- `docker/`
196−- `skills/`
197−- `scripts/`
198−- `docs/`
199−- `README.md`
200−- `CONTRIBUTING.md`
201−- `Makefile`
202−- `config.example.yaml`
203−- `extensions_config.example.json`
204− 
205−## 9) Instruction Priority
206− 
207−Trust this onboarding guide first.
208− 
209−Only do broad repo searches (`grep/find/code search`) when:
210− 
211−- you need file-level implementation details not listed here,
212−- a command here fails and you need updated replacement behavior,
213−- or CI/workflow definitions have changed since this file was written.
189+Route asset budgets are enforced with `pnpm perf:check`. The command measures
190+`/login` from a normal production build, then builds in static-demo mode for the
191+fixture-backed workspace routes. It starts the production server on temporary local
192+ports, measures the unique JavaScript and CSS files referenced by representative
193+routes, writes the detailed result to `.next/performance-results.json`, and compares
194+totals with `performance-budgets.json`. Fix route ownership or split points when a
195+budget fails; do not raise a ceiling without documenting and reviewing the measured
196+regression.
214197  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack