Skip to content

feat(ux): simplify bot creation and personalize first-run experience - #861

Open
samjulien wants to merge 19 commits into
mainfrom
codex/openbot-ux-review
Open

samjulien wants to merge 19 commits into
mainfrom
codex/openbot-ux-review

Conversation

@samjulien

@samjulien samjulien commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR simplifies OpenBot's first-run and Bot creation flows, adds an editable display name, and makes explanatory copy more consistent across the app. Built-in Bots can be created from one screen, while custom agents follow a shorter connection flow that preserves entered values when navigating back.

Bot creation

  • Start with a New Bot heading beside the animated orb, followed by Name, optional Instructions, visibility, and finally hosting.
  • Remove the optional Title field from creation. Keep Instructions as the label for both built-in Bots and custom agents, and remove the extra helper text below it.
  • Use matching selection cards for Run here and Connect a custom agent, placed below Private/Public. Public visibility now says “Everyone in your company can find and use it.”
  • Let users create a built-in Bot directly from this first screen.
  • Clicking Connect a custom agent validates the required details and advances to framework selection. Selecting a framework advances directly to its connection screen, removing the extra Continue click.
  • Put An agent you already run first in the custom-agent list.
  • Preserve the name, instructions, visibility, framework, endpoint, key, and app selections when moving back and forth. Retain endpoint connection testing before creation.

Conversation entry and app chrome

  • Add New Bot at the top of the conversation recipient picker, using the default orb and the preview text “Create a new Bot”. Selecting it opens the existing creation flow.
  • Hide the CopilotKit Inspector by default.

First-run profile and Settings

  • Prompt users who need a display name with an animated orb and a large Hello, nice to meet you. welcome heading.
  • Add a Profile form in Settings so users can change their name later.
  • Persist the name and an explicit name-set preference. Startup no longer overwrites a saved name with the local development placeholder.
  • Add an authenticated, name-only profile update endpoint with trimming and length validation. Preserve IDs, email, permissions, other preferences, onboarding state, and conversation ownership.
  • Keep an untouched Settings form in sync when the welcome dialog saves a name, while preserving any unsaved edits already entered in Settings.

Restrained name personalization

  • When the conversation list is confirmed empty and a usable display name exists, show Hi [name], what would you like to work on? on the home screen. Otherwise retain the generic heading.
  • Keep this greeting in the UI rather than inserting a message into conversation history.
  • Resolve the current user's saved name for each agent run and provide one bounded profile context entry. Replace previous profile context instead of accumulating it, so edits and cleared names are reflected.
  • Keep the name as JSON data, accompanied by fixed guidance to use it sparingly, avoid repeated greetings or inferred personal details, and never treat it as an instruction. The guidance travels with application context because built-in agents omit incoming system messages by default.
  • Omit optional name context if lookup fails or the value is unsuitable. This does not block the user's conversation.

Copy consistency

  • Replace fragmentary explanatory text with direct, complete sentences across Bot pages, Connections, Capabilities, routines, Settings, and admin screens, including approvals, audit, credentials, people, skills, and enterprise configuration.
  • Update the repository's OpenBot copy guidance to carry the same conventions into future changes. Most of the wider file count consists of these small copy edits.

Scope and review focus

This PR contains the UX work on top of current main. The separate local setup fixes remain in #810 and #811.

Review the Bot creation flow in both hosting modes, including validation and Back navigation; the first-run name prompt and subsequent Settings edits; and the scope and handling of agent profile context. The pre-PR review fixed stale Settings state after the welcome dialog and ensured the name-use guidance reaches built-in agents.

CI follow-up

  • Update the IME regression test to start on the new details-first screen while retaining assertions that composing Enter never creates a Bot.
  • Fix an existing failure also present on main: the AG2 test now verifies the exact API key in either the legacy x-api-key header or the Bearer header used by Anthropic SDK 2.3. Endpoint, model, and successful-run assertions remain intact. All five AG2 tests pass locally with the CI dependency versions.

Validation

  • Formatting and lint passed for all 57 changed TypeScript files.
  • App, server, and shared typechecks passed; test typecheck reported no new errors against its existing baseline.
  • 87 tests passed across 17 test files, including profile persistence against an isolated PostgreSQL database.
  • App, server, and worker production builds passed. Vite reports an existing chunk-size warning.

Model wording itself has not been evaluated in a live model conversation; sparing name use is guidance, not a guaranteed model behavior.

@guidovizoso guidovizoso left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this. The Settings profile endpoint, the profileNameSet flag, the dev-actor change that stops overwriting the name, and keeping the greeting in the UI rather than in message history all look good.

The main UX points are inline:

  1. Name prompt: move it into the onboarding flow instead of a global modal, which currently stacks on top of /onboarding.
  2. Recipient picker: your own Bots should come before "New Bot", which is currently auto-highlighted.
  3. Bot creation: "Connect a custom agent" should be a selectable card like "Run here", with the footer button switching between Create Bot and Continue.

Also: the PR has merge conflicts with main, and there's some import-reordering noise in server/src/index.ts and app.ts that makes the diff harder to review.

<CopilotProvider>
<AppHotkeys />
<Outlet />
<FirstRunNamePrompt />

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is mounted in the /_authed layout, which also wraps /onboarding. So a brand-new user gets a modal that can't be dismissed, sitting on top of the "Welcome to OpenBot" onboarding slides. It also gates every authed page, admin included.

Suggestion: make the name a step in onboarding.tsx's STEPS (right after WelcomeStep), reusing ProfileNameForm firstRun, and drop this global prompt. The Settings Profile form stays as is. For people who already finished onboarding and have needsName, the generic greeting from firstConversationGreeting is a fine fallback, or show a lighter one-time prompt outside /onboarding.

multiple
items={profiles ?? []}
isItemEqualToValue={(item: AgentProfile, value: AgentProfile) =>
items={[NEW_BOT, ...(profiles ?? [])]}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With defaultOpen and autoHighlight, "New Bot" is the highlighted item when you land on a new conversation, so pressing Enter takes you to /bots?new=true instead of picking your first agent. Your own Bots should come first.

Two smaller issues:

  • In multi-select, picking New Bot after choosing some Bots throws that selection away.
  • After creating the Bot, you aren't brought back to the conversation with it selected.

Suggestion: move it to the end, or better, make it a footer action outside the list so it can never be auto-highlighted. Ideally open NewBotSheet in place and select the new Bot as recipient once it's created.

* profile links and reloads preserve the pending recipient without creating an empty channel.
*/
/** What `GET /api/agents/:id` answers for a Bot this person cannot see. */
const NEW_BOT = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this was inserted between the AGENT_NOT_FOUND JSDoc and its constant, so "What GET /api/agents/:id answers for a Bot this person cannot see" now describes NEW_BOT.

aria-pressed={custom}
onClick={() => {
if (busy) return;
setCustom(true);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two cards share OPTION_CLASSES and aria-pressed, so they look like a pair of choices, but they behave differently: "Run here" toggles, while this one validates the name and jumps to the framework step immediately.

Suggestion: make this card just setCustom(true) and let the footer button carry the action. It already switches between "Create Bot" and "Continue", and advance() already branches (custom ? go("runtime") : create(...)). The name check belongs in advance. canContinue already requires named.

const [visibility, setVisibility] = useState<Visibility>("private");
/** Run it here unless the person picks otherwise: the answer that needs nothing set up. */
const [runtime, setRuntime] = useState<Runtime | null>({ kind: "builtin" });
const [custom, setCustom] = useState(false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When builtInAvailable is false, "Run here" is disabled but nothing is selected, so Create stays disabled until the user happens to click the other card. Consider useState(!builtInAvailable). Since capabilities loads asynchronously, deriving it would work too: const effectiveCustom = custom || !builtInAvailable.

*/
const STEPS = ["runtime", "endpoint", "about"] as const;
/** Details come first; custom agents then choose a framework and verify their endpoint. */
const STEPS = ["about", "runtime", "endpoint"] as const;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR replaces several longer "why" comments here and on NewBotSheet, AboutStep and RuntimeStep with one-liners. Some of that reasoning still applies after the restructure, e.g. why the heading takes focus on arrival, and why there's no separate Test button because the action that moves on also tests the endpoint. Worth keeping the parts that are still true, in line with the rest of the codebase.

const loadName = deps.loadNameForActor;
if (loadName) {
for (const agent of Object.values(agents)) {
agent.use(new NameContextMiddleware(() => loadName(actor.id)));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attaches the name middleware to every resolved agent, including custom (BYO) agents. So the user's display name is now sent as context to third-party endpoints, which the PR description doesn't mention. Either limit it to built-in agents or call it out explicitly.

} from "@ag-ui/client";
import { defer, from, map, type Observable, switchMap } from "rxjs";

const LEGACY_PROFILE_CONTEXT = "OpenBot account owner profile";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LEGACY_PROFILE_CONTEXT and the PROFILE_GUIDANCE_ID message filtering (on the input and on MESSAGES_SNAPSHOT) handle entries nothing creates any more. It looks left over from an earlier iteration of this PR, so it can probably be removed.

Comment thread server/src/index.ts
coordinationForActor(actorId, initiator, depth),
),
loadInstructionsForActor,
loadNameForActor: async (actorId) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: this builds a new ProfileStore and does a DB read on every agent run (and /api/me gains a query too). Build the store once, alongside the createProfileStore(database) passed to createApp.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants