How I Build AI Teammates With Claude Code - Grok Bot Clone

Code With Antonio· 2 hr 14 min· 23,476 words· 107 min read· English ·Watch on YouTube

This is the full transcript of How I Build AI Teammates With Claude Code - Grok Bot Clone, published on YouTube by Code With Antonio. Every paragraph carries the moment it was spoken, so you can click any line to jump straight to that point in the video, search the whole thing for a word, or copy it out.

0:00Over 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 X AI with their Grock 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 OpenClaw, 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

0:35it's completely open- source and free? Primarily, it is because OpenClaw was mostly intended for techsavvy 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 Open Claw failing at simple tasks. All of these companies saw that as an opportunity. They decided to rebuild their own open claw, package it into a familiar user experience, give it official connectors to other companies

1:04and power it by an intelligent model. Which is why in this video I'm going to teach you how to build your own product like Grockbot, 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 700 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

1:39know. 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

2:16the 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

2:49what is my staging URL and it's written down in its memory file. So I can now go inside 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 aentic coding to develop this project, you will be able to style it however you want. I personally chose the Grockbot look and feel, but you can style it as open AI

3:24dots 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're going to know exactly what we're 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

4:04appears 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 dialogue, 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 bots'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

4:46machine? 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 Nex.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 longunning 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

5:27case 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 nextjs environment, it would unfortunately be interrupted and it scales separately from the Nex.js web tier and it is in this stateless worker where the magic actually happens. What we do here is we invoke a new AI SDK agent and we give it tool access. Specifically, we give it sandbox execution tool, sandbox file tools and sandbox screenshot tools. And

6:16with 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 node VNC web sockifi 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 I frame we

6:59load it in a full screen dialogue 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 agents 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

7:37scares 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 monor repo 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 for the project to

8:14install. 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

8:50are 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 rundev, 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 verified that our app runs correctly, it's time to prepare

9:17this environment for agentic coding. This means adding certain skills and modifying agents.md instructions so that the agent behaves as we expected 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,

9:43work 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 to this project, but

10:12I 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 clot code, which is preferable to many people over a terminal user interface. But the second reason is builtin native connection to Visual Studio Code, which is our code editor. So whenever you use this extension, clot 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're going to be using Shatzen components to develop this project, it's

10:52a good idea to install Shatsen 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 shaden 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

11:29recommend 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 andclaude. Both of them should have the exact same skills chaten and migrate radics to base. Keep in mind that in case you are not seeing one of the folders, for example, if cla is missing, you will not be able to load this skills in cla. So go back to where I installed the skill and carefully look

12:08at how I select clot 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 Shhatzen UI. You should see migrate radics to base and the shatzen scale. Since we currently have 29 unttracked files inside of our source, let's go ahead and tell our

12:36agent to stage and commit all changes. Now that we have the Shatsian skill, we can reliably tell the agent to add all available components. Previously, this would mean that the agent would have to fetch the chats 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 components UI folder. Previously in here

13:14we only had the button component but now you should have the complete set of shatzen 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 tool tip 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

13:57the 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 tool tip 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 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

14:32we 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

15:14once 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

15:36your 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 dialogue. 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

16:16production. 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

16:54project. 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 Grockbot. 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 web fetch 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 web fetch tools. Very shortly

17:34after 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 web fetch 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 web fetch 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

18:16competitors who are building a similar thing like I am. I want to see what technologies they're using, whether they are 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 Firecrol and I connect it to my agents and give it access to the entire

18:52web. So let's go back to our first example. I want to get the branding from our Grockbot 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, Firecrol 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,

19:20spacing units, as well as border radius. But what about all of the resources which web fetch tools cannot access either by being forbidden or by being loaded with JavaScript after the page loads. This is where Firewall's newest product comes in. Firecrol 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 teammate, 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

20:01same 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

20:38agent, first run the following command. This is going to initialize the firecroll CLI and log you in using the browser method. So make sure you create an account first. Make sure to authorize the firecroll cli and then you can go back into the terminal. Now make sure to restart clot code and try searching for firecrol. You should see all the tools available here as well. Now let's try this again. Use firecroll 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

21:15continue this conversation and tell our agent to apply this theme to our Chatsen 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 Firecrol and then describe what you're searching for. Fire call 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, Firecrol

21:54successfully 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're going to need to

22:17add authentication to our project. Whenever you install a new skills, make sure to use the latest version tag because the previous ones had a bug where it didn't install. Claude 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

22:54link installation method. You will now have a bunch of new files inside of your source control. The only thing I'd want you to check is that you have the same skills in both agents andclaude. 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 uh layout and page files that we actually modify and don't confuse that with this 162 changes. So

23:27let'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 dialogue 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

23:55a breakdown of what's going to happen. So it checked the project. It detected that this is a nex.js16 app router with shaten, npm, node, and no existing out provider or any clerk install, which means this is a clean setup with no migration needed. Uh 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

24:18start 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 uh this instance of your project on clerk will you ask the agent to perform clerk out login which will take you uh to their login page. So the only thing I'm noticing here is the fact

24:56that 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 uh npm rundev running like this just go ahead and close it because otherwise if you have it running uh this scale right here can get confused and it's going to say oh it's already running and it's just going to 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

25:32test whether their implementation works correctly. So if you try doing npm rundev, 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 and sign up buttons. And these buttons should redirect to the signin page or to the sign up page. Now

25:58go ahead and try creating an account. And once you create an account, instead of the signin 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. Uh those things don't really matter right now because

26:26it's not going to end like this anyway. We will have a sidebar and that's where our AL control is going to be. So, as long as you can actually log in and log out, you're 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

26:54doing 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 uh run clerk. What do I mean by that? Well, take a look at your project. You have environment.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 railway CLI and 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

27:34to 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 cloth code uh 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

28:12the 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

28:35message. Cannot replace to directory. And then it actually talks about claude skills. And then I figured out what it is. Uh skills sometimes include small versions of your Nex.js application. I mean like a Nex.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 Nex.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 re recognizes

29:17that the likely fix is to keepcloud 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 and I'm going to show you the exact file that's going to be created because of this. It's a very simple file. docker ignore and inside you should haveclaude and 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

29:52services like railway and whoever uses rail packs like netify versel all of those actually get confused when they read templates because they look for package.json 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

30:31you 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 agents synchronize my local environment

30:49variables with the uh deployment ones. And it's just going to do it. I don't have to do anything else. And then I'd just go ahead and verify that you 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 Clerk offers all of that. They are an entire user management platform. So, it's a shame not to claim your application, which you

31:23will 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 clot 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

32:01all 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 uh 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

32:41recynchronize the environment variables. Now in my case, the agent told me that nothing needed reyncing, meaning that all six variables already match what we have here in the 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

33:21provide our agent with a set of reliable primitives and then let it build from that. Think of it like shaden but for backend. That is exactly what Neon is becoming. Previously in my projects, I used Neon for database only. But since then, Neon has expanded into a complete back-end 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

34:08add many useful back-end 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

34:45you 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 East1. 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

35:28how to interact with neon. As always, make sure to reload skills if you have an active clot code session and then open the skills dialogue and search for neon. You should see both neon and neon posgress skill available. Now it's a good idea to run stage and commit so we get rid of these 17 unttracked changes. You can now tell your agent to set up drizzle rm 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 ogress and some dev dependencies

36:06like postgress 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

36:45and 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 posgress database and click on tables. And if you can see the to-dos 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

37:28perform 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.mmd 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 going to 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

38:09should 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

38:57every change you do has to be backfield 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.mmd so they don't drop your production database. And now you can tell your agent to stage, commit, push, and recynchronize 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 unpulled, and neon branch. And all of those need to go into our railway instance. The deployment will most likely be

39:43successful 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 thirdparty 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

40:29ID 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 UYU ID. 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

40:57instructions 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 inside 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

41:38something like this. I have 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 file. 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

42:05page. 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're 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

42:46this smiley face right here. Simply pick a random image from our project like a logo or a favicon. Do not build one yourself. Use shhats and 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 Grockbot avatars. You can then go inside of the documentation, choose the

43:27section 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 cloth code session and tell your agent to create a chat avatar component inside of components chat avatar using dice bear'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,

44:11specifically 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 dialogue. 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

44:52file 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 components bot dialogue component using shhatsen dialogue primitive and form composition using react hook form 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

45:36dialogue to our page create bot button. And I finish it up with use chats skill, neon skill, and clerk skill. Now go ahead and click on create a new bot. The dialogue 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 going to give this a name of

46:12Antonio. And I'm going to click on create bot. I can see a toast. Antonio is ready. And now I'm going to 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

46:52infamous 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 shad cnui or maybe a skill like clerk or neon skills. And

47:20then 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 bots table. I know that we are using drizzle or 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

47:43form that wants to create a bot. Otherwise, if I didn't specify this, it would probably still work. I have no doubt about that. But it would most likely define the schema within the bot dialogue 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 reused 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 dialogue and

48:28specifically 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

48:46agent how to develop a form every time. So, how about we simply use this conversation and tell the agent to update agents.mmd 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 drizzle validator. We wouldn't have to

49:20tell it to use react hook form 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 equivalent 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

49:59chats 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 bots'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

50:35required and defaults to now so we know whether a new message has been sent and a created ad 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

51:11has one chat and one bot you should now have a new chats table which has an ID user ID kind which corresponds to a posgress enium 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

51:51problem 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

52:31between 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

53:05and 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

53:24their bots as well as the group chats. Every chat features the bot avatar, bot name, last 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,

54:07I'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 signin pages. So if we just tell

54:44our 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 loggedin 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 Nex.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 are going to create a

55:24new folder in parenthesis 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

55:45page 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 chatsen scale. Your application will now have a sidebar. Keep in mind that if you are zoomed in or if your screen is too

56:18small, 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 going to tell my agent to render the clerk's user button component inside our new app sidebar in the footer. Display both avatar and the username 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 scale. Now most likely Nex.js developer indicator will be right over the user button. So you can just tell your agent

56:57to 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 signin 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

57:33of 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 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,

58:09which doesn't happen. So, 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. I just want to show you how I communicate with the agent to

58:42get 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

59:16information 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's 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 dialogue, this one. And the create group chat shouldn't open anything. And then I'm just going to 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

59:51have a plus button, which opens a drop-own dialogue. And when you click on create a new bot, it should open the same bot dialogue 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

1:00:12moment. 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 dialogue. Now, I'm going to 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 going to paste the screenshot to the agent and I'm going to

1:00:48tell it to add a list of users chats inside a new sidebar group inside sidebar content inside app sidebar. Use the chats and 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 date fns package for any date string manipulation which are things like 2

1:01:33days 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

1:01:56immediately 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 uh 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

1:02:19immediately 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 the 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 going to 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

1:02:58to 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 dialogue 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 dialogue should also be opened via command or control plus the letter K. And you can use the built-in Shatsian components for the

1:03:37keyboard shortcut effect. And I'm going to end the prompt with use skill. So the reason I'm repeating quite a few things here is because I am in a brand new clot 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 dialogue and displays

1:03:54the same chats that you can see here. The same command dialogue should be openable via command or control plus the letter K. Now I'm going to tell the agent that selecting a chat from the command dialogue 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 going to do that later. Clicking on an element inside of the sidebar should now redirect you to a 404 not found. Same as

1:04:28clicking inside of the command dialogue. You can now tell the agent to stage all and commit. Now I'm going to 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

1:04:52should 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 shhatsen 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

1:05:27match 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 get commit and push. 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

1:06:05conversation 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, bots response, and finally this composer at the bottom. So, I'm going to start by telling my agent to register a chats chat ID page inside of our dashboard and then render the current

1:06:35chat 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 chats 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

1:07:11page 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

1:07:39exactly 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

1:08:01implement the default state as focused. So if it's hard for you to take a screenshot like this, you can always like doubleclick 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.ds and scope it to clerk's user ID and it can use clerk skill for that. Then load the chat by its ID inside of our new chat ID page. Then develop a components

1:08:38chat header which we paste a screenshot of according to the screenshot and use the chat avatar component to display this chats bots 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.dts. Now, this isn't important

1:09:20functionalitywise, 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 codebase for the agent to do the correct thing. In case your agent uh 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

1:09:54does nothing. To make this chat header more useful, I want to turn this bot's avatar and name into a button which opens a dialogue 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 clot code session. The first time we created a form, we told our agent to update the agents.md and give it 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

1:10:31use 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 dialogue 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

1:10:51name 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 dialogue 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

1:11:12delete a bot from the edit dialogue. 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 chat UI, which makes them directly compatible with our existing components. It's also deeply integrated into AI SDK, which is an open-source tool, which we're going to use to communicate with various AI providers. Let's start by running the

1:11:52following command. This is going to install all necessary dependencies and setup components needed to develop conversational UI. Once the 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

1:12:27is a pretty good place for a checkpoint. So tell your agent to stage all and commit. Now that we have the front-end primitives set up, let's install AI AI SDK 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 AI SDK. 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

1:13:05environment.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 Open AI, Anthropic, Google, and Grock. To get an API key, you need to visit your provider's respective platform. For example, Open AAI 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

1:13:46feature 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

1:14:19respective 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

1:15:01the 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 inside 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

1:15:41case, it might be Anthropic or Open AI. 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

1:16:09component 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 instead of the stream text where the model is being chosen. So if you use Enthropic or Open AI, you can only choose between models from those providers. But because I chose Neon, I have access to all entropic models, all Gemini models, GLM models, GPT models, Grock models, Kimmy models, Meta models,

1:16:53Quen models, Llama 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

1:17:32of 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

1:18:11first thing that happens is chat UI submits a message into the Nex.js application route handler. Now, route handler 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 nextjs application, route handler once again breaks the connection. If anything bad happens to this instance, the agent is interrupted, which is completely unacceptable for longunning tasks, which

1:18:45are 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 Nex.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

1:19:25where 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

1:20:04it twice if you selected multiple targets like I did. Cloud code and agents.mmd. 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 scaffold it 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

1:20:39login, 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 5 seconds, and then completes the task. Now, go inside of your dashboard, find API keys in the

1:21:22sidebar, 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 inside of your dashboard, you should see the exact task that you

1:21:55have defined within your trigger folder. Example.ds. 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 NextJS server. In fact, I don't even have my Nex.js started. That's what I wanted to demonstrate. This is the

1:22:35environment 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 connection 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 dev chat agent and add

1:23:18transcript storage for persistence and scope that persistence to chat ID. So yes, trigger also comes with builtin 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

1:24:03which is now run in a stateless machine. Now make sure you have your next server running and make sure that you have your trigger server running. Now pick a 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

1:24:38that 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

1:25:20because 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 environment variables stage all commit and push. It appears that our

1:26:00builds 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

1:26:19failing 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

1:26:55to 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 Nex.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

1:27:39independently 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 AI SDK 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

1:28:22to set expires to never full access and create API key. I will then copy this API key and then I'm going to go inside of my railway project. I'm going to 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 going to click update variables. I'm

1:28:51then going to go back inside of here. I'm going to 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

1:29:16it both for development and production. You can now also redeploy your environment variable change on railway. So let's try this again. I just shut down my trigger server and I shut down my Nex.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,

1:29:49let'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 tools 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 a $100 in credits, which is more than enough to complete this tutorial. But if you want to, you can redeem coupon code Antonio 100, which will give you an additional $100 in Daytona credits. Now head into the API keys section and click create an API

1:30:26key. 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

1:31:06so 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

1:31:35to have everything on the main branch. If you want to know the symptom of why this happened, it is because when we added a trigger in the last chapter, it created a claude.md file. And in here, it simply added use trigger.dev skills. And I simply assumed that the agent was going to continue to read both agents and claude. But it looks like if only agents.m MD is present, then all agents will read that file. But if I have an explicit cla.md, claude is going to read this file. So it's a pretty good idea for you to add agents.m MD reference inside of your claude MD file. This way

1:32:12your 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 a

1:32:50optional 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

1:33:27don'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 bots'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 AI SDK and trigger skills to give our chat agent base instructions about its name, job, purpose, and the

1:34:03sandbox 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

1:34:27description 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

1:35:02use. 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

1:35:40and the bubbles to render tool calls. Use AI SDK trigger Daytona Chats 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,

1:36:18your 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. But if you use the token I told you at the beginning, which is Antonio 100, 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

1:36:54with 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

1:37:21tell our agent to stage all and commit. Now, let's tell the agent that the desktop icon button inside 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

1:37:59chat 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 chats and scale. 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

1:38:39in full screen using our dialogue component. VNC inside the dialogue should accept inputs and prevent the dialogue from being closed by any hotkeys or inputs to prevent bad user experience. That is because the built-in dialogue probably has some hotkeys like escape button or space which close the dialogue. But when we open this dialogue, 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

1:39:21the 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 uh 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

1:39:55preview 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 going to tell my agent that ever since resizable panel

1:40:31has 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 skill. 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 skill to add new

1:41:05environment 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 it 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

1:41:41environment 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 going to 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

1:42:15take 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

1:42:38tell 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 oneshot the entire group chat feature. Now, one shot usually isn't associated with exactly good results, but what I'm trying to say is we already

1:43:17taught 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

1:43:59layout where each item sits inside the square. Two items sit side by side overlapping. Three form a triangle and four fill a 2x two 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

1:44:29implemented. Sidebar and chat header. 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

1:45:07purposely 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

1:45:34minimum 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 Grockbot uh 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

1:46:12face 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, and 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

1:46:50I'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

1:47:12transport 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 uh their individual DMs. Uh 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

1:47:54minimum 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 bots's avatar next to its message in group chats in our chat component, which

1:48:32uses 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

1:48:59metadata 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 bots's instructions for group chats to

1:49:33understand 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

1:49:55to 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 me 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

1:50:14obviously 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. Uh, and then the assistant researcher uh handed off to code reviewer. This is a good place to stage all and get 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

1:50:52because 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 uh input composer

1:51:27dropdown, you implement a reg x which recognizes the at 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 batch. So, it actually didn't succeed to do it and my message

1:51:58and 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 older 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. All right. 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

1:52:33to 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 agents 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

1:53:13tried 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.m 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

1:53:54instead of uploading the file simply use bash tools like touch memory MD instead. So I just created a new butt and I'm going to tell it remember this my cat's name is Naughty. Now I'm going to create a group chat which includes the writing editor to whom I told the secret. I'm going to 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

1:54:30reference 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 going to 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

1:55:05group 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 going to 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 Naughty 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

1:55:40cat'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 naughty. 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 dialogue form to change the name, add or remove members, or delete the group chat. Just

1:56:18like 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,

1:56:45group 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 clerk to our project, you should see clerk billing available inside of clot code. On the

1:57:22off chance you don't have clerk billing skill, simply run this command again and this time make sure that set 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

1:58:08be 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

1:58:48to 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 of the subscriptions your application has as well as your monthly or annually recuring revenue. I bet you didn't expect that we were going to implement billing that quickly. But thanks to the primitives which we added

1:59:26to 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 build a 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 Antropic that would be the equivalent

2:00:14and 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. Our current 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

2:01:01we can end the prompt with use clerk billing Daytona neon AIS SDK skills and perform your own web search. Now this is a pretty significant task. Though it's normal if it takes a while, especially if your agent is using 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 this types of progress or limit components implemented by your agent. Now, your agent might have added more or less categories for

2:01:45the user to keep track of. Technically, all of these 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 bots 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

2:02:26perform 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 uh 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

2:03:06satisfied, 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

2:03:46them to give you this amazing offer where you can try a premium version of Sentry for 3 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 sentry get started skill for this project. Now the skill is

2:04:27going to recognize the environment it's in. So it tells me that this is a Nex.js 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

2:05:09under developer settings group. In here click on create new token. Give it a name of AI teammates and click create token. You can now copy this value and then store it under sentry out token in your environment file. Your application won't look any different, but all errors will now be recorded and added to Sentry. 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 uncought error to bot creation function with value bot creation error and perhaps tell your agent that this is

2:05:46for 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

2:06:29session 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. So my agent 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 going to showcase how it actually looks. Now I'm going to tell the agent to add a

2:07:10demo clientside 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

2:07:48to 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 Nex.js application. But the majority of business logic happens in a durable

2:08:30stateless 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. have. 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 endtoend observability from our Nex.js application all the way to our stateless

2:09:15machine 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,

2:09:55anything 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

2:10:13the 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 information, all of

2:10:54that 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 SEIR, 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

2:11:32it. Once you have your repository connected, you can go back into the configure sear 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 seir which now has access to your source code. The seir agent is going to find the root cause, explain why it happened and give you the reproduction steps. It will also give

2:12:08you the evidence and the file attached. You can then make a plan to fix it. Once you're 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 Seir has drafted and then you can merge it into your codebase. And just like that you can use Seir 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

2:12:45any 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 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 sentry out token, and same variable should have

2:13:23been 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.

Where these words come from. This is the caption track YouTube holds for this video, written automatically by YouTube rather than by the creator. We read it, tidied the line breaks and laid it out so it can be read. The plain text version is at https://viewrankai.com/tools/youtube-transcript/mQBuZ6Dp0u0.txt.

All rights in this video belong to Code With Antonio. Watch it on YouTube. If this is your video and you would rather this page did not exist, tell us and we will remove it.