Developer Guide
This guide explains how to run, change, test and debug the current source. For submitting work, start with Contributing. For operating an installed instance, use Docker, host/PM2 and operations instead. A development command is not a production deployment procedure.
AgentifUI provides an enterprise-oriented UI for external Agent services. It is not an Agent builder or a LangChain execution host. Current supported backends are Dify and compatible LangChain services, with explicitly bounded shared AG-UI interaction. Future backends or protocol features are not enabled by adding a name to a dropdown.
1. Find The Relevant Code
| Area | Main paths | Responsibility |
|---|---|---|
| Web pages and layouts | app/ |
App Router, navigation, server-rendered pages, authentication handlers and compatibility API stubs |
| UI | components/ |
Shared controls, chat/message rendering, application views and administration forms |
| Client state and requests | lib/hooks/, lib/stores/, lib/services/client/ |
Browser state, history reconciliation and requests to the product APIs |
| API runtime | apps/api/src/server.ts, apps/api/src/routes/ |
Fastify registration, authorized business routes, streaming and proxy behavior |
| Agent execution | apps/api/src/lib/agent-*.ts, apps/api/src/lib/agui/, apps/api/src/lib/langchain/ |
Trusted app/run resolution, admission, backend adaptation, persistence and human input |
| Shared protocol | packages/shared/src/agui/ |
Typed contracts, event validation/writing and display/history projections without database or backend credentials |
| Server data and identity | lib/db/, lib/server/, lib/auth/ |
Repositories, PostgreSQL actor context, authentication and identity synchronization |
| Schema | database/migrations/ |
Ordered SQL migrations, constraints, RLS and persistent runtime state |
| Public default content | messages/, content/pages/, public/ |
Shipped translations, page defaults and public assets, not an installation’s private runtime content |
| Tests | __tests__/ and colocated *.test.ts / *.test.tsx |
Regression, component, contract, script and integration coverage |
| Tooling and deployment | scripts/, docker/, compose.yaml, .github/workflows/ |
Public checks, runtime/deployment tools, images and CI |
Read architecture for ownership boundaries and database design for persistent entities. Use the source version you are changing; the latest branch’s docs can differ from an older tag.
2. Prepare A Local Development Instance
Toolchain And Dependencies
- Use the Node version in
.nvmrcandpackage.jsonengines, currently 24.22.2, and pnpm 10.34.9 frompackageManager. - Run commands from the repository root unless a command explicitly selects a workspace. Do not install separate dependency trees inside each workspace.
- Full application development needs PostgreSQL, Redis and S3-compatible storage. They can be dedicated local services or isolated namespaces on services you control. See the deployment guides for initialization rather than using production connections as a shortcut.
- Real conversations additionally need at least one configured Dify application or compatible LangChain service. Synthetic regression tests do not require a real model account; LangChain is not required merely to work on a Dify path.
After cloning your fork or the project, install the pinned dependencies:
npm install --global --ignore-scripts pnpm@10.34.9
pnpm install --frozen-lockfile
pnpm build:shared
cp .env.example .env.dev
The one npm command installs the pnpm tool itself. Use pnpm for project
dependencies and scripts. Keep pnpm-lock.yaml in sync with intentional package
changes; do not resolve an unrelated issue by upgrading the whole dependency set.
Configure Development, Not Production
Edit the ignored .env.dev. The base template includes historical rollout
defaults and example HTTPS hosts; copying it alone does not produce a working
current login configuration. For a loopback development instance, explicitly
select matching settings such as:
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://127.0.0.1:3000
BETTER_AUTH_URL=http://127.0.0.1:3000
CORS_ALLOWED_ORIGINS=http://127.0.0.1:3000
BETTER_AUTH_ENABLED=true
AUTH_BACKEND=better-auth
AUTH_PHONE_OTP_ENABLED=false
NEXT_PUBLIC_GITHUB_LOGIN_ENABLED=false
FASTIFY_PROXY_ENABLED=1
FASTIFY_PROXY_BASE_URL=http://127.0.0.1:3010
FASTIFY_API_HOST=127.0.0.1
FASTIFY_API_PORT=3010
NEXT_UPSTREAM_BASE_URL=http://127.0.0.1:3000
REALTIME_SOURCE_MODE=db-outbox
Also set the real development-only database, Redis and S3 values, isolated
prefixes/bucket, BETTER_AUTH_SECRET and a valid API_ENCRYPTION_KEY. Generate
private values locally; openssl rand -hex 32 produces a suitable new encryption
key for a new environment. Preserve that key after storing encrypted service
credentials. Do not copy a production key into an example or replace an existing
environment’s key simply to make startup pass.
If you change Web/API ports, align the public origin, rewrite destination,
Fastify listener and NEXT_UPSTREAM_BASE_URL together. Inside a container,
127.0.0.1 refers to that container, not the host or another service. Remote
development requires the actually reachable browser origin and appropriate
authentication/storage configuration, not just an open server port.
The development email modes default to dev-log, which records verification and
reset links/tokens rather than delivering mail. Use this only in an isolated
development instance; do not share its logs. SMTP testing and production email
setup are separate, controlled operations. See configuration
for email, GitHub/SSO, branding, storage and optional settings.
Initialize Data And Configure An Application
- Create a new development database, a dedicated Redis namespace and an object bucket. Keep development and test namespaces separate even on one server.
- Apply the repository SQL migrations in filename order to the new database using a migration-owner connection. Follow the role/grant instructions in host deployment or the chosen installation runbook; their database security model also applies to development. Do not rerun fresh-install initialization against an existing database. Apply only reviewed pending migrations when upgrading.
- Use a non-superuser application connection with appropriate grants and RLS actor context. A migration-owner connection is not a normal Web/API identity.
- Start the application, create/sign in to your own development account and follow administrator bootstrap if you need to configure the site. There is no shipped default admin password.
- Configure a provider and service instance, its real backend/category and
ordinary-user access using the administrator guide.
LangChain applications open from
/apps, not the Dify model selector.
For tests, prepare the separate .env.test and dependencies described in
test environments. Do not run data-writing integration tests
against the development database just because it already contains useful records.
3. Start, Edit And Debug
pnpm build:shared
pnpm dev:all
Open http://127.0.0.1:3000 for the example above. dev:all starts Web and API;
pnpm dev:web or pnpm dev:api starts only that part. Web-only mode cannot
serve business routes owned by Fastify. Do not disable the proxy and expect
disabled compatibility handlers to recreate the API.
The default Web dev command uses Webpack. pnpm dev:turbo is an explicit
alternative, not the default acceptance runtime. pnpm dev also enables the
Node inspector; do not expose a debugger on a public network.
The API’s current dev script runs ts-node without a file watcher. Restart the
API after changing its server code; a Web hot reload alone does not reload that
process. Rebuild the shared package after shared-source changes so workspace
imports see the new dist output. Restart the affected process if needed.
Environment And Build Outputs
| Command or directory | Behavior |
|---|---|
scripts/with-env-local.sh |
Sources the selected trusted shell env file, defaulting to .env.dev; AGENTIF_ENV_FILE selects another profile |
pnpm dev:web / .next-dev |
Prepares runtime content and writes Web development output separately from production .next |
pnpm build:check / .next-build |
Performs a production Web build check with separate Web output; does not by itself build shared/API packages |
pnpm build:check:all |
Builds shared/API and then the separated Web build check; shared/API dist is still shared within the checkout |
pnpm build:all / .next |
Builds shared, API and production Web; use an independent checkout if active services use these outputs |
output/runtime/, output/build-messages/ |
Ignored development/runtime-content fixture and prepared translation build input, not files to commit |
Some package commands explicitly select .env.dev or .env.prod; do not assume
an outer AGENTIF_ENV_FILE overrides an assignment inside a script. Inspect
package.json when composing commands. The env wrapper sources shell syntax, so
only load trusted local files; Compose dotenv parsing has different rules.
An external AGENTIF_RUNTIME_DIR must exist and be initialized intentionally.
Without one, supported development/build commands prepare an ignored local
fixture from public defaults. Existing runtime edits can override default copy:
use a fresh isolated fixture when checking changed shipped translations/pages.
Do not delete a live runtime directory to test a default. Avoid invoking next
directly and bypassing content preparation.
Separate Web output directories do not isolate API/shared dist, dependencies,
type caches or runtime data. Do not run competing builds in a checkout serving
another process; use a separate source directory when needed.
4. Make Changes At The Right Boundary
UI, State And Localization
- Reuse existing navigation, message renderers, input and history components. Backend-specific capabilities belong in the capability/adapter contract, not duplicated sidebars or a second independent history system.
- Keep database clients, service credentials and upstream calls on the server. Client code calls authenticated product APIs, not a backend URL with a key.
- Preserve stable message/run identities and ordered history reconciliation; concatenating streamed text or treating one page as the complete history can duplicate or discard messages.
- Use
next-intlkeys, add public English defaults and maintain matching locale structure. Runpnpm i18n:checkfor changes; Home/About runtime translation is different from compiled interface translation. - Check narrow/mobile layouts, keyboard behavior, labels, focus, loading/error states and the affected ordinary-user path, not just administrator access.
API, Authentication And Persistence
- Next owns auth and selected server-rendering endpoints. Fastify owns business APIs. Update both rewrite/registration contracts and their tests when changing a route; preserve deliberate closed compatibility stubs.
- Resolve the session/actor and application access server-side. A browser-supplied user, conversation, run or backend ID is not authorization to access that resource.
- Use the existing repositories and request-scoped PostgreSQL context. Preserve RLS with the actual runtime role; a test passing under a superuser does not establish user isolation.
- Add a new ordered SQL migration for schema changes rather than editing an already-applied historical migration. Include grants, constraints and affected types/repositories, and check both fresh and incremental paths as applicable.
- Use the existing error envelope/request identifiers. Keep secrets, raw upstream errors, tokens and user content out of ordinary logs and public issue evidence.
Backends, Streaming And AG-UI
Start with Dify, LangChain, AG-UI and generation governance.
Shared responsibilities are application/actor resolution, admission and root submission identity, local conversation/run ownership, public event projection, history and persistence. Backend adapters own native endpoints, request/event translation, native identifiers and supported capabilities. Keep those native differences explicit instead of forcing a feature that one service does not have.
When adding an operation or backend, trace the whole path:
- Provider/instance configuration and validation, encrypted credentials and application access/capability description.
- Client request and shared chat/history behavior, including reload and errors.
- Authenticated route, submission/admission checks and trusted native binding.
- Backend transport and allowlisted public event conversion, without leaking tool arguments/results or upstream metadata.
- Server/browser persistence ownership for that specific path, idempotency, saved-result lookup, timeout/disconnect/unknown-outcome behavior and tests.
The negotiated Dify chatbot path is explicitly text-only and server-managed; legacy Dify paths retain their compatible behavior. LangChain human input is a separate opt-in, single-task approve/reject profile. AG-UI schema validity alone does not authorize a tool or implement arbitrary input forms, cancellation or crash recovery. Never blindly retry a generation/resume POST after an unknown outcome or regenerate merely to repair saving.
5. Validate According To The Change
During editing, start with focused tests. For example, after preparing the test profile and building shared:
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh pnpm test --runInBand lib/services/client/agui-stream.test.ts
Before a code candidate is ready, run the relevant full checks against isolated dependencies. From a checkout whose build outputs are not serving live traffic:
pnpm build:shared
pnpm gate:quality:verify
pnpm --filter @agentifui/shared type-check
pnpm --filter @agentifui/api type-check
pnpm format:check
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh pnpm test --runInBand
AGENTIF_ENV_FILE=.env.test bash scripts/with-env-local.sh pnpm build:all
gate:quality:verify includes root TypeScript/ESLint and route, client/database,
RLS and error-envelope guards; it is not a replacement for tests, workspace
checks or production builds. Publication-related tests also need the pinned
Gitleaks scanner described in publication protection.
| Change | Add or emphasize |
|---|---|
| Documentation only | Source/command accuracy, changed Markdown formatting, relative links/anchors and public-content review; no application rebuild or environment rehearsal solely for prose |
| UI | Component regressions and affected production-mode desktop/mobile interaction |
| API/auth/authorization | Route contracts, ordinary-user and disallowed-user behavior, real affected auth/session boundary |
| Schema or data logic | Isolated database, migrated schema/runtime grants, RLS and affected upgrade compatibility |
| Agent/AG-UI | Synthetic backend and stream fixtures, event/history ordering, ownership, idempotency, persistence failures and affected legacy compatibility |
| Translation/content defaults | pnpm i18n:check, relevant content checks and fresh isolated runtime fixture |
| Image/startup/deployment | Real isolated Docker build/startup and affected business smoke; scaling/draining/recovery only as affected |
CI runs in GitHub Actions, not against a contributor’s existing services. The current workflow includes publication checks even for docs, change-filtered quality/tests and Docker Build and Smoke when triggered, with CI Status Check as the aggregate result. Docker can provide the application build in place of a duplicate host build in that CI run. A green CI result does not establish real model, inbox, authorized SSO or production deployment acceptance.
Maintainers validate ordinary features in a separate production-build Stage release under PM2, with automated smoke and affected manual acceptance. Local Docker deployment validation is additional when image/startup/deployment changes require it, not a mandatory full-stack rebuild for every feature edit. Changes are fixed source candidates, not uncommitted production patches. Contributors without shared Stage access should state what they tested and let maintainers complete that environment’s acceptance; never invent a passed result.
6. Diagnose Common Development Problems
| Problem | Check |
|---|---|
| Shared module or exported type not found | Frozen workspace install, then pnpm build:shared; API/shared imports use built package output |
| Business API returns 503 or connection refused | API is running, expected listener/rewrite target and route ownership; disabled Next stubs are not a fallback implementation |
| API edit appears ineffective | Restart the API’s non-watching dev process and rebuild changed shared code |
| Login fails or cookies disappear | Better Auth enabled, matching browser/auth origin and scheme, expected cookie scope, migrated schema and identity profile |
| No models available | Model versus /apps entry point, provider/instance backend/category, enabled state and ordinary-user permissions |
| Default text change does not appear | Runtime overrides, prepared translation input and whether the Web build/dev process has reloaded the intended fixture |
| Relation/permission error | Missing SQL, runtime grants/RLS context and isolated database selection, not an invitation to use the owner role |
| Reply visible but history differs after refresh | Persistence owner, run/save state and trusted history IDs; do not create another model run to repair storage |
| Storage signing/upload fails | Reachable browser endpoint, signed host/query/headers, bucket CORS and actual file limits |
For further diagnosis, see operations. Capture sanitized request
IDs and reproduction steps. Do not publish a complete .env file, reset link,
session cookie, database dump or real conversation to make a report reproducible.
7. Hand Off The Change
Review the diff and follow Contributing for branch/PR, checks, hooks and publication review. Include new migration/configuration and capability limits in the appropriate docs in the same change. Public examples must work without private scripts, operational notes or a maintainer’s credentials.
Merging source into main, tagging a release and deploying an installation are
separate actions. A documentation or feature PR does not automatically authorize
any release or live deployment.