Modern Creator
Code With Antonio · YouTube

Build Your Own AI Teammate Platform: A Grok Bot Clone, Start to Finish

Code With Antonio spends two hours building a multi-agent chat product where every bot gets its own Linux desktop, and the whole thing is driven by Claude Code without a single line of vibe-coded guesswork.

Posted
4 days ago
Duration
Format
Tutorial
educational
Views
48.1K
782 likes
Big Idea

The argument in one line.

An AI teammate product is not a chat wrapper, it is a durable worker plus a real computer, and the quality of the code depends on how many primitives you hand your coding agent before you ask for a feature.

Who This Is For

Read if. Skip if.

READ IF YOU ARE…
  • You want to ship an AI agent product that actually does things, not one that only returns text, and you need the architecture that makes that possible.
  • You already use a coding agent daily and keep getting back bloated, inconsistent code you have to rewrite.
  • You need to understand why a long-running AI task cannot live inside a Next.js route handler, and what to use instead.
  • You are weighing whether to let users watch and take over an agent's screen, and want to know what that actually costs to build.
  • You have an agent product that works but has no billing, no usage caps, and no idea what a user was doing when something broke.
SKIP IF…
  • You want a no-code path. Every step here is a prompt into a coding agent against a real TypeScript codebase.
  • You are looking for prompt-engineering tricks for chat. This is product architecture, not prompting technique.
  • You only need a single-user script on your own machine, where durability, sandboxes and billing are all overkill.
TL;DR

The full version, fast.

Building an AI teammate means solving three problems a chat app never has to: the task outlives the request, the agent needs a computer, and the user needs to watch and take over. The architecture answers each one. A durable stateless worker runs the agent outside the web tier so a refresh or a redeploy cannot kill a run, a per-bot cloud sandbox gives the agent bash, files, mouse, keyboard and screenshots, and a VNC stream in an iframe lets the user grab the same desktop mid-task. The build discipline matters as much as the architecture: install the vendor's agent skill before asking for the feature, give the agent an existing primitive to copy, and write every working pattern back into agents.md so you never explain it twice. Done that way, late features one-shot.

Free for members

Chat with this breakdown — free.

Sign in and you get 23 free chat messages on us — ask for the hook, quote a framework, find the exact transcript moment, generate a markdown action plan. Bring your own key when you want unlimited.

Create a free account →
Chapters

Where the time goes.

00:00 – 03:47

01 · Intro and live demo

Three companies shipped the same product within months, all rebuilding an open source tool that was ahead of its time. Demo of the finished app booking a flight with human takeover, negotiating MacBook prices over email, and recalling a memory across chats.

03:47 – 07:50

02 · Architecture breakdown

The whole system in one diagram. Why the message leaves Next.js for a durable stateless worker, how the agent gets sandbox exec, file and screenshot tools, and how noVNC in an iframe becomes a takeover-able desktop.

07:50 – 16:54

03 · Project setup and agent rules

Scaffold the Next.js app, write the first agents.md rules forbidding unprompted branches and commits, install the component library skill, and deploy to Railway through the agent's CLI skill on every push.

16:54 – 22:14

04 · Giving the agent a real web

The built-in fetch tool fails on both a branding extraction and a JavaScript-rendered company directory. Swapping in a real scraper returns full design tokens and structured results, at 93 percent fewer tokens.

22:14 – 33:08

05 · Authentication

Install the auth skill, let the agent set up keyless mode, then hit a real deploy failure caused by skill folders containing miniature apps. The fix is a .dockerignore, and the agent diagnoses it from the build log.

33:08 – 39:50

06 · Backend primitives

Database, object storage and AI gateway from one platform, wired through Drizzle ORM. Two agents.md rules get added here: push the schema and never migrate, and never write backwards compatibility because the data is worthless.

39:50 – 41:26

07 · Bot schema

The single bots table. Marking user id and sandbox id as third-party ids stops the agent from inventing users and sandboxes tables, and an index on user id keeps the sidebar query fast.

41:26 – 49:35

08 · Bot creation form

Generate the insert schema from the table first, then the server action, then the dialog. The same conversation then gets written back into agents.md as reusable form rules, so later forms need one sentence.

49:35 – 53:13

09 · Chats and membership

A chats table and a chat members join table, so bots exist independently of chats and a chat can hold many bots. Bot creation becomes one transaction that writes bot, chat and membership together.

53:13 – 1:06:01

10 · Sidebar and command palette

Route groups separate signed-in pages from auth pages so only the former get a sidebar. Chat list, searchable command dialog, collapsed icon mode, and cookie-persisted sidebar state.

1:06:01 – 1:11:19

11 · Chat page and header

The chat id page inside the dashboard route group, an active-chat highlight, and a header whose avatar opens an edit and delete dialog built from the form rules already captured in agents.md.

1:11:19 – 1:17:21

12 · Wiring up AI chat

Install the chat UI primitives and the SDK skill, pick a provider or route everything through an AI gateway for access to all frontier models at standard prices, and get token streaming into the composer.

1:17:21 – 1:29:43

13 · Durability

Refresh the tab mid-answer and the run dies, because the route handler is part of the web app. The agent moves to a stateless worker with built-in transcript storage, and the run now survives refreshes, crashes and deploys.

1:29:43 – 1:42:17

14 · Sandboxes and computer use

One sandbox per bot, created and destroyed with the bot. The agent gets bash, file, mouse, keyboard and two screenshot tools, then the desktop button opens a resizable VNC panel the user can click into and control.

1:42:17 – 1:56:39

15 · Group chats, handoff and memory

The whole group chat feature lands in one prompt because the codebase already has the patterns. Then an avatar per reply, a handoff tool, and memory upgraded from an instruction to explicit remember and recall tools.

1:56:39 – 2:03:10

16 · Billing and usage limits

A paid plan gating bots and sandboxes, then a real usage ledger for the two costs that scale: model tokens and sandbox hours, shown as progress bars with a reset date on a usage page.

2:03:10 – 2:13:47

17 · Observability

Error tracking and session replay in the web app, then inside the durable worker where the agent actually runs, then structured logs, then a review agent that root-causes an error and opens the pull request.

Atomic Insights

Lines worth screenshotting.

  • An AI teammate is not a better chatbot, it is a durable worker with a computer attached, and almost all the engineering goes into those two things.
  • A Next.js route handler cannot host a long agent run. Refresh the tab or redeploy the app and the connection dies mid-task.
  • Moving the agent into a stateless durable worker took deleting one route handler and moving one stream call, and it turned an MVP into a production-ready chat.
  • Every bot gets its own cloud sandbox on creation, so the sandbox id is just a third-party column on the bot row, not a table of its own.
  • Screen takeover is an iframe pointed at a signed noVNC URL, promoted to a fullscreen dialog with inputs enabled. There is no custom remote-desktop code.
  • Give the agent two separate screenshot tools: one it uses privately to look at the screen, one it uses when the user asked to see the screen.
  • Installing a vendor's agent skill before asking for the feature is the difference between the agent guessing from training data and the agent following current docs.
  • Asking for the validation schema first, then the server action, then the form, produces better code than one screenshot and a request to wire it all up.
  • Coding agents treat every project as production and write pointless backfills and compatibility shims, so tell yours in writing that the data is worthless and to prefer data loss.
  • Replacing the built-in web fetch with a real scraping tool uses 93 percent fewer tokens and can read pages that load their content with JavaScript.
  • The built-in web fetch tool converts a page to markdown and summarizes it, which is why it can never return a site's colors, fonts or button styles.
  • If a CLAUDE.md file exists, Claude reads it instead of agents.md, so reference agents.md from inside it or your rules silently stop applying.
  • Agent skill folders ship miniature example apps with their own package.json, which breaks deploy builds until you add them to a .dockerignore.
  • By the time a codebase has forms, dialogs, queries, durability and persistence as working examples, a whole group-chat feature can land in one prompt.
  • Instruction-based memory is flaky. Replacing 'reference the memory file when appropriate' with explicit remember and recall tools made it work every time.
  • The honest usage-billing model meters the two things that scale with use, model tokens and sandbox hours, and shows the user a progress bar for each.
  • An error tracker with session replay plus logs plus repository access can find the root cause, write the fix, and open the pull request itself.
Takeaway

Agents need a worker, a computer, and your patterns.

WHAT TO LEARN

An AI teammate only works when the run outlives the request and the agent has a real machine to use, and the code only stays clean when you hand the coding agent an existing pattern before every feature.

01Intro and live demo
  • Three companies shipped near-identical AI teammate products within months by rebuilding an open source tool, which means the idea was never the moat.
  • The feature people actually want is human takeover: the agent gets stuck on a checkout field, you grab the keyboard, and the task finishes.
  • Memory is what turns a tool into a teammate. Anything told to a bot privately should be recallable when that bot is in a group chat.
02Architecture breakdown
  • Messages leave the web app for a durable worker because that tier has no request timeout and survives crashes and redeploys.
  • Three tool groups cover almost all computer use: run commands, read and write files, and take screenshots of the screen.
  • Screen takeover is a signed noVNC URL in an iframe, promoted to a fullscreen dialog with inputs enabled. No remote-desktop code of your own.
03Project setup and agent rules
  • Write rules into agents.md the moment the agent misbehaves, starting with never creating branches or commits unless you explicitly asked.
  • Install the vendor's agent skill before asking for the feature, so the agent follows current documentation instead of whatever version its training data remembers.
  • Set up continuous deployment on day one through the platform's own CLI skill, so the agent can deploy, read logs and fix its own build failures.
04Giving the agent a real web
  • The built-in fetch tool converts pages to markdown and summarizes them, so it can never return a site's colors, fonts or button styles.
  • It also cannot read anything rendered by JavaScript after page load, which quietly breaks most research prompts against modern sites.
  • Swapping in a real scraper returns structured design tokens and registry data, and does it with 93 percent fewer tokens than the built-in search.
05Authentication
  • Keyless auth setup lets the whole flow be tested before any account exists, so authentication stops being the thing that blocks the first deploy.
  • Skill folders ship miniature example apps with their own package.json, and deploy builders mistake them for your app. Add those folders to .dockerignore.
  • When a build fails with a cryptic message, hand the deploy log back to the agent. It diagnosed the duplicate package.json problem unprompted.
06Backend primitives
  • Prototype with database push and no migrations, because migration conflicts cost time and tokens on a project with no real users yet.
  • Tell the agent in writing that the data is worthless and to prefer data loss, because agents default to writing backfills no prototype needs.
  • Remove those rules before production. The same instruction that keeps prototyping fast will happily drop a live database.
07Bot schema
  • Mark ids that belong to another service as third-party ids, or the agent will invent extra tables to own data it does not own.
  • Index the column your main list query filters on at the moment you define the table, not after the list feels slow.
08Bot creation form
  • Order the prompt as schema, then server action, then form, so validation has one source of truth and the form can never drift from the table.
  • After the agent builds something well, ask it to write that approach into the instructions file. Every later version of that feature then costs one sentence.
09Chats and membership
  • Join tables let the same entity serve both one-to-one and group cases, so direct chats and group chats share one schema instead of two.
  • Wrap dependent inserts in a transaction so a bot cannot exist without its chat, and a chat cannot exist without its membership row.
10Sidebar and command palette
  • Route groups keep a layout scoped to the pages that need it, so signed-in pages get a sidebar while sign-in pages stay clean.
  • Build the list before the search over it. A search box has nothing to filter until you know how the list loads.
11Chat page and header
  • Point the agent at a layout file that already exists when a query lands in the wrong folder. It will relocate the new code to match your pattern.
  • Reusing one edit dialog for update and delete gives a single place to manage an entity, instead of scattering destructive actions across the UI.
12Wiring up AI chat
  • An AI gateway gives one key and one base URL across every frontier provider at the same prices, so switching models costs nothing.
  • Free provider tiers are usually too limited for heavy tool use and computer use, which is the exact workload an agent product runs.
13Durability
  • Refresh the tab mid-response and a route-handler agent dies. That single test is the clearest argument for moving the run off the web tier.
  • The migration was small: delete the route handler, move the streaming call into a worker task, and get transcript persistence for free.
  • Scope persistence to the chat id so each conversation keeps its own transcript, and a reload rejoins a run already in progress.
  • Production needs its own worker instance and its own key. A production web app pointed at a local worker hangs forever in submitting state.
14Sandboxes and computer use
  • Create and destroy the sandbox with the bot so there are no orphaned machines quietly billing you after a user deletes an agent.
  • Give the agent two screenshot tools: one it uses to look at the screen privately, one it uses when the user asked to see it.
  • Tell the agent what it is before you tell it what it can do. Base instructions about its name, job and environment ship before any tooling.
  • If a CLAUDE.md exists, Claude reads it instead of agents.md. Reference one from the other or your rules silently stop applying.
15Group chats, handoff and memory
  • Once a codebase has forms, dialogs, queries, durability and persistence as working examples, a whole feature can land in one prompt.
  • Ship the dumbest version of multi-agent first. One bot per turn, chosen by the user, gives you a primitive to upgrade rather than a problem to solve.
  • A handoff tool is the smallest real step toward agents that cooperate, and it needs explicit prompting before a model will reach for it.
  • Instruction-based memory is flaky. Explicit remember and recall tools made cross-chat memory work reliably where a memory file instruction failed.
  • Restart the worker after adding tools. A stale instance silently serves the old tool set and the new behaviour appears broken.
16Billing and usage limits
  • Gate the expensive features behind a plan before you build a pricing page, and let the blocked action itself route the user to the plans.
  • Flat subscription pricing ignores the two costs that actually scale: model tokens and sandbox hours.
  • Meter those two as a ledger with a visible progress bar and a reset date, so the user sees the ceiling before they hit it.
17Observability
  • An error your user sees and you never hear about is the real failure. Error tracking belongs in the project before the first real user.
  • Session replay shows what the user clicked in the seconds before the error, which turns unreproducible bug reports into a video.
  • Observability in the web app only covers half the system. The worker where the agent actually runs needs its own instrumentation.
  • Structured logs around every meaningful action turn an isolated stack trace into a sequence you can read backwards from the failure.
  • Give the error tracker repository access and it can root-cause an error, write the fix and open the pull request for review.
  • Just using the finished app surfaced errors nobody knew existed, which is the honest argument for shipping observability before launch.
Glossary

Terms worth knowing.

Durable stateless worker
A process that runs outside the web application with no request timeout. Work survives crashes and deploys because the run is tracked independently of any open browser connection.
Sandbox
A disposable cloud Linux machine assigned to one agent. It has a filesystem, a shell and a graphical desktop, so the agent can install things, open a browser and click around without touching your infrastructure.
VNC and noVNC
Virtual Network Computing is a protocol for viewing and controlling a remote screen. noVNC is the browser client, so a desktop can be streamed into an iframe and controlled with no plugin installed.
Agent skill
A folder of instructions and documentation a vendor publishes so a coding agent knows the current, correct way to use their product instead of guessing from training data.
MCP (Model Context Protocol)
A standard that lets a coding agent call an external service's tools directly, so it can create a project or set an environment variable instead of telling you to do it.
AI slop
Technically working but bloated AI-written code: duplicated schemas, needless abstraction and defensive compatibility layers, usually produced when the agent was given a goal but no existing pattern to copy.
Route group
A Next.js folder named in parentheses that groups routes under a shared layout without adding a URL segment, so signed-in pages can share a sidebar while sign-in pages stay bare.
Drizzle ORM
A TypeScript database layer where the schema is code. Validation schemas can be generated straight from the table definition, so forms and database stay in sync automatically.
Database push versus migration
Push rewrites the database to match the current schema file and may drop data. Migration keeps versioned change files. Push is faster while prototyping, migration is correct once real users exist.
AI gateway
One endpoint and one key that proxies to many model providers at their normal prices, so you can switch between frontier models without separate accounts and credits for each.
Transport metadata
Extra fields sent alongside a chat message, such as which bot the user is addressing, so the backend can route the turn without encoding that choice into the message text.
Handoff tool
A function an agent can call to pass the user's request to a better-suited agent, which is the first real step from parallel single agents toward a cooperating team.
Usage ledger
A running count of metered consumption inside the current billing period, used to show progress bars and to block further use once a plan's allowance is spent.
Session replay
A reconstruction of what the user saw and clicked in the seconds before an error, played back alongside the stack trace so you can see the path that caused it.
Resources

Things they pointed at.

1:19:02toolTrigger.dev ↗
1:29:31toolDaytona ↗
18:40toolFirecrawl ↗
07:18toolClerk ↗
2:03:32toolSentry ↗
33:08toolNeon ↗
14:38toolRailway ↗
10:40toolshadcn/ui and its agent skill
35:45toolDrizzle ORM
40:26toolnanoid
43:20toolDiceBear avatar library
1:01:09tooldate-fns
1:11:35toolAI Elements
1:12:34toolAI SDK
19:19productFirecrawl Alexandria data registry
1:13:45productNeon AI Gateway
06:48toolnoVNC websockify
2:11:01productSeer, Sentry's code review and autofix agent
10:10toolClaude Code VS Code extension
Quotables

Lines you could clip.

03:31
“Even though we will be using AI to develop this project, we are not going to vibe code our way through. We are going to know exactly what we are doing at every step of the way.”
the thesis of the whole build in two sentences, and a direct shot at the dominant way people use coding agents→ IG reel cold open↗ Tweet quote
46:44
“Well, you could do that but that is also how you create AI slop. By giving the agent some primitives at the beginning, it will almost certainly create better code.”
names the failure mode and the fix in one breath, no setup needed→ TikTok hook↗ Tweet quote
1:17:54
“The answer is the agent would be interrupted. This is why durability is so important.”
the punchline to a live demo where refreshing a tab kills the agent mid-sentence→ TikTok hook↗ Tweet quote
1:25:02
“So with this change not only did we implement durability and persistence, but we actually went from an MVP to a production-ready AI chat.”
huge claim attached to a tiny diff, which is the surprise→ newsletter pull-quote↗ Tweet quote
38:40
“Agents are notorious for backwards compatibility that is unnecessary because they treat every project as a production project.”
names an annoyance every agent user has felt but not articulated→ newsletter pull-quote↗ Tweet quote
20:18
“Not only are you going to get cleaner and faster results, but you will also use 93% fewer tokens.”
hard number on a swap most people have not made→ TikTok hook↗ Tweet quote
1:09:20
“It's very important for agents to have some existing patterns and primitives, and why we were very strict in the beginning and becoming more and more lenient now with our requests.”
explains why prompts get shorter as a codebase matures, which is counterintuitive→ newsletter pull-quote↗ Tweet quote
1:42:54
“Since our project is very mature at this point and already has enough primitives and examples, we can actually one-shot the entire group chat feature.”
the payoff of ninety minutes of discipline, stated as a casual aside→ IG reel cold open↗ Tweet quote
1:54:17
“Unfortunately, I was a bit overconfident and it didn't work.”
a tutorial admitting a failed demo on camera, then fixing it, which is rare and builds trust→ TikTok hook↗ Tweet quote
2:12:28
“Even though we thought our app doesn't have any errors, look at just how many it was able to find by just using the application.”
the argument for observability made by evidence rather than assertion→ newsletter pull-quote↗ Tweet quote
2:10:53
“Imagine having all of that information, all of that stack trace, all of those logs and the power of AI. At that point, no error is unsolvable.”
big closing claim, clean on its own→ IG reel cold open↗ Tweet quote
The Script

Word for word.

Read-along

Don't just watch it. Burn it in.

See every word as it's spoken — crank it to 2× and still catch all of it. The same dual-channel trick behind Amazon's Kindle + Audible.

metaphoranalogystory
Over the past few months, we've seen multiple companies release AI teammates which can assist you with everyday tasks. The first one to do it was XAI with their Grok bot. Shortly after, Meta followed with its own version called Muse.
And just a few days ago, OpenAI introduced DOTS. You might remember that there was a product before any of these companies released their own called OpenClub, which was the first AI that actually did things. You could book a flight, or send an email.
So that begs the question, why are these companies gaining so much traction if something like that already exists? and it's completely open source and free. Primarily, it is because OpenClaw was mostly intended for tech -savvy people, but it was also because it was ahead of its time.
During its initial release, the models simply weren't as intelligent as they are today, and you could often see examples of OpenClaw failing at simple tasks. All of these companies saw that as an opportunity. They decided to rebuild their own OpenClaw.
package it into a familiar user experience, give it official connectors to other companies, and power it by an intelligent model. Which is why in this video, I'm gonna teach you how to build your own product like Grokbot, OpenAI Dots, or Metamuse. And this is the result.
Let me show you what it can do. I'm going to ask my agent to book me a flight from London to Barcelona on Friday morning. Make it non -stop and nothing before 7 a .m.
The agent can use its desktop to open a browser and search for the flight I requested. Once it finds the flight, it goes to the checkout page. In here, it enters the information it knows, but it gets stuck with information it doesn't know.
In that case, I can simply take over the desktop and input that information myself. And just like that, me and my agent have booked a flight together.
Now, I've purposely made this an example where a human has to take over, but your agent also has memory. So next time, he can actually remember these details. I just told my agent that 10 new engineers start on Monday and they all need MacBook Pros.
So ask distributors for the prices. and get the best deal. The agent can use the email client just like you can, simply give it access to your account.
The agent can write the email itself and send it to the recipient. When it receives an email, the agent can open the email and reply itself.
This way, you can give it a task to negotiate the best deal for you. Everything that you can do on your desktop, the agent can learn how to do on its own desktop. It's always going to ask us for the final confirmation.
In this example, I am confirming that I want to take this deal and the agent will draft a reply for me. And just like that, the agent found the best deal. One of the most important features our agents can do is memory.
I'm telling an agent in an individual chat to remember what is my staging URL and it's written down in its memory file. So I can now go instead of a group chat with the same bot and I can ask it to pull out that memory from its file. This way everything you tell to your agent stays as a part of their personality.
The possibilities of what you can make your agents do in this project are truly endless. Not only that, but since we will be using agentic coding to develop this project, you will be able to style it however you want. I personally chose the Grokbot look and feel, but you can style it as OpenAI Dots, or Metamuse, or maybe your custom design.
Even though we will be using AI to develop this project, we are not going to vibe code our way through. We are going to know exactly what we are doing at every step of the way.
We will be utilizing skills and MCPs to get exactly what we want deterministically without AI slop. So, without further ado, let's get started. Before we can start building this application, we need to understand how it works and the architecture behind it.
The user interface behind this application is fairly simple. On the left side, we have a sidebar, which is in a global layout, which means that the same sidebar appears for an empty root page, but also for an individual chat page. Besides the app sidebar, the only other components we have are the global command dialog, which helps us search through chats, and an individual desktop preview for each agent.
The desktop preview is within a resizable panel, so the user... can adjust exactly how much they want to see. And if they want to take over, all they have to do is click on the actual component, which then allows them to take over the bot's desktop.
So what actually happens when I tell the agent to open the browser and type in Hello World? How does it know to connect to a remote machine? How does it know how to open the browser?
And how does it know how to type on that remote desktop? And why is it so simple for us to take over? When we submit a message from our chat interface, it first reaches the Next .js application.
And in here, it's being processed by either a server action or a route handler. We could technically run the entire AI processing within that environment, but the reason we don't is because... it wasn't built for long running tasks like AI functionality.
Because of that, our server action or route handler needs to pass that information to a stateless durable worker, which in our case is a trigger .dev agent. This agent lives outside of the Next .js environment where the rest of our application is running, which means that it has no request timeout, so long agent runs actually survive any crashes.
or deploys will resume the run automatically. Whereas if we had run this in our Next .js environment, it would unfortunately be interrupted. And it scales separately from the Next .js web tier.
And it is in this stateless worker where the magic actually happens. What we do here is we invoke a new AISDK agent and we give it tool access. Specifically, we give it sandbox execution tool, sandbox file tools, and sandbox screenshot tools.
And with this combination, it is able to achieve 99 % of the tasks that require computer use. So how are we able to see what's happening on the agent's screen and how are we able to take over? Well, the answer is in VNC, which stands for virtual network computing.
Every time we create a new agent, we also create an equivalent sandbox so it can access the computer. The sandbox has an open virtual network computing protocol using a noVNC web SOCify, which is open on a specific port. On the UI side, we start an iframe, which loads a signed URL, which can access that open port.
And then once we click on that iframe, we load it in a full screen dialog and we enable the input. And from that point on, we can control the agent's browser. I purposely wanted to dive deeper into those two features, sending a message and controlling the agent's desktop, because I feel like they are the distinguishing features that make this project and what most of you are interested in how it works.
Besides that, we're going to be using Clerk to protect our application and Neon in combination with Drizzle ORM to persist and store our data. Now, I understand that not everyone who watches this video comes from a technical background. So if a diagram like this scares you, don't let it discourage you.
All of this is simply to explain what direction I'm going to be following while I build this project and not something that you have to necessarily deeply understand. Now let's run the following command to scaffold our project and to give us the primitives we need to start developing.
For the template option, make sure to select Next .js. For the monorepo option, make sure to select No. For the component library, choose Base UI.
For the preset, choose Nova. And finally, for your project name, give it something recognizable like AI teammates. And then wait a few minutes.
the project to install. Once the project has been initialized, you should be able to see it within your directory and you should now open it within your IDE. Once you open your project, you should see a folder and file structure that looks like this.
If you're working with a separate terminal, make sure to change directory into the new project we just created. Now let's create a new GitHub repository so that we can keep track of all the changes that we do in our project. Once you've created a new repository, go ahead and find the snippet for pushing an existing repository from the command line.
Then go inside of your project, make sure you are in the correct folder and paste it and run the snippet. Once the command has been successfully executed, go ahead and refresh your GitHub repository and you should now see the entire project here as well. Now go ahead and run npm run dev which is going to start this project at localhost 3000.
You can then visit that page in your browser and you will see a welcome screen that looks like this. Now that we verify that our app runs correctly, it's time to prepare this environment for agentic coding. This means adding certain skills and modifying agents .md instructions so that the agent behaves as we expect it to do.
So first thing that I like to do is go inside of my agents .md which is essentially readme for agents. And I like to add the following.
So a separate section for Git. Never create branches, work trees or commits automatically. Only do so when the user explicitly asks for that specific action in the current request.
Leave changes uncommitted in the working tree on the current branch and let the user decide when and how to commit. So I always like to add this because I really like to use my agent to commit and create branches, but I don't like when the agent does that on its own. Now, how you are going to run your agent is irrelevant.
relevant to this project but I do want to give you a recommendation. I always have this extension turned on for multiple reasons. Number one is that it gives me a very nice interface to run cloud code which is preferable to many people over a terminal user interface.
But the second reason is built -in native connection to Visual Studio Code which is our code editor. So whenever you use this extension cloud code knows exactly which file you currently have open or which line you have selected.
So this is just a recommendation from me if you want to have a better experience. Since we are going to be using ShadCN components to develop this project, it's a good idea to install ShadCN skills so our agent actually understands how to use it. Now, if you've never installed skills before, I'm going to guide you through it, but all other times I'm just going to tell you to install the skill.
So let's go ahead and run the following command. It's going to ask you which skills you want to install. You can select all, which is going to install both the Migrate skill and the ShadCN skill.
Then it's going to ask you for which agent you want this to install. So go ahead and make sure that Cloud Code is selected. And if it asks you for the installation scope, I would recommend choosing project so you can actually commit these skills.
So if you have anyone working with you, they're going to have the exact same skills. And for the installation method, choose symbolic link if available. And then proceed with the installation.
You should now see two new folders within your project. .agents and .cloud. Both of them should have the exact same skills.
ChatCN. and migrate Radix to base. Keep in mind that in case you are not seeing one of the folders, for example, if Claude is missing, you will not be able to load these skills in Claude code.
So go back to where I installed the skill and carefully look at how I select Claude code. To verify whether you correctly installed the skill or not, Make sure to run reload skills if you already have an active instance running, and then go ahead and open the skills dialogue.
And inside of here, search for ShadCN UI. You should see migrate Radix to base and the ShadCN skill. Since we currently have 29 untracked files inside of our source, let's go ahead and tell our agent to stage and commit.
all changes. Now that we have the Schatzian skill, we can reliably tell the agent to add all available components. Previously, this would mean that the agent would have to fetch the Schatzian documentation online or use its training data, which can be different depending on what model you're using and what effort you're using.
But by installing a skill, you can reliably tell your agent, use this skill and then... do the following and both you and I will have very similar if not the exact same results. You should now see a bunch of new files within your component's UI folder.
Previously in here we only had the button component but now you should have the complete set of ShadCN primitives inside. Before we stage and commit, I would recommend running the following prompt. Ensure the layout has all providers necessary to run added components.
So some components need providers registered in the global layout file in order to be used in pages later on. Inside of my source control, I now have changes in my layout file. The agent has added toaster and the tooltip provider and simply registered it inside of the root layout.
Inside of the conversation, I can actually see that it left out three providers. sidebar provider, message scroller provider and the direction provider. The reason it left those out is because they don't belong into the root layout.
They belong in their specific layouts. So just make sure that inside of your layout file you can see the theme provider, the tooltip provider and the toaster. I am purposely being very granular in the beginning of this tutorial because I want to make sure that you have the correct foundation that you can grow from later on.
Now that you have have all the components added and the layout providers properly registered, go ahead and stage and commit. You can also tell your agent to push the changes because we are now ready to deploy our project.
We now need to establish a workflow so that every time we push our changes to GitHub, it automatically creates a continuous integration and deploys. And for that, we're going to be using Railway.
Once you've created an account with Railway, you can go ahead and install their CLI tool, which will help you with agentic use when it comes to deploying your project. Once you've installed the CLI, you might find yourself with this problem. Railway command not found.
If that's the case, You can simply copy what it says in step two, source home railway environment, or whatever it says for your machine. So once you go ahead and paste this and try running railway login, you should be able to run the command.
Once you click on the link that it gives you, it should connect with your newly created account. Just make sure to authorize. Now you can run railway setup agent, which will detect all the agents on your machine.
and install the necessary tooling for your agents to communicate with Railway. In this initial list, just ensure that the agent you actually use is selected. Once you proceed, for the configuration of MCP server, you can simply leave it as the default remote option.
Now open a brand new Cloud Code session, reload your skills, and open the Skills dialog. In here, search for Railway. and you should see the Use Railway skill.
Right now, I have zero projects in my Railway dashboard, and all I have to do is tell my agent to deploy this project using Railway. And in a few moments, your agent should give you the URL where you can visit your app in production. If you take a look at your dashboard, you will now have one new project in here, AI Teammates.
The only thing we have to do is connect the GitHub repository to this deployment. So go inside of your settings and click on connect repository. And then find the respective repository that you want to connect.
And make sure to hit deploy to save the changes. Now every time you push a new change into your GitHub repository, it's automatically going to create a new deployment on Railway. And from now on, you can simply ask your agent if you want to change, add, debug, or fix anything with your railway project.
Now, Cloud Code comes with a bunch of useful tools, but sometimes they are not enough for what we need. Let me give you a clear example. We already know that we are trying to build an application that looks and feels like Grokbot.
So let's say just for prototyping purposes, I wanted to extract the entire branding from this page. I might prompt my agent something like this. Use your WebFetch tool to open the URL I'm trying to clone.
and tell me the exact colors, fonts, and button styles it uses. Now, I'm purposely engineering this prompt so it can show you the limitations of the built -in WebFetch tools. Very shortly after you paste this request, the agent will tell you that it didn't succeed to extract the things we wanted.
The request itself actually failed, but even if it was successful, the built -in WebFetch tool can't extract colors, branding, or fonts from a URL because the only thing it does is it converts the page to Markdown and then summarizes it with a small model. And to be honest, this is a pretty specific example.
So let's give it something easier. Once again, use your WebFetch tool to search the Y Combinator company directory at the URL and search for AI agent teammate. This is a pretty common request.
Perhaps I'm researching all competitors who are building a similar thing like I am. I want to see what technologies they're using, whether they're accepted into Y Combinator or not. So let's see what happens.
Once again, the agent failed to fetch the companies. So this time, it wasn't even a complicated request. I just wanted to see a list of companies on this URL.
The problem is they are loaded with JavaScript after the page loads. And WebFetch... can't handle that.
This is why in all of my builds, I recommend and personally use an open source tool called Firecrawl. And I connect it to my agents and give it access to the entire web. So let's go back to our first example.
I want to get the branding from our Grokbot URL. So in the format, I'm just going to choose branding. I don't even need the markdown.
And I'm going to click start scraping. Instead of failing, Firecrawl managed to extract the logo, icon, image, as well as the primary and secondary buttons, the fonts the website uses, typography information, all the colors on the site, spacing units, as well as border radius.
But what about all of the resources which WebFetch tools cannot access, either by being forbidden or by being loaded with JavaScript after the page loads? This is where Firecrawl's newest product comes in, Firecrawl Alexandria. So let's try our request one more time, but this time by using the Y Combinator data registry.
I'm going to use the Y Combinator search tool and enter the query AI agent. which is the exact query we told our agent to search for. And instead of failing to load the data, we get back structured JSON showing us a list of every single company that is currently building the same thing that we are.
Now you might be thinking that this tool should only be used for specific cases like extracting the branding or bypassing JavaScript loading, but that's not the case. You can in fact replace the entire built -in web search tool with Firecrawl and not only are you going to get cleaner and faster results, but you will also use 93 % fewer tokens.
If you're still not convinced, Firecrawl is completely open source, completely free for our use case, gives you additional free credits if you create an account, and completely reversible if you change your mind. To enable it in your coding agent, First, run the following command.
This is going to initialize the Firecrawl CLI and log you in using the browser method. So make sure you create an account first. Make sure to authorize the Firecrawl CLI.
And then you can go back into the terminal. Now make sure to restart Cloud Code and try searching for Firecrawl. You should see all the tools available here as well.
Now let's try this again. Use FireCrawl to open the URL and tell me the exact colors, fonts, and button styles it uses. And this time, instead of failing, the agent successfully extracted all of the things we asked it for.
We could now easily continue this conversation and tell our agent to apply this theme to our ShadCN components. And that way, we can easily clone any website we want to rebuild.
So how do you use Alexandria? Well, very simply, tell your agent to use Firecrawl and then describe what you're searching for. Firecrawl is going to intelligently use Alexandria when it needs to use a registry of data to give you the results you need.
For example, I'm going to ask it to find all companies building AI agent teammates and then summarize what are the most common features they offer. And once again, instead of failing at the first site of a JavaScript load, Firecrawl successfully loaded all companies that are building AI agent teammates and also gave us a list of the most common features that they have.
I think it's pretty clear at this point why I add this tool to every single project that I build. Without changing a single line of code, we have drastically increased the knowledge and capabilities of our coding agent. Now let's go ahead and install the skills which we are going to need to add authentication to our project.
Whenever you install new skills, make sure to use use the latest version tag because the previous ones had a bug where it didn't install .cloud folder skills at all. So just ensure that you're using the latest version and then add clerk skills.
Now let's go ahead and simply select all skills. Now for the additional agents, I always like to scroll down and check whether cloud code is selected or whatever is the equivalent of the agent that you are using. And once again, I'm using the project installation scope.
so that I can actually commit these skills to the repository. And I'm using the symbolic link installation method. You will now have a bunch of new files inside of your source control.
The only thing I want you to check is that you have the same skills in both .agents and .cloud. So you should see instances of clerk skills in both. And since 162 changes is quite a lot to keep track of, let's go ahead and commit early on so that we can actually be aware of the layout and page files that we actually modify and don't confuse that with this 162 changes.
So let's do a very simple stage and commit. Now go ahead and open a brand new Cloud Code session. And like we always do, first reload your skills.
So it adds all the skills we just installed and then verify that you can actually see them by opening the skills dialog and simply search for clerk. You can now tell your agent to add authentication to this project using clerk skills.
So before my agent run anything, it actually gave me a breakdown of what's going to happen. So it checked the project. It detected that this is a Next .js 16 app router with chat cn, npm, node, and no existing out provider or any clerk install, which means this is a clean setup with no migration needed.
The clerk setup skill asks the agent to confirm before running anything. It's going to set up clerk in this project. and it's going to start the app with the clerk installed.
So it's going to run the following npx commands and it purposely appended the no skills flag because we already have skills and because we just told the agent to use the skills so no need to do it twice. And it's going to install clerk in keyless mode which means you don't need a clerk account to actually test your app. Only later when you are ready to claim this instance of your project on clerk, will you ask the agent to perform clerk out login, which will take you to their login page.
So the only thing I'm noticing here is the fact that it's going to start the app with clerk installed. This can sometimes be a little bit annoying because we often have our own npm running. So in case you have npm run dev running like this, just go ahead and close it because Otherwise, if you have it running, this skill right here can get confused and it's gonna say, oh, it's already running and it's just gonna waste your tokens.
So let's just go ahead and say... continue. After clerk is initialized, it will most likely take up your port 3000 because I already told you the agent told us it's going to run localhost 3000 because it needs it to test whether their implementation works correctly.
So if you try doing npm run dev, it's going to tell you that port is already in use. So simply tell your agent to kill that port. You will now be able to load your project on localhost 3000 and you should now see a header with sign in.
in and sign up buttons and these buttons should redirect to the sign in page or to the sign up page. Now go ahead and try creating an account and once you create an account instead of the sign in button and sign up buttons you will see a user button with your currently logged in user. Now I wouldn't really obsess over whether your agent implemented this exactly the same as mine, meaning that perhaps your agent added a user button in the page instead of in the header here, or it didn't add anything to the layout.
Those things don't really matter right now because it's not going to end like this. Anyway, we will have a sidebar and that's where our auth control is going to be. So as long as you can actually log in and log out, you are ready to stage all.
and commit. What I would recommend doing now is pushing all of your changes. So if you have a button you can click here, go ahead and click it or just tell your agent to push the changes.
Railway is now going to detect this push in your GitHub repository and it's going to do a redeployment. But if you actually try doing login there, it's going to fail. The reason it's going to fail is because the Railway environment doesn't have the necessary environment files and environment variables to actually run clerk.
What do I mean by that? Well, take a look at your project, you have dot environment dot local. So what you would have to do is you would have to copy all of these values, and you would have to add it to railway project.
And while you can do that manually, there's nothing wrong with that. Remember that we do have a railway CLI and the railway skill installed in cloud code. So I'm just going to go ahead and add use railway.
And I'm going to tell it to add environment. files to the project. I probably should have said environment variables instead of files, but I think the agent will understand what I mean.
You can see that it's reading the environment local keys and they are being redacted for the agent. So they won't accidentally leak into any cloud code backend session, any API call that's happening. And after a few moments, your agent should tell you that the variables have been added.
Now you can verify that by going inside of your project and inside of variables and this is also the place where you can add them manually in case the agent failed for whatever reason you can always open the raw editor just copy your environment local and paste it here update variables and click redeploy but there's something interesting happening in my deployment so it looks like it's failing at first I would understand if it fails because we had the missing environment variables right but even though my agent did a redeployment It failed again with a very cryptic message.
Cannot replace to directory. And then it actually talks about Claude's skills. And then I figured out what it is.
Skills sometimes include small versions of your Next .js application. I mean, like a Next .js template to explain to the agent how this is supposed to work. And sometimes these deployment scripts can recognize those files and think, oh, this is the application.
This is what I have to bundle. and build and compile. But that's not true, right?
And it thinks that we have multiple Next .js applications in our project. And surprisingly, that's what my agent also told me. So it told me why the deploy failed.
And it actually recognizes that the likely fix is to keep .cloud and .agents out of the build with a Docker ignore. So that's exactly what I'm going to tell my agent to do and what you should do as well, regardless, because this is a very good thing to have. going to show you the exact file that's going to be created because of this it's a very simple file dot docker ignore and inside you should have dot cloud and dot agents so the exact two folders that have skills because if you take a look at like astro patterns i think somewhere in here you have templates which has its own package json and source and this confuses the deployment services like railway and whoever uses rail packs like netlify ever sell all of those actually get confused when they read templates because they look for package .json so if that ever happens to you this is a solution but with agents it's honestly pretty simple so I'm now going to tell the agent to stage commit and push so it's automatically going to redeploy this and there we go after a few moments your agent should tell you that the docker ignore fix worked and the railway deployment has finished with status success
And I can also verify that right here. So I found the railway skill and CLI to be quite useful because you never actually have to visit their dashboard. You can do the entire thing from your agent.
So if you postponed adding the CLI and the skill, I would highly advise adding that because we will have more and more environment files, I mean, variables in this project. And I will simply tell the agent, synchronize my local environment variables with the deployment ones. And it's just going to do it.
I don't have to do it. anything else. And then I'll just go ahead and verify that she can actually log in and log out on your production instance.
Now, the only thing left to do is to actually claim this clerk instance. Right now, there is no way for you to see how many users you have in your app. You cannot impersonate any users.
You cannot do customer support by changing their name or changing their email. And the clerk offers all of that. They are an entire user management platform.
So it's a shame not to claim your application, which you will be required to do anyway when you actually push this to production. So you can use the link on the screen to create a completely free clerk account.
Now you don't actually have to create any application from the dashboard. All you need is an account because we installed clerk skills and we can simply tell the agent claim this project with my clerk account. Go ahead and open a new cloud code session and tell your agent to use the clerk skill to claim this keyless project.
Now your agent will ask you to open a website. So make sure to confirm that.
And now in here, log in with your clerk account and allow all the permissions. And once the login finishes, the agent will be notified and it's going to continue its work. You will now see a new application called AI teammates inside of your clerk dashboard.
And in here, you should see your first user. So you have now successfully claimed this account and claimed this project.
So you can use the entire Clerk B2B Suite billing organizations. everything it offers. Now, when you claim this project, the environment variables will most likely be changed, specifically the publishable key and the secret key.
So what I'm going to do is I'm going to use the railway skill and tell the agent to resynchronize the environment variables. Now, in my case, the agent told me that nothing needed resyncing, meaning that all six variables already match what we have here in environment local and what Railway has in its environment.
So in that case you don't have to do anything. If you want to you can still always copy, go inside of Railway and paste it there and redeploy. But looks like Clerk claimed this project using the exact same keys.
Now it's time to add backend to this application. The only problem is, our agent doesn't know how to build a backend. It certainly has some information in its training data, but it would be better if we could provide our agent with a set of reliable primitives and then let it build from that.
Think of it like ShadCN, but for backend. That is exactly what Neon is becoming.
for database only. But since then, Neon has expanded into a complete backend platform with authentication, object storage functions, and an AI gateway, which means that this time we can simply tell our agent, add me a database, but also add me a storage, add me an AI. gateway.
These were things we previously would have to use different services for. So before we add Neon skill and instruct our agent on how to add many useful backend primitives, let's first create a Neon account. Once you have an account, go ahead and run the following command.
Then choose the option recommended setup. The agent will ask you to open a website, so make sure to confirm that. Now go ahead and log in with your new account and make sure to authorize the Neon command.
line interface. Now go back to your terminal and choose Create a new project.
Now give your new project a name. I'm going to use AI teammates. When it comes to choosing a region for your Neon project, it's important to choose the one which has the backend primitives available.
If you go inside of the Neon documentation, for example, for object storage, there is a small note telling you where object storage is currently available. So that includes US East 2, US East 1, EU Central 1. and Southeast 1, all of them under AWS.
So I'm going to be choosing US East 1. And once the setup is complete, you will be able to see your new project inside of your Neon dashboard. Once you're here, make sure that you verify that you can see Object Storage, Functions, and AI Gateway.
This means that you are in the correct region which has those primitives enabled. Now, let's install the skills necessary so the agent knows how to interact with Neon. As always, make sure to reload skills if you have an active Cloud Code session and then open the skills dialog and search for Neon.
You should see both Neon and Neon Postgres skill available. Now, it's a good idea to run stage and commit. get rid of these 17 untracked changes.
You can now tell your agent to set up Drizzle ORM with Neon. Let's start by going through the package JSON to review the result. You will most likely have some new scripts like Database Generate, Migrate, Push, and Studio.
Besides that, you will have some new dependencies like Drizzle ORM, Postgres, and some dev dependencies like Postgres Types and Drizzle Kit. A new important file you should have is the Drizzle config file. Besides that, you should have a new folder, database, with index and schema.
Now, your agent might have populated the schema. It doesn't really matter because we will instruct the agent to define the schema the way we want it to be. Now, tell your agent to add a simple to -do list schema and push to database.
Or if you already have a schema, you can tell your agent to replace any existing schemas. Your schema file should now be modified and include a to -dos. table.
Obviously, this will later be replaced. I just want you and me to have the same table so that we can verify that the same is actually pushed onto the Neon platform. So you don't have to have the exact same fields like user ID, title, etc.
But something that indicates that this is a to -do list will do. just fine. Now go inside of your project on Neon and find the Postgres database and click on Tables.
And if you can see the To Do's table in here with all the fields in here, it means the push was done correctly. Now I would recommend telling your agent the following. If you've done any migrations, remove all artifacts from it.
Then add a rule to Agents .md to only perform database push and never migrations. And also remove the migration script from package .json.
You should now have a new entry inside of your agents .md that looks something like this. It's basically an instruction for agents to avoid using migrations. Now, migrations are not bad.
In fact, they are preferred. But when it comes to developing a project, they can be quite annoying, especially if conflicts arise. And it's just gonna spend your time and tokens.
The rest of the files are pretty much unchanged. except the package .json. I explicitly told the agent to remove the migration script because I don't want any new agent to accidentally look at that script and think, oh, I should probably use this.
Now, besides instructing our agent to not do migrations, I would also instruct it to never do backwards compatibility. So tell your agent to... Update Agents MD with the fact that this is a development project, meaning there is no backwards compatibility and the data is worthless.
Prefer data loss. Never backfill, migrate, or preserve existing rows. And never spend effort on keeping old data working.
Every schema change means a clean slate. I would highly suggest running this prompt and updating agents MD according to this rule because agents are notorious for backwards compatibility that is unnecessary because they treat every project as a production project and they think that every change you do has to be backfilled and backwards compatible.
And that makes very weird. and confusing code, which is often associated with AI slop. Do be careful when you eventually reach production.
At that point, you should most likely remove those rules from your agents .md so they don't drop your production database. And now you can tell your agent to stage commit. push and resynchronize railway environment variables because if you take a look at your environment local you will see that we have some new variables like database URL, database URL unpooled and neon branch and all of those need to go into our railway instance.
The deployment will most likely be successful and there isn't anything we can really test because we simply added the configuration files and environment variables. Now let's tell our agent to replace everything that we have in our schema with a single bots table.
Each bot should have an ID, user ID, name, avatar, job, instructions, sandbox ID, and a created at property. What's important is that for sandbox ID and user ID, you mention to the agent that it's a third -party ID.
This lets the agent know that that ID property is going to be populated by a third -party service and that it shouldn't create an additional table like sandboxes. or users. Another important thing for user ID is to tell the agent to index it so the queries load faster.
Now my choice of IDs is nano ID which are significantly shorter than the default which is UUID. So it's my recommendation to you to tell your agent to use nano ID for the IDs or any type of seed and then run the prompt. Now go inside of your schema and verify that you have the ID.
user ID, name, avatar, job, instructions, sandbox ID, and created at. And also verify that you have an index on the user ID. You will also have a new dependency in your project, nano ID.
Now tell your agent to perform a database push. Instead of your neon console, where you previously had the to -do table, you should now have bots table, and the to -dos table should be deleted or dropped.
You can now tell your agent to stage and commit. Now that we have the proper bots schema we can create a bot form and I want that form to open from this screen right here but I want to change from what it currently is into something like this. I've prepared a completely free Figma file for you where you can find various states of this application including the forms which we're going to start building very soon.
You can use the link on the screen to get access to this Figma. Since we're going to use the root page to open the creation form, we need to ensure that only logged in users have access to that page. So tell your agent to use clerk skills to protect the default page.
Now go ahead and try logging out. Instead of seeing this page you should immediately be redirected to the sign in page. And if you try to manually visit localhost 3000 the exact same thing should happen.
So only logged in users can now see this page where we are going to add this creation form right here. Now head into our Figma file, find home empty variant and simply take a screenshot of this state right here.
Now go inside of your agent and tell it to replace the page content using the empty primitives composition to achieve the look in the image attached. For the actual avatar, Empty Media, referring to this smiley face right here, simply pick a random image from our project, like a logo or a favicon. Do not build one yourself.
Use Shadzian skill. And then simply paste the screenshot that you took. Your page should now look pretty similar as to our empty page.
in the Figma file. The only problem is our agent has no idea how to generate avatars like this. So let's give it access to a completely free and open source avatar library.
In its playground, you can explore various avatar styles. I personally like this one. because I think it looks the closest to Grokbot avatars.
You can then go inside of the documentation, choose the section for AI assistance and copy their content for agents .md. Then go inside of your agents .md and paste the content. Now open a new Cloud Code session and tell your agent to create a chat avatar component inside of components chat avatar using Dicebear's gaze style.
So in case you chose a different avatar style, simply change the name here. The component should take a seed prop, an optional animated prop and a class name prop so callers can resize it and it should render a 32 pixel next image. Install whatever packages it needs.
Once you have a new component, you can tell your agent to replace the content within the page, specifically the empty media, which is what this should be, with our new chat avatar component and give it a different seed on every refresh. If you go back to your page, you should have a different avatar within the avatar style you defined on every refresh.
And now this page should exactly match what we have in our Figma file. You can now tell your agent to stage and commit. Now let's go back inside of our Figma file and let's take a screenshot of the bot creation dialog.
Then give the screenshot to your agent with the following prompt in a brand new session. Generate an insert schema for bots table. from our schema file using the drizzle validation libraries.
We tell it to do this first so that it creates a source of truth for the forms to validate upon. Then develop a create bot server action using that insert schema from above. And we also tell the agent to use clerk utils to extract user ID.
I also tell the agent where I want that create bot server action developed. Then I tell the agent to develop a component's BotDialog component using ShadCNDialog primitive and form composition using ReactHookForm and Zod to match the image attached which refers to the screenshot we just took. And then I tell the agent to wire up that new Bot dialog to our page Create Bot button.
And I finish it up with Use Shadzian skill, Neon skill and Clerk skill. Now go ahead and click on Create a new Bot. The dialog that opens should resemble the one we see in the Figma file.
In here you should be able to shuffle for a different avatar style and you should also be able to use the presets to populate some of the information. I'm noticing that my presets are not populating the name so I will go inside of the code to verify whether that's on purpose or a bug.
But for now I'm just gonna give this a name of Antonio and I'm gonna click on create. bot. I can see a toast, Antonio is ready and now I'm gonna go inside of my Neon console and verify that I can see a new insert in the database and that is true, meaning that our form officially works.
So why did we go through the trouble of explaining to the agent to first develop an insert schema, then develop actions and only then develop a bot dialogue? Why didn't I just take a screenshot and tell the agent just wire all of this up.
Well, you could do that but that is also how you create AI slop. That is that infamous bad code that is generated with AI. By giving the agent some primitives at the beginning, it will almost certainly create better code.
And that is something that is true from the start of this project. You will notice a pattern. Every time we start some feature, we see if we have any existing primitives.
That can be a set of components like ShadCNUI or maybe a skill like Clerk or NeonSkills. And then we tell it to implement a feature. So I took the same approach with implementing this.
I first told the agent, okay, I know that we have this bot stable. I know that we are using DrizzleORM. So why not use...
some type of drizzle validator util to generate an insert schema. Because now this insert schema is the source of truth for any form that wants to create a bot. Otherwise, if I didn't specify this, it will probably still work.
I have no doubt about that. But it would most likely define the schema within the bot dialog component directly. And that causes drift from the schema definition and the bot creation form.
So if I changed any of the properties here from required to not required or vice versa, I would have to also do that in the bot form. But because we reuse the bot insert schema, which we defined once inside of our schema file, we never have to worry about drift again. And by looking at the bot dialog, and specifically looking at the presets I found out that the name is not populated so it's not a bug.
My agent purposely made it so that when I choose the preset I am the one who chooses a name. So you can decide whether you want it to be like that. or not.
Now it would be annoying to have to explain to the agent how to develop a form every time. So how about we simply use this conversation and tell the agent to update Agents .md with rules on how to develop forms based on this conversation. And now all of this useful information will be remembered by the agent and when we encounter a new form we will very simply just tell the agent implement a form for this entity.
If let's say we have a new table in our schema. We wouldn't have to tell it once again to first generate a schema using DrizzleValidator.
We wouldn't have to tell it to use ReactHookForm with Zod. It has all of that information inside. You can now tell the agent to stage, commit and push all changes.
We don't have to change any environment variables because this was mostly just bot creation. We can now create new bots but we still can't communicate with them. In order to achieve that we need to create an chat record for every bot that we create.
We can then list those chat records in an application sidebar. And once the user clicks on a chat, they will get redirected to the conversation ID. This way, Bots can exist independently from chats and chats can also become group chats by adding many bots inside.
Let's tell our agent to add a new chats table as well as a chat members join table and the relations for all the tables that we're going to have in our schema. Chats table will have an ID, user ID, A kind, which is an enum, which can either be direct or group.
Name, which is optional, and it's going to be null for direct chats, in which we're going to use the bot's name directly. But for group chats... we will be able to give the group a name.
Last message preview to display what's the last message sent in that chat. Last message at which is a required and defaults to now so we know whether a new message has been sent and they created at property which defaults to now. Then we're going to have the chat members join table.
Each chat member should have a chat ID that it belongs to which should reference the chat table and it should cascade on delete. Bot ID which should be indexed references the bots table and also cascades on delete joined at and a primary key which is a composite key of chat id and bot id together and finally we define the relations a bot has many memberships a chat has many members and a chat member has one chat and one bot you should now have a new chats table which has an id user id kind which corresponds to a Postgres enum that can either be direct or group, an optional name, last message preview, last message at, created at, and an index for the user ID.
You should also have a chat members table which has a composite primary key for chat ID. and bot id and you should have bots relations chat relations and chat member relations now tell your agent to perform a database push stage and commit inside of your neon console besides the bots table you should have chat members and chats table the only problem we have now is that when we create a new bot it doesn't automatically create a new conversation or chat record so i just pushed a new bot and you can see that my chats are still empty but I have a new bot.
So now we need to modify our bot action to also create a new chat record and a chat member insert. We can simply tell our agent to modify actions bot. to batch create a new bot, new direct chat and a chat member.
Your agent should either use database transaction or database batch for this. Both work as long as you can see some kind of encapsulation between creating a bot and a chat and inserting the chat members. This way, a bot cannot exist without its equivalent chat.
and a chat cannot exist without its equivalent chat member insert. That's why we are doing this within a batch or a transaction so we don't have any orphaned bots. I would now recommend going inside of your Neon console and deleting all of your bot records so you can try and test whether this works on a clean slate.
So once again try and create a new bot and this time It should also create an equivalent chat and chat members record. There we go.
All three records have been added. You can now tell your agent to stage, commit, and push all changes. We are now ready to implement the chat UI.
This is what the chat UI looks like. We have an application sidebar which offers the user a list of chats they have with their bots as well as the group chats. Every chat features the bot avatar, bot name, last name, message preview and when it was sent.
Clicking on the chat should redirect to the chat ID page where we are further going to develop the functionality and UI for conversations. Inside of this application sidebar, we also have a button to open a command dialogue where we can search for all chats, a plus button to create a new bot or a group chat, and down here we have controls for signing out.
So we have to start by cleaning up what we currently have, which is this header with our... user button and sign in sign up controls. So I'm going to start by asking my agent to find and remove the header that renders the sign in sign up and the user button when logged in.
You can see that now my login screens no longer feature that redundant header and instead when the user is logged in they are simply taken to this welcome screen. The only problem is the user cannot log out from here and this is where the sidebar comes into play so that's what we're going to develop. next.
The problem right now is that our page where this hello world is defined is on the same level as our sign up and sign in pages. So if we just tell our agent okay add a sidebar to this application.
There's a chance it might try to develop the sidebar within the root layout. That would mean that once again, our sign in and sign up pages would have the sidebar, which is not what we want. This sidebar is intended to only show for logged in users.
So how exactly do we separate this page from our sign in and sign up routes? We do that with the concept of route groups, which is a feature in Next .js, which is our framework offers.
It's a convention that lets us organize routes by category or team. This is how the folder structure will look like. We're going to create a new folder in parentheses and we're going to call it dashboard and then inside we're going to move our page file which is right now standing directly inside of the app folder.
And one of the use cases is opting specific route segments into sharing a layout while keeping the others out. So that is specifically what we need.
So I'm going to tell my agent to move the existing page to a new dashboard route group. Then I'm going to tell the agent to add a new layout to that new route group. Then register a sidebar provider inside the new layout.
Develop a completely empty UI sidebar primitive inside a new components app sidebar component. Register the new app sidebar inside the layout inside the sidebar provider. And I'm going to finish it up by use ChatCN skill.
Your application will now have a sidebar. Keep in mind that if you are zoomed in or if your screen is too small it might be in collapsed mode. So make sure to expand or zoom out so you can actually verify that you have a sidebar.
Now I'm gonna tell my agent to render the clerk's user button component inside our new app sidebar in the footer. Display both avatar and the user name in the user button. Render the user button as a sidebar menu button and use the user button appearance prop to match the design and spacing of other sidebar menu items and buttons.
We can also tell the agent to use the clerk skill. Now most likely Next .js developer indicator will be right over the user button. So you can just tell your agent to turn off the dev indicator inside of the next config.
You should now be able to use the user button within the application sidebar. From here you can sign out which should redirect you to the sign in page. And once you're logged in you should be redirected to the page with the application sidebar and your user button controls.
Now one annoying thing about my user button is that if I click right here in the empty space, nothing happens. Looks like the clickable element is only this part right here.
So I'm going to tell my agent that the clickable area of the user button should be the full spacing of the sidebar menu element and not just the user button. Obviously, these are just UI improvements. You don't have to obsess over this and make it exactly the same as mine.
I just want to show you how I communicate with agent. how I explain it to the problem and how I tell it what I expect. So with that previous prompt, I can now click in this empty space right here and the user button activates, which in my opinion is just a little bit better than having to click directly here.
But I can notice one more problem. When I hover over here, there should be a subtle background change, which doesn't happen. When I hover over a button, you can see that there's a subtle background change.
The same should happen here, but it doesn't. So first, I'm going to tell the agent the element is now clickable throughout its entire spacing, but there is no hover effect over the sidebar menu element that renders the user button. The behavior should be exactly the same as if this were a regular sidebar menu item with a button inside.
And also change the user avatar to 1 .5 rem. You don't have to do this if you're satisfied with how yours looks like. you how i communicate with the agent to get the result that i want so if i try hovering over the user button you can see that it's now highlighted exactly as this button right here which means it's going to match all other elements in our sidebar that we add in the future once you're satisfied with how your user button looks and feels go ahead and stage all and commit now i want to add this plus button into the sidebar header so what i'm going to do is i'm going to find home new menu screen in the Figma file and I'm carefully going to screenshot just this.
I purposely don't want to give the agent too much information because then it's going to see the characters, it's going to see a list of chats, it's going to see this search button and it's going to try to do something else. So instead I'm going to tell my agent add a sidebar header to the app sidebar. It should be a justify end plus icon button that opens a drop down menu.
The create a new bot should open our existing bot dial. this one and the create the group chat shouldn't open anything and then I'm just gonna paste this screenshot right here so it sees the icons it sees the options and it sees the position of the button you should now have a plus button which opens a drop -down dialog and when you click on create a new bot It should open the same bot dialog that you can open from here.
The only UI problem I don't like here is that it made it highlighted by default. And it's because of this screenshot right here. This button is actually in hover mode in this exact moment.
Luckily, the fix is very easy. Tell the agent to change the plus button to be a ghost variant. So only when hovered should its background be visible.
And that should give us the exact result we want. By default, there is no background. only when we hover over it there's a subtle background which opens the drop -down dialog.
Now I'm gonna tell the agent to implement this list of chats right here. I'm purposely going to do this before I do this because this search bar doesn't really have a purpose before we establish how to load this list initially. I'm gonna paste the screenshot to the agent and I'm going to tell it to add a list of users chats inside a new sidebar group inside sidebar content inside app sidebar.
Use the chat scene skill for proper sidebar menu item composition. Instead of icon, use the chat avatar component for each chat's respective bot. And I'm going to tell the agent Ignore group chat functionality for now.
Simply fall back to the first bot that the chat has in the chat members. Develop a queries bots .ts query for this. Make sure to scope it to clerk's user ID and I tell the agent use clerk skill.
And also add datefns package for any date string manipulation which are things like two days ago. You should now see a list of your chats using the bots avatar in the sidebar. content.
What I can notice immediately is that my agent added a placeholder, no messages yet, in case there is no last message preview. So I'm just going to tell the agent to drop that. Now go ahead and try creating a new bot.
Chances are that this doesn't immediately add it to the sidebar. Perhaps it does, maybe it doesn't. If yours didn't do it, tell the agent to refresh the query whenever you add a new bot.
So in my case it already worked. Maybe the agent did this in this run. Maybe it did it before but I'm going to show you what you can tell the agent in case your agents don't immediately appear in the sidebar.
Something like this would be sufficient for the agent to understand that it needs to reload the query. Now whether that's directly in the server component or via API that's something agent will know but what's important is that every time you create a new bot the sidebar gets reloaded.
More specifically the query gets reloaded and then you can see your new chats here. Once you're satisfied tell your agent to stage all and commit. Now I'm gonna tell my agent to implement this search button right here.
And yes, it's a button even though it appears to be an input. Then I'm going to tell the agent to add this search button to the app sidebar header. below the create menu button.
The search button should visually appear as the input component so copy its default classes to this search button but keep the default hover effect of the button. The button should open a command dialog that renders a list of users chats with the chat avatar. Same as in the sidebar content and once again I tell the agent to ignore group chats for now and simply render the first bots avatar in that chat.
The command dialog should also be open via command or control plus the letter K and you can use the built -in Shatian components for the keyboard shortcut effect. And I'm gonna end the prompt with use Shatian skill.
So the reason I'm repeating quite a few things here is because I am in a brand new Cloud Code session so it doesn't have the information that I told it before. You should now have a search button which opens the command dialog and displays the same chats that you can see here. The same command dialog should be openable via command or control plus Now I'm going to tell the agent that selecting a chat from the command dialog should redirect to chat's chat ID.
Same as clicking on the sidebar menu items. Right now, chat's chat ID route doesn't exist. Leave it like that.
I'm saying this just so the agent doesn't go ahead and implement that route because we're gonna do that later. Clicking on an element inside of the sidebar should now redirect you to a 404 not found. Same as clicking inside of the command dialog.
You can now tell the agent to stage all and commit. Now I'm gonna find the sidebar collapsed view. and I'm going to take a screenshot of this entire collapsed sidebar so the agent has a better idea of how it's supposed to look like.
I'm going to paste that screenshot to the agent and I'm going to tell it to enable the collapsed icon mode instead of the app sidebar. Collapsing should be done using the sidebar rail. Each section from the expanded sidebar should be visible in the collapsed mode.
Each button in the collapsed mode should be the same size. Use ShadCN skill. You should now be able to collapse your sidebar by clicking on the rail right here.
One thing I immediately notice is that my search button has a visibly different design in comparison to all other buttons. So I'm going to take a screenshot of this state right here and I'm going to tell it to the agent. The search button has a visibly different variant than other buttons when in collapsed mode.
And there we go. Now we match the design almost exactly. Make sure that everything still works as expected.
Now you might notice that every time you refresh, your sidebar state will be reset into expanded mode. So if you want to persist the state, tell your agent to persist state with a sidebar cookie. And if you try it now, collapse, refresh, and it should stay collapsed.
You can now tell your agent to stage all, git commit, and And you don't have to worry about mobile responsiveness for now. We can do all of that at the end of the project.
Now that we have the sidebar in place, we are ready to start developing the conversation UI. This will include the header that lets us know which agent we are currently talking to and this desktop button, which will eventually open the desktop preview. Besides that, we're going to build the middle content, which is the actual conversation.
Our message, bot's response, and finally this composer at the bottom. So I'm going to start by telling my agent to register a chat's chat ID page inside of our dashboard and then render the current chat ID from the URL inside a paragraph, nothing more. So right now, if I click on any of the chats, you can see that I get redirected to chat's chat ID, but it's currently not found.
So after this, we're going to see the exact ID displayed and it's no longer going to be not found. And also it's going to persist this sidebar right here. I can now click.
on any of my chats and I will be redirected to chats ID with the ID displayed. I can also switch between chats with the sidebar persisted. The reason this persists is because we explicitly told the agent build the chat page inside of the dashboard route group and our dashboard route group has the layout which renders the sidebar.
That's why it was important to explicitly mention that. Now I'm going to tell the agent to ensure that the active chat is focused inside of the app sidebar. Because right now I can't really see which agent I'm talking to.
I'd like it to be highlighted like this when it's active. There we go. Now I can switch between my conversations and I can see exactly which one is currently selected.
So now I want to develop this header right here. So we can either take a screenshot by zooming out. and selecting it like this.
Or I want to show you one little trick that we can do. So preferably choose the one where the desktop button isn't focused because you can see that on one Figma screen it's focused and then the agent will implement the default state as focused.
So if it's hard for you to take a screenshot like this you can always like double click until you select this element and then copy as PNG and then you can paste it to the agent and you can see it's very very hard to see but that's the exact screenshot you would have taken anyway. So now I'm going to tell the agent To start by developing a chat get query inside of chats queries .ts and scope it to clerk's user ID and it can use clerk skill for that.
then load the chat by its ID instead of our new chat ID page. Then, develop a component's chat header, which we paste a screenshot of, according to the screenshot, and use the chat avatar component to display this chat's bot's avatar. Ignore group chats for now, simply use the first chat's member information.
And the desktop button on the right end of the chat shouldn't do anything. So, I told my agent to develop the query inside of chats queries which isn't exactly correct because our current pattern is a queries folder and then inside we have the entity luckily because we already have an example for bots my agent decided to revert that and it put the query inside of the queries folder chats .ts now this isn't important functionality wise but I just want to show you how it's very important for agents to have some existing patterns and primitives and why we were very strict in the beginning and becoming more and more lenient now with our requests because we have enough information in our code base for the agent to do the correct thing.
In case your agent did explicitly as you told it and now you have queries all over the place you can simply tell your agent can you consolidate all of my queries in one place. And you should now see a chat. header for each of your conversations with a desktop button that currently does nothing.
To make this chat header more useful I want to turn this bot's avatar and name into a button which opens a dialog like this but instead of creating a new bot it's used for updating the current bot. So I'm going to start by taking a screenshot and then I'm going to add it into a new Cloud Code session. The first time we created a form, we told our agent to update the agents .md and give it the rules on how to develop forms.
So in here, it has all the information it needs, how to create schemas, how to create actions, and how to validate forms and which packages to use for the forms itself. So all we have to do is tell the agent to make this a button that opens an edit update version of the bot dialog and adhere to form rules or form creation rules.
You should now be able to click on this button and try changing the face and the name of your bot and click save changes. You should then see updates both in the chat header and in the sidebar.
I'm also going to tell my agent to add a delete button in the new edit dialog with confirmation before the delete actually happens. So I want this to be a one -stop shop for updating or deleting an agent. You should now be able to delete a bot from the edit dialog.
Now it's a good time to tell our agent to stage. all and commit. We can now navigate between different conversations but we still can't communicate with the agents.
To do that we have to implement the conversational UI which includes these bubbles and this input composer at the bottom. We're going to achieve this with AI elements. which is another set of primitive components built on top of ShadCNUI, which makes them directly compatible with our existing components.
It's also deeply integrated into AISDK, which is an open source tool which we are going to use to communicate with various AI providers. Let's start by running the following command. This is going to install all necessary dependencies and setup components needed to develop conversational UI.
Once this setup is complete you will have a few new dependencies inside of your package .json and a bunch of new components which you can use to compose AI chat. Now let's also install the skill to teach our agent how to use all of those new components we just got.
You will now have an equivalent skill and documentation for each component you installed, meaning that your agent has a very deep knowledge of how each of those new primitives is going to be used. This is a pretty good place for a checkpoint, so tell your agent to stage all and commit. Now that we have the front -end prim...
set up, let's install AI, AISDK React, and Zod. We should already have Zod installed, but just in case your agent never added it, now is a requirement to have it. And now let's give our agent a skill so it knows how to use the AISDK.
Now once again, tell your agent to stage all and commit. You now have to choose which AI provider you're going to use. Depending on your AI provider, you're going to have to add its respective API key inside of your environment .local.
To find the exact API key that you need to add, go ahead and find your provider within this list and then look for the API key. Here's an example for OpenAI, Anthropic, Google, and Grok. To get an API key, you need to visit your provider's respective platform.
For example, OpenAI API platform. Cloud Platform, XAI Platform or Google Gemini Platform. In case you want to give your users the access to all of these models and all of these providers at once and instead of having to add credits to all of the platforms independently, you can simply use Neon's AI Gateway.
All of these platforms feature a relatively similar way to create a new API key. If you decide to use the Neon AI Gateway, keep in mind that you will have two variables, one of which is the base URL and the other one is the secret token, which is the equivalent to an API key in all of those other providers. So if you chose the direct provider, you will simply have one API key and one environment variable and if you chose AI Gateway, you will have both base URL and the secret token.
Once you've set up your API keys, make sure to install the respective package for your provider. Here are the examples of OpenAI, Anthropic, Google, XAI. and finally Neon Gateway.
I'd always recommend taking a look at the documentation yourself so you can verify that the package I've shown you is still active and not deprecated or replaced. You should also be aware that most of these providers will require some amount of credits added to their platform. The only exception I am aware of are XAI, which offer a free tier, and Google AI Studio, which offers free API keys.
Do keep in mind that sometimes these free offerings come with very limited models and they are most likely unable to do the things we need our agents to do, which is complicated. tool use and computer use.
So my advice is, if possible, do add credits to whichever platform you're going to use because that automatically gives you access to the better models. You can now tell your agent to use AI SDK and AI Elements skill to set up an AI chat instead of our chat ID page. Tell it to not implement database persistence.
Create the route handler. and wire up the UI. Use the simplest setup possible for an AI chat.
And then tell your agent, use your installed AI SDK provider. In my case, that is Neon AI SDK provider. In your case, it might be Anthropic or OpenAI, basically whichever provider you have installed.
Your chat page should now have a composer at the bottom. So go ahead and send your agent a message. Very shortly, you should see streaming coming back token by token with the answer from your agent.
The implementation itself should be quite simple. You should now have some new component rendered inside of your chat page. In my case, that is the chat component right beneath the chat header.
The chat component itself should use the AI elements which we installed, which is used to compose the entire UI. render the prompt composer, etc. And the actual logic should happen within a route group.
In my case, that is app API chat. And what's cool here is that you can find your provider inside of the stream text where the model is being chosen. So if you use Anthropic or OpenAI, you can only choose between models from those providers.
But because I chose Neon, I have access to all Anthropic models. all Gemini models, GLM models, GPT models, Grok models, Kimi models, Meta models, Quan models, Lama models, basically all Frontier models are available to me and that's the benefit of using something like Neon AI Gateway. And best part of it all is that pricing is exactly the same as if I were to set up an API key with each of these providers directly.
It's all just consolidated onto one platform. neon. You can now tell your agent to use railway skill to add new environment variables and then stage all commit and push.
We can now finally send messages to our agents but we are met with another problem and that is durability. Even though we explicitly told the agent don't implement any kind of persistence, let's say we told the agent implement persistence in a database, that still wouldn't exactly fix the durability problem. Let me give you an example.
Tell your agent to write you a long story. Now imagine this streaming which is being returned to us is actually stored in the database. What happens if I refresh?
in the middle of that story? Do you think it would still be saved to the database or do you think the agent would be interrupted? The answer is the agent would be interrupted.
This is why durability is so important. That brings us back to this diagram right here. What actually happens when we submit a message?
The first thing that happens is Chat UI submits a message into the Next .js application RouteHandler. Now, RouteHandler is great for a small demo or for very short responses, but the problem is it's by definition part of the Next .js application, which means if I refresh my tab, route handler breaks the connection.
If I redeploy my Next .js application, route handler once again breaks the connection. If anything bad happens to this instance, the agent is interrupted, which is completely unacceptable for long -running tasks, which are the ones our agent will be doing. It's going to perform tool use and computer use and actually do things on the machine.
That is why we need to implement this. A durable stateless worker. We need to use the route handler only to invoke this task.
which will run completely independently of our Next .js application. And in here we can invoke the agent, in here we can implement tool calls, in here we can call the sandbox and let the agent run as long as it wants. This is where trigger .dev comes in.
We are now going to install dependencies and skills to teach our agent how to develop stateless machines where we can run our agents using trigger .dev AI chat. It's a good idea to create your account first so that you can easily connect it once you run the CLI.
I'd also recommend creating a project from the dashboard. I'm going to call it AI teammates. Now let's run the following command to get started.
Make sure to select both MCP server and agent skills, which are important so we can teach our agent how to write trigger .dev code. Then make sure to select your coding agent. I'm going to select both Cloud Code and agents .md directly.
Now go ahead and choose each of these options. You will most likely have to do it twice if you selected multiple targets like I did, Cloud Code and agents .md. If you chose to install the MCP server, simply select the coding agent you're going to use.
I'm going to choose the project option for the scope of the MCP server. For the option of how you want to set up your project, I'd recommend the first one. which is scaffolded now with the CLI.
This means you will have to log in, connect your account, create the trigger config and an example task. This is pretty deterministic and both of us are going to be on the same page. So choose that option.
Once you've authorized your login, go ahead and choose the new project you created, AI teammates. For the folder path of the trigger .dev directory, I'd simply choose trigger. The default is source trigger, but because we are not using the source folder, it really doesn't make sense to create one just for trigger.
So simply replace the placeholder with trigger. For an example task, pick simple hello world.
Inside of your project you should now have a new trigger folder with example .ts inside. In here you should see a simple hello world task which simply logs hello world, waits for five seconds and then completes the task. Now go inside of your dashboard, find API keys in the sidebar and click new API key.
Give your new key a name. For simplicity sake you can make it never expire. Give it full access and click create.
API key. Copy the value and then add it to your environment file under the key trigger secret key. You are now ready to start the trigger server which runs independently of our Next .js server.
You can use the following command to get it started. Now that your trigger server is up and running, instead of your dashboard you should see the exact task that you have defined within your trigger folder example .ts. Go ahead and find the test button.
If you want to you can give it some payload. I'm going to use a very simple foo and bar and I'm going to click run test.
What happens next is pretty uneventful but that's exactly what we want. This example task is purposely defined to be as simple as possible. to demonstrate that the agent can wait for 5 seconds and then finish the task.
But what's actually important is that this entire code was never running on our Next .js server. In fact, I don't even have my Next .js started. That's what I wanted to demonstrate.
This is the environment where we are going to run our agent code. A complete stateless machine separated from our Next .js environment which isn't prone to crashes, redeploy problems, or network connections. issues.
Now go inside of your cloud code and verify that you can find all of these trigger skills. Now before we actually move our AI SDK from a route handler into trigger, let's stage and commit all of our changes so it's easier to keep track of what's actually modified. You can now tell your agent to migrate the existing AI SDK implementation inside of the route handler and our chat component, which is the composition of AI elements, to trigger .dev.
chat agent and add transcript storage for persistence and scope that persistence to chat ID. So yes, trigger also comes with built -in persistence and transcript storage, so we don't have to implement that ourselves. I'm also going to tell the agent do not add any new features, tools, or metadata except those needed for the migration.
The implementation should stay simple. and then use trigger and AI SDK skills. You should now have a new task within your trigger folder called chat.
Inside you should find a very simple implementation of stream text which uses your AI SDK provider but this time not within a route handler but rather within trigger .dev chat agent which is now run in a stateless machine. Now make sure you have your Next .js server running and make sure that you have your trigger server running.
Now pick random agent and send it a message. Wait for an answer and then refresh.
This time both messages should be persisted and the persistence itself should be scoped to individual chat ID so different chats should have different storage. You can also test telling your agent to write you a long story and the moment they start answering go ahead and refresh the tab. Now it might seem that the agent has stopped responding But let's see what's actually happening in the stateless machine.
If you go inside of your runs, you will see that your agent is still executing. And in here, you can find the chat turn where I asked the agent, write me a long story. After a while, you will see that your agent did in fact finish the story.
and that is what's important. That is why we implemented this entire feature to implement durability because imagine telling your agent to book you a flight and then refreshing the tab in the middle of that task. No user would expect the agent to be interrupted just because they closed the application.
So with this change not only did we implement durability and persistence, but we actually went from an MVP to a production -ready AI chat. Now take a look at the ridiculously small amount of code change we had to do.
First things first, we got rid of the route handler because it's no longer needed for anything. We moved the entire stream text into the chat agent task directly using the same provider and model as we did in the route handler. You can now tell your agent to use the railway skill to sync and environment variables, stage all, commit and push.
It appears that our builds are failing and have been failing since the last chapter when we added AI elements. I just never noticed this. Lucky for us, these are just type errors.
So we can simply tell our agent to use the railway skill to debug and fix all of these issues. I'm going to tell my agent that deployment build is failing because type check fails. Resolve all issues and redeploy until it's successful.
I can now verify that the deployment has been successful, meaning that the agent has resolved all of the type issues. Even though the deployment is successful, it looks like the page fails to load. So let's debug why.
I'm going to go into the successful deployment and I'm going to click on deploy logs. In here, I can see what's actually happening. I'm going to take a screenshot of the deploy logs and I'm going to show it what actually happens when I load the page.
And I'm going to tell the agent that even though deployment is successful, the page fails to load. And I'm going to provide it with both screenshots.
Now try doing the following. Go inside of your production instance and open any conversation. And then shut down your local trigger server that's running.
So make sure nothing is running. And try and communicate with your agent. Let's see what happens.
The agent is infinitely stuck in submitting mode. That is because we're mixing a Next .js production instance who is trying to connect to our local. trigger server.
So we have to create a production trigger instance and then create the new trigger secret key and add that to railway deployment. And then the trigger server will be running independently somewhere else and not on our machine. So go back inside of your trigger dashboard and switch from development to production.
Now go inside of your sidebar and scroll down until you can find environment variables. Then go ahead and click add new. Select both development and production.
Then go inside of your environment file and copy everything inside. And then simply paste all of the keys here.
What's important is that you have the AISDK provider secrets in here. And then go ahead and save. Now go inside of API keys.
Just like we've created an API key for deployment, we need to create an API key for production. So once again, I'm going to set expires to never, full access. and create API key.
I will then copy this API key and then I'm gonna go inside of my railway project. I'm gonna go inside of variables, raw editor, I'm going to find the trigger secret key and I'm going to replace what is currently a development secret key here which we can see with the dev prefix and I will replace that with a production key and I'm gonna click update variables.
I'm then gonna go back inside of here, I'm gonna go back inside of my tasks and now you have to choose how you actually deploy trigger to production. The easiest way is manual. All you have to do is copy this and run it within your CLI.
Keep in mind that this command will only work if you added the correct environment variables. So make sure that you did that first and that you did it both for development and production. You can now also redeploy your environment variable change.
on the railway. So let's try this again. I just shut down my trigger server and I shut down my Next .js server.
I am back in my production instance. and I'm going to tell my agent, hello world. Let's see if this time we can get a response on our production instance.
If you go inside of your production instance, inside of your project, you will see that you have a new run executing. Now it's time for the fun part. We can finally add sandboxes to our agents.
And for that, we're going to be using Daytona. First things first, let's install the package. Next, let's find the skill to teach our agent how to use Daytona.
You can almost certainly find this in every tool's documentation. Simply find something like Agent Skills. and in here, you can find the exact command you need to run.
Now go ahead and create an account so that you can create a new API key. By default, Daytona gives you $100 in credits, which is more than enough to complete this tutorial. But if you want to, you can redeem coupon code ANTONIO100, which will give you an additional $100 in Daytona credits.
Now head into the API keys section and click Create an API key. Give it a name, and full access permissions.
Now add both API key and API URL into their respective variables within your environment file. You can verify whether the names for the environment variables are still the same by looking inside of Daytona TypeScript documentation. In here under configuration, I can find that the environment variables are Daytona API key and Daytona API URL.
Or since we simply gave our agents skills, You can just ask the agent, what are the environment variables I need to add for Daytona TypeScript SDK. Since we have quite a lot of changes by adding the skills, let's do a stage all and commit.
so that we can clearly see the changes we do going forward. Here's a fun thing that happened once I told my agent to commit. It actually changed my branch to feature Daytona SDK, even though in our agents .md, we explicitly forbid that.
So I'm going to tell my agent that we explicitly forbid changing branches unless the user requests so and force the rule even further and keep everything on the main branch. It isn't a big deal if the agent changes a branch, but I feel like it's just easier to have everything on. the main branch.
If you want to know the symptom of why this happened, it is because When we added the trigger in the last chapter it created a clod .md file and in here it simply added use trigger .devskills and I simply assumed that the agent was going to continue to read both agents and clod but it looks like if only agents .md is present then all agents will read that file.
But if I have an explicit clod .md, clod is going to read this file. So it's a pretty good idea for you to add agents .md reference inside of your clod. md file this way your agent will have to read both files regardless and that is why this mistake has actually happened.
Since I just modified a couple of files I'm once again going to tell my agent to stage all and commit. You can now tell your agent to use Daytona skill to create a sandbox for each bot that gets created. Use or add a sandbox id property to the bot schema to maintain the relationship between the two.
For now do not invoke the sandbox. Start it. don't perform anything on it.
We just want to create the sandbox on bot creation and delete it on bot deletion. Remember that once we created the bots schema, we added an optional sandbox ID. In case you didn't add it, the agent will add it because of this paragraph right here.
If you want to, you can also add it manually. So right now I have zero sandboxes inside of my dashboard and I've deleted all of my bots. So let me go ahead and create a new one and let's see if creating the bot will also create the sandbox.
The bot is created and the sandbox is created as well. This is the ID of the sandbox and if I go inside of my database table I should be able to find that exact sandbox ID relation under the property sandbox ID. Even though we don't have the UI to look at what's happening in the sandbox you can simply click on the sandbox within the dashboard and in here you can explore the file system or you can spin up a VNC desktop which will give you a preview of what we will eventually have in our application so our users can easily take over the bot's desktops.
This is the desktop that we are going to teach our bot how to use and which our users will be able to take over. Now tell your agent to use AISDK and trigger skills to give our chat agent base instructions about its name, job, purpose, and the sandbox environment it is in. Do not mention any use or tools yet as we do not have any functionality regarding computer use or any tool calling.
We just want to make our agent aware that it is within a sandbox and what its purpose is. The agent should generate something like this. Instructions which tell the agent their name, its purpose, and its job.
It should also include the description of the sandbox that it owns. In order to test whether the agent actually understands this, let's make sure our local trigger server is running. One fun fact is if you installed trigger MCP, you can simply tell your agent to start the trigger server for you.
I'm going to ask my agent, who are you? Very shortly, I'm going to get a tailored response based on the actual purpose and the job of this agent. And it also mentioned that it has its own isolated Linux sandbox, which we just gave it access to.
Now let's tell our agent to give our chat agent tools so it can perform computer use. Tell the agent that it's at liberty to add more or less, but this is the general idea.
A bash tool, read, write, and list files, mouse click, move and scroll, keyboard type and press, and then two different screenshot tools. One is a view screen, which should be used when the agent itself needs to look what's on the screen. That should never be rendered in chat.
And the other one is show screen, which is used when the user explicitly asks the agent what's on the screen, and that should be rendered in chat. Compose the UI inside of our chat component, which our existing AI elements are composed, like this prompt and the bubbles, to render tool calls. Use AISDK, Trigger, Daytona, ChatCN, and AI element skills.
Let's give it a spin. I'm going to tell my agent to open the browser, search for Code with Antonio, confirm any pop -ups for cookies. and take a screenshot.
There's a chance your agent restarts your trigger server, so just make sure that it's running. The agent should now start outputting its tool use. For example, right now it's looking at the screen and it actually tells you when it completes.
Now it clicks, looks at the screen again and performs all kinds of tool use until it reaches the goal I gave it. Eventually your agent will produce or should produce a screenshot. Now I purposely want to show you this example where my agent is not connected to the internet.
This is a Daytona free tier limitation and usually you have to upgrade to get access to higher limits and network access. If you use the token I told you at the beginning, which is ANTONIO100, you can simply redeem additional $100 in credits and also get network access. if you just redeemed the coupon then go ahead and create a new bot so a new sandbox gets created and then try with the network access one more time you can also connect to the vnc access at any point if you want to see what your agent is doing in real time and this time your agent should have full network access but once again If you don't manage to do this for whatever reason, if the coupon expires, you don't have to worry because the tool use you implemented works equally good with or without the network access, you just can't really demo it.
This is a pretty good spot for a checkpoint, so let's tell our agent to stage all and commit. Now let's tell the agent that the desktop icon button instead of our chat header, which is currently unused, should toggle a resizable component panel on the right side. The resizable panel composition should be done inside of the chat ID page.
So it encapsulates both the chat header and the chat content. I'm going to explain what I mean by this in a second. And nothing should be rendered within the resizable panel.
Essentially, when the user clicks on this desktop icon right here, I want the resizable panel to open on the level of the chat ID page and not beneath the chat header. So I want it to push the chat header completely.
It might be easier to just show you the design. Here it is. When the user clicks on the desktop icon, this right panel should push the entire content.
It shouldn't appear like this, right? It should appear like this. And we can end it with use ChatCN skill.
You should now be able to click on the desktop icon, which should open. a resizable panel and the resizable panel should, as the name implies, resize. We can now tell the agent that the resizable panel should render a Daytona VNC sandbox preview.
and clicking on the preview should open it in full screen using our dialog component. VNC inside the dialog should accept inputs and prevent the dialog from being closed by any hotkeys or inputs to prevent bad user experience. That is because the built -in dialog probably has some hotkeys like escape button or space which close the dialog.
But when we open this dialog, we are controlling the machine. We should be able to use all letters on the keyboard without closing the connection.
The VNC itself should only load once and it should be reused for both resizable panel and the dialogue. We add this sentence to prevent the agent from doing it lazily and so that you will have to wait for the VNC connection once the resizable panel opens and then one more time for the full screen dialogue. You can tell your agent to use the Daytona skill.
and I would recommend taking a screenshot of the content within the resizable panel from Figma if you have access to it. Let's give it a try. I'm going to go ahead and open the desktop panel and I can see that the preview has loaded.
If I click on it, it opens in full screen and from here I have full access. For example, I can accept cookies.
Now I can see a few problems with this implementation. Mainly that the sandbox preview is centered instead of being positioned at the top. So I'm just going to take a screenshot of that.
And I'm going to tell the agent that the sandbox preview in the resizable panel should be at the top. not centered. The sandbox preview is now loaded exactly where I would expect it to be at the top.
One problem I'm noticing after we implemented the resizable panel the entire page scrolls but the only thing that should be scrollable is this chat component. So not even the chat header nor the resizable panel should scroll with the chat. I'm gonna tell my agent that ever since resizable panel has been composed within the chat page the entire page scrolls.
But the only scrollable element should be inside of the chat component, specifically the messages. Everything else should stay in place. You can also interrupt your agent if you forgot to add something.
So I'm just going to add use AI elements scale. There we go. Now we have the exact behavior we expect.
The only scrollable element should be the AI elements composition where it renders the messages. The chat header, the prompt, everything should stay exactly where it is. Now I'm going to tell my agent to use the railway scale.
to add new environment variables to the deployment. I'm also going to tell it that the trigger secret key is different because it requires a different key for deployment. And I'm also going to add, add this info to agents MD so I don't have to write it every time.
And then stage all, commit, and push. My agent reminded me that I need to add Daytona API key into trigger environment variables. But I'm curious, since I have trigger MCP installed, Can I simply tell the agent to use the MCP to add it for me?
So it looks like the agent didn't do it through the MCP, but it still found a way to do it using environment variables API. which is really cool. So verify that inside of your production instance you have Daytona API key or simply add it manually the same way we added all other environment variables.
I'm now gonna tell my agent to deploy trigger for me. Once you verify that the railway has been deployed and you deployed a new trigger version, go ahead and try the whole flow again in production. You should be able to get the exact same results.
I ask the agent to take a screenshot of its machine and I also open the desktop panel from where I can take over the desktop in production. Every now and then when you start your Trigger server you might be prompted to upgrade. I would honestly recommend doing that as often as you can especially in development phase so that you always have the newest feature that Trigger has to offer.
and it's also a good idea to also install the new skills for that version. Once upgraded, your server will run as usual and you can tell your agent to stage all and commit. Now that we completed the entire configuration for individual agents, let's implement group chats.
That means we're going to have to enable this option right here, create a group chat that currently does nothing. What I'm going to do is I'm going to head inside of my Figma file and I'm going to take a screenshot of the group chat form example.
And since our project is very mature at this point and already has enough primitives and examples, we can actually one -shot the entire group chat feature. A one -shot usually isn't associated with exactly good results, but what I'm trying to say is we already taught our agent how to behave and how to write code. We already have examples of forms and dialogues.
We already have durability and persistence. So the agent actually has to only build on top of what we have and not invent any new complicated things. So let's start from the UI and slowly explain to the agent how we want the functionality of a group chat to work.
Develop a group chat form and dialogue and attach it to app sidebar header drop down option. create a group chat. Then create a group chat avatar which features up to three visible bot avatars and the rest is shown with a number.
Develop this with a layout where each item sits inside the square. Two items sit side by side overlapping, 3 form a triangle, and 4 fill a 2x2 grid.
The total size of this avatar should be the same as a single chat avatar, so when rendered in sidebar or chat header, the size taken up is identical. Register the new group chat avatar in all places where we hardcoded the first bot in chat members list before group chats were implemented . Then, let's start working towards the functionality.
How does the user choose which bot to talk to? For starters, we are going to do a very simple implementation. I don't want any complexity here.
We are simply going to modify the existing chat component with AI elements and change the input composer to feature an AI elements dropdown that lets the user pick which bot they currently want to talk to. So that is as simple as it goes, right? One bot per turn.
They won't be able to hand off work to each other. They won't be able to... decide who answers, the user will decide who answers.
We are purposely doing this in a primitive way so that we can build on top of this feature rather than having to solve it all at once. Then modify the trigger chat agent to do bot per turn type of response for group chats depending on the user selection from the UI. So this user selection should go through the transport metadata.
Do not implement any other complications, tools or complexity in regards to group chats other than the minimum needed to create a group chat, choose which bot to talk to from the UI and get the response from the same. bot. Then let's make sure to use AI SDK, AI elements and trigger skills.
And I would also like to add that group chats shouldn't have the desktop panel option. I don't want any complexity there as well. Users can always go into the individual chat and then open the desktop from there.
This is actually the exact same behavior that Grok bot chat group has. And then let's run the prompt. So the app doesn't crash.
That's good news. Now let's go ahead and create a new bot. I'm going to give it a random face and I'm going to create it.
Then I'm going to go ahead and create a group chat. I'm going to select both of my bots, give the group chat a name. click create group and so far so good.
First thing I notice is the group chat avatar. I can see that the layout I described is exactly what I would expect for two bots in an avatar and together This avatar takes up the same amount of space as this single avatar which is exactly what I wanted.
Inside of my composer I can choose exactly who I'm talking to. So I'm going to start by selecting the research assistant and I'm going to ask for its purpose. And the response I got is exactly what a research assistant would have told me if I directly asked it that question.
So now within the group chat I'm going to change to code reviewer and I'm going to do the same question. There we go. the agent answered I am a code reviewer.
So it successfully registered this change which was then transferred to the transport which got into the chat agent. So just like that in one prompt we got a high quality reliable group chat. While this isn't wildly functional because we purposely toned it down I purposely asked for a single bot per turn and no handoff functionality so this is essentially the same thing as just talking.
with bots inside of their individual DMs, it's a good start and it gives us the big group chat primitive which we can now expand upon. For example, it's a pretty good idea to have the bots avatar next to their response in group chats because how exactly do we know who answered what? And then the minimum upgrade I would do is a handoff tool.
The ability for agents to call a handoff and decide, okay, this task is better suited for another agent. So I'm going to hand off this user's prompt to that bot.
That would be the minimum. And then we would have the next level, which would be no selection of bot at all and simply sending a message and then let the bots decide. who does what.
The first thing I want to do before any of those upgrades is stage all and commit. Now I'm going to prompt the agent to render the bot's avatar next to its message in group chats in our chat component, which uses AI elements. Since I'm prompting this inside of the conversation where I already loaded AI elements, I don't have to explicitly mention that again.
I just thought you should know that this request restarted my trigger server, so make sure that yours is up and running. And there we go. When I asked a new question, I now got a response and the avatar.
The reason it didn't retroactively upgrade the previous messages is because they didn't have the metadata as to which agent answered it. So it added that as a new feature inside of the trigger dev chat agent. So only new messages will be able to get the avatar.
Let's try this again. So code reviewer, I'm going to ask to write me some code and there we go. We can now see a response coming from the code.
reviewer. We can now tell the agent to implement a handoff tool that bots can call in group chats if they deem the user's prompt is better suited for another bot. And I'm also going to tell my agent to also upgrade the bot's instructions for group chats to understand that they are within a group chat and to understand the tools they can use, etc.
All right, so I restarted my trigger server and I'd recommend you do the same. I refreshed my page and I'm going to select research assistant and I'm going to tell it to write me a hello world. in JavaScript.
I feel like this is a sufficient enough prompt to make it say, oh, I should probably hand this off to code reviewer. Maybe, maybe not. Maybe I will be wrong.
Let's see. So that actually didn't thread the needle as much as I hoped it would. So I'm going to ask, I need you to review some code for me.
Are you the correct bot? Perhaps that will kind of nudge it in the direction of a handoff. And yes, this was a failed demo, but obviously you can iterate over this, make the handoff tool more strict.
less strict. Perhaps I need to start the brand new group chat. There we go.
Handoff just happened. Okay, so I needed to be very explicit. And then the assistant researcher handed off to code reviewer.
This is a good place to stage all and git commit. So I want to add one more UI improvement which is to add a small badge component at the beginning of the user message in group chats with bot avatar and the bot name to know exactly which bot the input prompt was referring to at the time. So I'm going to send a brand new message because I assume this works in a similar way as the avatar and there we go.
So the metadata was now added and I can now see exactly to what bot the message was referring to. Though I don't like how it's at the top of the message, even though this doesn't necessarily look bad, but I would rather it appears like I tagged a bot, right?
You know, when you have the option to tag like research assistant, and then that turns into a nice tag. That's what I was kind of imagining in my messages. And that could be the next feature you implement, right?
Instead of this input composer dropdown, you implement a regex, which recognizes the add sign, lists all of the bots in the chat, selects it, and then adds it. That's also an option, right? The options are endless, limitless with AI agents, especially with a high quality.
codebase that we've generated so far. So I'm going to send a screenshot and tell the agent that the badge should be in the same line as the first line of the message, as if the user tagged an agent. The name can be truncated if too long for the badge.
So it actually didn't succeed to do it and my message and the agent tag is exactly the same. So I just sent the same screenshot one more time and I told the agent nothing was changed, still the same problem. Should I attempt by sending a new message or should have this update?
of yours retroactively update all their messages as well since it's only CSS and basically it looks exactly the same. So I'm going to prompt the agent one more time. Alright, so after this additional prompt and a refresh, it now finally looks as I would expect it to look.
You can now tell your agent to stage all, commit and push. and redeploy trigger if needed. Or if you are unable to use your agent to deploy, you can just manually redeploy trigger.
One thing that would be extremely useful is everything we communicate with the research assistant in its private chat to be remembered and then referenced in a group chat. We can achieve this with a very simple prompt. Create a blank memory .md file in bots sandbox on creation.
then update the agent's instructions to write and reference said memory file when appropriate. The goal should be that the agent can remember things said in the private one -on -one chat even when asked about it in a group chat. And we can tell the agent to use Daytona trigger and AI SDK skills.
So I tried creating a new bot and it actually failed with the following Daytona error. Uploading files is not supported. Module form data is not available in the node runtime.
So I'm going to highlight this. I'm going to copy it and I'm going to give it to the agent. So the problem was that the agent tried to implement the memory file.
using file upload but it doesn't have to do that at all. It can simply use touchmemory .md which is a CLI command which you can run instead of any Unix machine. So it picked that option instead because it doesn't need any additional configuration.
If you have the same problem tell your agent to instead of uploading the file simply use bash tools like touch memory md instead. So I just created a new bot and I'm gonna tell it remember this my cat's name is Noddy.
Now I'm gonna create a group chat which includes the writing editor to whom I told the secret. I'm gonna start by asking the tutor who doesn't have that memory line What's my cat's name?
And as expected, it doesn't know my cat's name. So now I switch to writing editor who should have that information because I told it in a private chat. Let's see if it can reference the memory file.
Unfortunately, I was a bit overconfident and it didn't work. But I feel like we can fix this pretty easily. Remember how I told you that this doesn't have to be a tool call?
Well... Why wouldn't it actually? So I'm gonna screenshot how I told this to the writing editor.
one -on -one and then I'm going to go back inside of the group chat and I'm going to screenshot the situation again. This way the agent will understand what went wrong. I'm going to tell the agent that I tried testing this by first telling an agent a secret in a one -on -one chat and then seeing if it can reference it in a group chat but it failed and perhaps the instructions are too flaky and we should have a get set tool for memory instead which should be a much more solid solution for this.
Before you test whether this works or not make sure to restart your trigger server. If the trigger instance is stale it's not gonna allow your agents to use the new tools or memory files.
I created two bots and I told one of them that my cat's name is Noddy and it successfully used the new remember tool which is much more deterministic and solid. Now I just asked the other bot in a group chat can you recall what's my cat's name and it invoked the recall tool and as you can see its memory is completely empty because I never told it about any cats.
So now I'm going to select code reviewer and I'm going to do the same thing. And there we go. Now in a group chat, this agent persisted its memory using the recall tool.
The user's cat's name is Noddy. You can now tell your agent to stage all and commit. One more thing left to add, at least for my agent, is that group chat header should be a button and open the edit dialog form to change the name.
add or remove members, or delete the group chat, just like the edit form for an individual bot. You should now be able to use the chat header to remove bots, change the name, or delete the group altogether. Since we added new tools to the agents, let's make sure to do a trigger deploy so our production agents have the exact same instance.
And you can then tell your agent to stage all, commit, and push. So far, we implemented AI chat, durability, persistence, sandboxes, group chats, tool calling and memory.
All that's left to do is to implement the billing so that we can monetize this application and turn it into a real software as a service. As with every major feature in this project, we start with the primitives, we teach the agent how to use the primitives and then we have the implementation. But believe it or not, we already have the primitives and we already have the skill that teaches the agent how to use the primitives and it's called clerk billing.
Since we chose to install all skills when we added the clerk to our project, you should see clerk billing available inside of Cloud Code. On the off chance you don't have clerk billing skill, simply run this command again and this time make sure that said skill is selected.
You can now tell your agent to enable clerk billing in this project. Create a paid plan that gates billable features in this project, which include bots, creation and usage, and sandboxes, creation, connection, and usage. For now, keep the features simple.
On paid plan, all features are available. On free plan, everything is blocked and it shows a toast notification error if the user tries to do anything. Do not implement any unit economic layers, ledgers or wallets.
We are purposely keeping it simple. Develop a pricing page that can be accessed via toast notification error. do not add it to the sidebar.
So the pricing page should only be accessed via the notification. All right, let's give our app a spin. I'm going to try creating a new bot.
And as expected, I have a toast notification that I need to upgrade to continue. Now let me try going inside of my existing bots and see what happens. I'm going to try sending a message.
And once again, I'm prompted to upgrade. Now I'm going to try operating the desktop panel. and once again prompted to upgrade.
So let's finally click see plans. I am now redirected to the pricing page where I can subscribe to a $20 a month plan. We can simply use the pay with test card option and just like that our account has been switched to a pro plan.
So let's try this again. Create a new bot is now. available make sure to go through your app and verify that all features truly are unblocked after you subscribe you can now also go inside of your clerk dashboard into the billing tab and in here you can see all the subscriptions your application has as well as your monthly or annually recurring revenue i bet you didn't expect that we were going to implement billing that quickly but thanks to the primitives which we added to our project our agent was able to implement this flawlessly, especially thanks to clerk billing, which fits very nicely to our existing clerk users.
You can now tell your agent to stage all and commit. Our current monetization solution works great for a small demo, but it fails short when you need to actually account for all the costs our project has. Now, we could technically bill the user for every single thing that costs us money, such as database, hosting, etc.
But those really aren't our biggest expenses. Our biggest expenses are usage -based expenses, primarily AI gateway, or if you're using individual providers like OpenAI Anthropic, that will be the equivalent. And our sandbox, which just as well is usage -based.
Let's tell our agent to extend our current monetization solution to cover the usage -based billing costs of our app. monthly plan shouldn't allow the user to abuse Daytona sandbox resources and our AI provider token pricing. Besides just gating those features, we need to develop a ledger that maintains allowed usage within the current subscription period.
The ledger should be displayed in form of limit progress on a new usage page accessed through the user button in the sidebar footer. For that, we can use the clerk user button action customization skill.
The usage page should also display time till reset. And we can end the prompt with Use Clerk Billing, Daytona, Neon, AISDK skills and perform your own web search.
Now, this is a pretty significant task, so it's normal if it takes a while, especially if your agent is using a web search or fire crawl to explore what the pricing actually is for the tools that we are using. At the end of its implementation, you should have a new usage option within your user button. This usage option should lead you to this page where you should see these types of progress or limit components implemented by your agent.
Now your agent might have added more or less categories for the user to keep track of. Technically all of this could have been consolidated into one usage but I just feel like it makes more sense to tell the user this is how much it costs. for the model tokens, and this is how much it costs for the bot's machine.
Perhaps we could add a third and a fourth limit here. Our trigger runs in deployment also cost, so we could also do our serverless cost. If any of those limits are hit, the user is prevented from using them further until the reset which is the end of their current subscription period.
To test whether the limits work or not, tell your agent to perform a relatively complicated task so that it has to perform a lot of tool calls on the sandbox directly so that you will see the progress move on both the token pricing but also on the Daytona sandbox usage. And just like that the user will be able to keep track of exactly how much AI usage they have left and how many sandbox hours they have left in this subscription period.
Now make sure to perform a trigger deployment. With the limits in place you can now tell your agent to implement an even more expensive plan which will give power users more freedom to use your application.
And once you're satisfied, go ahead and stage all, commit and push. Now, as with every real world software, it's quite normal that your users encounter errors or bugs. But what's not normal is that you as the developer aren't aware of what's happening to your users.
Now imagine if you could replay exactly what your users were doing at the time they got an error. When it comes to observability, there is only one product that I keep recommending over and over again.
If you are preparing to deploy your project to production, don't do it before you add Sentry. You have nothing to lose and so much to gain. I've collaborated with them to give you this amazing offer where you can try a premium version of Sentry for three months.
So if you want to, you can use the link on the screen to get that offer. The easiest way to get started with Sentry is using their coding agent setup. Go inside of your terminal and run the following command.
The setup is automatically going to recognize all agents installed on your machine, so verify that the one you actually use is selected. Now make sure to run reload plugins. and reload skills inside of your coding agent.
Now tell your agent to use the SentryGetStarted skill for this project. Now the skill is going to recognize the environment it's in. So it tells me that this is a Next .js app router with trigger .dev tasks.
There is no Sentry installed and my organization has no projects. So it asks me to confirm whether I want this skill to create a new project called AI teammates, so make sure you select that option.
Now, once your agent is finished, you should see your new project inside of your Sentry dashboard. You will most likely also see a test error which the agent fired to verify that your project is receiving the errors from your application. Now, go inside of your settings and find organization tokens under developer settings group.
In here, click on create new token. Give it the name of AI teammates and click create token.
You can now copy this value and then store it under century out token in your environment file. Your application won't look any different but all errors will now be recorded and added to century. Now we aren't done just yet but it's a good idea to stage and commit this setup so we can keep track of what we add next.
Now tell your agent to add an uncaught error to bot creation function with value bot creation error and perhaps tell your agent that this is for a demo and it will be removed later. Now, give it a shot and try creating a new bot.
Once you try submitting the form, it should fail and instead, you should get an error. Something went wrong. Now, without Sentry, this is something only your user would see.
But with Sentry, every error that happens within your application appears in the dashboard. And here it is. Bot creation error.
Now in here you have an abundance of information, stack traces, logs. everything related to this error so you can understand exactly where, how and why it happened. Now, I'm going to tell my agent to use sentry skill to enable session replay.
Now, perhaps this is already enabled for you, but you lose nothing by asking your agent to do it as well. Now, in my case, session replays were already enabled, but the sample that actually gets recorded was very small. changed it to be a 100 % in development and then 10 % in production.
The reason it uses such a small sample is because it's expensive to keep those session replays and it's only available in the free, sorry, in the premium plan. So now that my agent has enabled this in development, I'm gonna showcase how it actually looks. Now I'm going to tell the agent to add a demo client -side error when user tries to click on the create a new bot on the home screen.
So now when your user lands on this screen, if they click this button, they should immediately get an error. And this time, once you find that error inside of your Sentry logs, you should also see a session replay describing exactly what the user was doing and where they clicked when the error happens. You can see how I'm actually hovering over.
the toast notification that I got at that exact point. We even have the exact mark when the error happened. Since all we did in this session was enable session replays to 100 % trace in development and then add dummy errors throughout our app, we can simply discard all changes from the source directory.
right here. Just make sure that you have previously committed the real Sentry implementation so you don't accidentally remove those changes. If you want to be more careful, you can tell your agent in the session where we told it to add all of these things to just revert it to the way it was.
Now for a second, let's go back to this diagram. What we added now is observability and error tracking within this environment right here, our Next .js application. But the majority of business logic happens in a durable stateless worker, which is entirely outside of this environment, which we just added observability to.
So what we have to do next is find a way to enable Sentry inside of Trigger. We can tell our agent to use Sentry skills to integrate Sentry with Trigger .dev. Once the integration has been set up, tell your agent to add an error inside of your Trigger chat agent.
Now make sure to restart your Trigger server and try sending any bot. a message and you should now be able to see that error inside of your Sentry dashboard. So we now officially have end -to -end observability from our Next .js application all the way to our stateless machine where the agent actually runs.
Now tell your agent to remove any demo or test errors that were added and stage all and commit. We can now track errors across both environments our application has. but one thing we don't have is end -to -end observability.
For that, we can use logs. So let's tell our agent to use Sentry Skills to enable and add logs throughout this project. This skill will find all the relevant places where a log needs to be added.
It's going to use the Sentry Logger and respectively choose Warn, Info, or Error. It's going to calculate how long something took, all the relevant information for the action, anything to give you a clearer picture of what's actually going on inside of your project.
The best way to try this out is to just use your app as you normally would. Ask your agent some questions, give it a task, open the desktop, just try and do things for a couple of minutes, and then we'll go into the dashboard to see what was captured. You can now go inside of explore, logs, and in here, logs will start to load one by one.
Desktop opened, bot created. Chat session started. Chat turned finished.
Sandbox metered. Group chat created. Everything that's going on inside of your application is now being logged.
Now, individually, these logs don't mean much, even though you could still make some specific conclusions just by looking at the logs. But combined with errors, they give you a much clearer picture of what was actually happened before the error occurred. This can be extremely helpful with hard to debug errors.
Now, imagine having all of that info. information, all of that stack trace, all of those logs and the power of AI. At that point, no error is unsolvable.
And the more information you can give to your agent, the better it's going to resolve the error. You can now tell your agent to stage all and commit. Now, you could go through these errors one by one and resolve them, but you could also configure something called SEER, which is Sentry's code review agent.
All you have to do is connect a repository. You will have to install the Sentry app onto your GitHub organization or profile. If you choose individual repositories, make sure that you select the one where you want to add it.
Once you have your repository connected you can go back into the configure seer section and this time you will be asked to enable autofix on a project. Make sure to select AI teammates and make sure to select the equivalent repository and then click save project. Now go inside of your feed to find any recent errors that might have happened and in here you can find the autofix option and you can start analysis using seer which now has access to your source code.
The SEER agent is gonna find the root cause, explain why it happened and give you the reproduction steps. It will also give you the evidence and the file attached. You can then make a plan to fix it.
Once you are satisfied with the provided plan you can press yes write a code fix and then you can draft a pull request and in a few moments you will be able to open the pull request SEER has drafted and then you can merge it into your codebase. And just like that, you can use Seer to autofix all of the errors that appear in your application.
So the same observability platform that has very deep knowledge of our app can now also fix every error it finds. And even though we thought our app doesn't have any errors, look at just how many it was able to find by just using the application. We wouldn't have known that we have any of those if we didn't implement Sentry.
You can now tell Tell your agent to use the Railway skill to add all new variables and use Trigger skill or MCP to add all new variables used by chat agent, followed by push or sync the changes. Now, my agent did this automatically, but if yours didn't, make sure to do a trigger deploy.
Inside of your environment variables, trigger agent should have added the SentryAuth token. and same variable should have been added to the railway deployment. After a successful deployment, go ahead and play around with your product.
Very soon inside of your Sentry dashboard, you should see a new environment detected production. You can now separately keep track of all errors that happen in development and production. Congratulations, you now have a production ready application.
The Hook

The bait, then the rug-pull.

xAI shipped Grok Bot, Meta shipped Muse, OpenAI shipped Dots. All three are rebuilds of an open source project that already existed and was free, which tells you the moat was never the idea. This build goes after the same product: bots with their own Linux desktop, their own memory, and a screen you can take over mid-task.

Frameworks

Named ideas worth stealing.

47:03model

The primitives-first build loop

  1. Check what primitives already exist: components, skills, schema, earlier examples
  2. Install the vendor's agent skill so the agent uses current docs, not training data
  3. Name the order of construction in the prompt: schema, then action, then UI
  4. Write the working pattern back into agents.md so it is never explained twice

The repeating shape of every feature in the build. Each cycle adds reusable context, which is why the earliest features need paragraph-long prompts and the latest ones one-shot from a sentence.

Steal forany repo where a coding agent keeps reinventing the same patterns
1:18:24model

Three tiers of an agent request

  1. Next.js route handler or server action receives the message and does nothing slow
  2. Durable stateless worker runs the agent with no request timeout
  3. Cloud sandbox gives that agent a filesystem, a shell and a desktop

Each tier exists because the one above it cannot do the job. The web tier breaks on refresh and redeploy, the worker has no computer, the sandbox has no model.

Steal forany product where an AI task runs longer than a page load
1:35:05list

Computer-use tool set

  1. Bash
  2. Read, write and list files
  3. Mouse click, move and scroll
  4. Keyboard type and press
  5. View screen, used privately by the agent and never rendered in chat
  6. Show screen, used when the user asked to see it and rendered in chat

The claim is that this set covers 99 percent of computer-use tasks. The split between viewing and showing the screen is what keeps the transcript readable while the agent works.

Steal forany agent that needs to operate software rather than describe it
06:22model

VNC takeover path

  1. Every bot gets a sandbox with a noVNC websockify port open
  2. The UI loads a signed URL for that port inside an iframe
  3. Clicking the iframe reopens it in a fullscreen dialog
  4. Inputs are enabled and hotkeys that would close the dialog are suppressed

Screen sharing and human takeover with no custom remote-desktop code. The only real detail is loading the connection once and reusing it for both the panel and the dialog.

Steal forany product where a human needs to finish what an agent started
09:41list

agents.md guardrails worth copying

  1. Never create branches, worktrees or commits unless the user explicitly asked
  2. Only database push, never migrations, and delete the migration script
  3. No backwards compatibility: the data is worthless, prefer data loss, never backfill
  4. Form rules captured from the first form the agent built correctly
  5. Reference agents.md from CLAUDE.md so both are read

Rules added at the moment each one was needed, each in response to the agent actually misbehaving. The last one exists because a tool wrote a CLAUDE.md and silently shadowed the other rules.

Steal forthe instruction file in any repo you build with an agent
1:48:03list

Group chat upgrade ladder

  1. User picks which bot answers each turn from a dropdown
  2. Each reply shows the answering bot's avatar
  3. Bots can call a handoff tool to pass the request to a better-suited bot
  4. No selection at all: send a message and let the bots decide who answers

Deliberately shipping the dumbest version first so there is a working primitive to build on, rather than trying to solve multi-agent routing in one shot.

Steal forany multi-agent feature you are tempted to over-design up front
CTA Breakdown

How they asked for the click.

VERBAL ASK
2:03:32link
“If you are preparing to deploy your project to production, don't do it before you add Sentry. You have nothing to lose and so much to gain. I've collaborated with them to give you this amazing offer where you can try a premium version of Sentry for three months.”

Seven sponsor tools are each introduced at the exact moment the build needs them rather than in a pre-roll block, so every pitch doubles as a build step. The Sentry one is the most explicit offer and lands with twelve minutes of demonstrated value already behind it. There is no subscribe ask anywhere in the video.

MENTIONED ON CAMERA
Storyboard

Visual structure at a glance.

Grok Bot opens the pitch
hookGrok Bot opens the pitch00:00
agent books a flight, human takes over
promiseagent books a flight, human takes over01:43
architecture diagram
valuearchitecture diagram07:31
built-in fetch tool fails
valuebuilt-in fetch tool fails17:33
backend primitives
valuebackend primitives34:12
Figma screenshot into the agent
valueFigma screenshot into the agent44:18
chat survives a refresh
valuechat survives a refresh1:19:26
taking over the bot's desktop
valuetaking over the bot's desktop1:42:16
billing in one slash command
valuebilling in one slash command1:57:14
observability and autofix
ctaobservability and autofix2:03:58
Frame Gallery

Visual moments.

One-click upgrade to your Google

Get more breakdowns in your search results

Add Modern Creator as a preferred source and Google shows you more of our breakdowns in Search, Top Stories, and AI Overviews. It only changes what you see, and you can undo it in your Google settings anytime.

Add to Preferred SourcesOpens your Google source preferences with us pre-loaded. Tick the box and you're done.
Watch next

More from this channel + related breakdowns.

13:13
Danny Why · Listicle

Claude Code Just Changed The Internet Forever

A weekly AI news roundup: a skateboard mod spliced into World of Warcraft, Anthropic's new Claude Motion, a free seven-app Adobe alternative built with Opus 5.5, and a Claude agent that almost submitted a false tip to a real police department.

October 12th