mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-13 16:28:38 +00:00
* fix(runtime): persist original human input outside model sanitization * refactor(history): load thread messages by global event sequence * fix(frontend): make summarization rescue a transient history bridge * fix(frontend): old message not append tail 1. add identity anchor 2. add bridgeOrder * fix(frontend): lint error fix * fix: address review feedback and harden pagination coverage - defer transient history ref writes until after render commit - cover large middleware-only history scans - verify infinite-query refetch recalculates page cursors - document AI event types and anchor-weaving differences * fix: harden message pagination and enrichment - append unmatched live tails after canonical history - warn and stop when pagination has_more lacks a cursor - deep-copy restored UI messages to isolate model-facing content - log invalid event sequence and non-advancing cursor errors - pass user_id explicitly through event-store history queries - cover middleware-only AI runs across memory, JSONL, and DB stores * fix: address pagination review feedback * fix(frontend): checkpoint has unknow redener content, optimize the anchor policy * fix(frontend): unit test issue missed previously, remove the TanStack cache trimming * fix(gateway): harden message history queries and provenance - reject externally forged original_user_content metadata - validate provenance metadata in upload and sanitization middleware - make run lookups fail closed by default - batch feedback queries by run ID - align memory message filtering with persistent stores
130 lines
13 KiB
Markdown
130 lines
13 KiB
Markdown
# AGENTS.md
|
|
|
|
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`.
|
|
|
|
## Project Overview
|
|
|
|
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.
|
|
|
|
**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+.
|
|
|
|
### Core dependencies
|
|
|
|
- **LangGraph SDK** (`@langchain/langgraph-sdk` ^1.5.3) — Agent orchestration and streaming
|
|
- **LangChain Core** (`@langchain/core` ^1.1.15) — Fundamental AI building blocks
|
|
- **TanStack Query** (`@tanstack/react-query` ^5.90.17) — Server state management
|
|
- **UI**: Shadcn UI, MagicUI, React Bits, and Vercel AI SDK elements (generated from registries — see Code Style)
|
|
|
|
## Commands
|
|
|
|
| Command | Purpose |
|
|
| ---------------- | ------------------------------------------------- |
|
|
| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) |
|
|
| `pnpm build` | Production build |
|
|
| `pnpm check` | Lint + type check (run before committing) |
|
|
| `pnpm lint` | ESLint only |
|
|
| `pnpm lint:fix` | ESLint with auto-fix |
|
|
| `pnpm format` | Prettier check (`pnpm format:write` to apply) |
|
|
| `pnpm test` | Run unit tests with Rstest |
|
|
| `pnpm test:e2e` | Run E2E tests with Playwright (Chromium) |
|
|
| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) |
|
|
| `pnpm start` | Start production server |
|
|
|
|
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.
|
|
|
|
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`.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Frontend (Next.js) ──▶ LangGraph SDK ──▶ LangGraph Backend (lead_agent)
|
|
├── Sub-Agents
|
|
└── Tools & Skills
|
|
```
|
|
|
|
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.
|
|
|
|
### Source Layout (`src/`)
|
|
|
|
- **`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`).
|
|
- **`components/`** — React components:
|
|
- `ui/` — Shadcn UI primitives (auto-generated, ESLint-ignored)
|
|
- `ai-elements/` — Vercel AI SDK elements (auto-generated, ESLint-ignored)
|
|
- `workspace/` — Chat page components (messages, artifacts, settings)
|
|
- `landing/` — Landing page sections
|
|
- `docs/` — Docs / MDX rendering components
|
|
- **`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), `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/`.
|
|
- **`hooks/`** — Shared React hooks
|
|
- **`lib/`** — Utilities (`cn()` from clsx + tailwind-merge)
|
|
- **`content/`** — MDX content (blog posts, docs) rendered by the app
|
|
- **`styles/`** — Global CSS with Tailwind v4 `@import` syntax and CSS variables for theming
|
|
- **`typings/`** — Ambient TypeScript declarations
|
|
- Root files: `env.js` (env validation), `mdx-components.ts` (MDX component map)
|
|
|
|
### Data Flow
|
|
|
|
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
|
|
2. Stream events update thread state (messages, artifacts, todos, goal)
|
|
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), 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.
|
|
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
|
|
5. TanStack Query manages server state; localStorage stores user settings
|
|
6. Components subscribe to thread state and render updates
|
|
|
|
`/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.
|
|
|
|
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. `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 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 should disable normal bottom input while `hasOpenHumanInputRequest(...)` is true so users answer through the card and preserve response metadata.
|
|
|
|
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.
|
|
|
|
### Key Patterns
|
|
|
|
- **Server Components by default**, `"use client"` only for interactive components
|
|
- **Thread hooks** (`useThreadStream`, `useSubmitThread`, `useThreads`) are the primary API interface
|
|
- **LangGraph client** is a singleton obtained via `getAPIClient()` in `core/api/`
|
|
- **Environment validation** uses `@t3-oss/env-nextjs` with Zod schemas (`src/env.js`). Skip with `SKIP_ENV_VALIDATION=1`
|
|
- **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.
|
|
|
|
### Interaction Ownership
|
|
|
|
- `src/app/workspace/chats/[thread_id]/page.tsx` owns composer busy-state wiring.
|
|
- `src/app/workspace/chats/[thread_id]/page.tsx` owns branch-from-turn submission and navigation; sidecar `MessageList` instances do not receive the branch action.
|
|
- `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.
|
|
- `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.
|
|
- `src/core/threads/hooks.ts` owns pre-submit upload state and thread submission.
|
|
|
|
## Code Style
|
|
|
|
- **Imports**: Enforced ordering (builtin → external → internal → parent → sibling), alphabetized, newlines between groups. Use inline type imports: `import { type Foo }`.
|
|
- **Unused variables**: Prefix with `_`.
|
|
- **Class names**: Use `cn()` from `@/lib/utils` for conditional Tailwind classes.
|
|
- **Path alias**: `@/*` maps to `src/*`.
|
|
- **Components**: `ui/` and `ai-elements/` are generated from registries (Shadcn, MagicUI, React Bits, Vercel AI SDK) — don't manually edit these.
|
|
|
|
## Environment
|
|
|
|
Backend API URLs are optional; an nginx proxy is used by default:
|
|
|
|
```
|
|
NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8001
|
|
NEXT_PUBLIC_LANGGRAPH_BASE_URL=http://localhost:8001/api
|
|
```
|
|
|
|
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.
|
|
|
|
## Resources
|
|
|
|
- [LangGraph Documentation](https://langchain-ai.github.io/langgraph/)
|
|
- [LangChain Core Concepts](https://js.langchain.com/docs/concepts)
|
|
- [TanStack Query Documentation](https://tanstack.com/query/latest)
|
|
- [Next.js App Router](https://nextjs.org/docs/app)
|
|
|
|
## Contributing
|
|
|
|
When adding features:
|
|
|
|
1. Follow the established `src/` structure
|
|
2. Add TypeScript types and proper error handling
|
|
3. Write unit tests under `tests/unit/` (`pnpm test`) and E2E tests under `tests/e2e/` (`pnpm test:e2e`)
|
|
4. Run `pnpm check` before committing
|
|
5. Update this `AGENTS.md` when architecture, commands, or conventions change
|