Skip to content

The parts of Urai

Urai has the objects that follow. This page tells you what each object does. The in-app agent guide shows you how to use these objects in sequence.

The organization is the boundary for billing and for access. These items belong to an organization:

  • Model provider credentials
  • Git access tokens
  • Libraries
  • Skills

You get an organization when you sign in for the first time. Each person that you invite joins the organization with a role. Owners and admins can open the credential screens and the member screens. Other roles cannot open these screens.

A project is a workspace in an organization. A project holds agents, conversation threads, and notes. Each project has its own member list. Most teams start with one project for each product area.

The agent answers the user. An agent has these properties:

  • A model
  • System instructions
  • Attached libraries
  • Skill tags
  • Attached knowledge collections
  • A JSON vars object, which Urai gives to each run

The API and the SDK use the name assistant for this object. Paths start with /api/assistants/, and payloads contain assistant_id.

An agent runs in one of two modes.

Advanced mode runs a loop that writes code. The agent calls write(name, code) to save a JavaScript file. The agent then calls execute(name) to run that file in the uraiJS sandbox. The agent reads the result and does these steps again until it calls final_answer. An advanced agent imports your published libraries, and it loads skills by tag.

Each execution starts in a new runtime. The files that the agent saves stay available for the thread, but the variables do not stay available. Three limits control the loop:

  • A step limit of 50 steps or fewer
  • The allowed-secrets list
  • The network allowlist

Simple mode runs a chat loop over a fixed set of remote tools. A simple agent cannot use libraries or skills. Use simple mode when the agent only answers questions.

An agent answers in three places. It answers in the Urai interface, it answers in a widget that you embed, and it answers your own code over the Agents API. Each door runs the same agent. Turn the Agents API on for one agent at a time, because an enabled agent answers any API key in the organization.

A collection is a group of documents that you upload. Urai keeps collections in your organization. You attach one or more collections to an agent. You can also name collections in a request to the Chat Completions API.

Urai accepts these file formats:

  • PDF
  • DOCX
  • PPTX
  • Markdown and plain text

Urai extracts the text from each file and makes Markdown from it. Urai then divides the Markdown into chunks and indexes the chunks. A document has the status pending, processing, ready, or failed. Only a document with the status ready is available to search.

Urai searches a collection with two methods at the same time. BM25 finds the documents that contain the words of the query. Vector search finds the documents with a related meaning. Urai then combines the two sets of results. This method is hybrid search, and it finds a document even when the user writes different words from the document.

An agent with one or more collections gets two more tools:

  • search_documents returns the passages that match a query. Each passage carries a citation with the name of the document. The citation also gives the page number for a PDF file or a PPTX file.
  • read_document returns the full text of one document. Urai sends a long document in more than one part.

An agent with no collections does not get these two tools. Search knowledge collections shows you how to use the same tools from the Chat Completions API. The Knowledge API gives you the collections and the search on their own, for code that writes its own prompt.

A library is reusable TypeScript code. An advanced agent imports a library as urai:lib/<org>/<name> in the code that it writes. Libraries run in the uraiJS runtime. This runtime is a sandboxed JavaScript environment, and it is not Node. A library has fetch, timers, web crypto, and npm imports. It has no filesystem and no process access.

Write a library with the uraijs CLI. Push the library to the git remote of your organization to publish it. Then attach the library to an agent in the agent editor.

The agent does not read your source code. The agent reads AGENT.md from the library, because Urai puts that file into the prompt. The agent also reads docs/llms.txt if you publish that file. These two files make the library usable. Write them with the same care as the code.

Two channels move data in and out of the code that runs:

  • meta.vars holds the variables of the agent and the context of the conversation. Urai always adds thread_id, organization_id, and user_id. The secrets that you permit have the names meta.secrets.NAME.
  • meta.urai.sendCommand(threadId, payload) sends a message to the browser before the turn is complete. The agent uses this method to tell your application to go to a page or to refresh a view.

A skill is a playbook in Markdown for one task. Urai keeps skills in your organization, and each skill has tags. Each agent has its own list of tags.

The prompt of the agent lists each published skill that has one or more of those tags. The list shows the name and the description of each skill. The agent calls load_skill to read the full text when the task occurs. The agent calls search_skills when the correct skill is not obvious.

Skills change independently of agents. When you publish a skill, or when you change the tags of a skill, your agents get new abilities. You do not edit an agent. A skill has three states:

  • Candidate, for a skill in review
  • Published, for a skill in live use
  • Archived, for a skill that you no longer use

Urai can also write a candidate skill from a conversation that was successful. You then edit that candidate.

Simple agents use a third mechanism: remote tools. A remote tool is an @tool function that the model calls directly. Advanced agents do not need remote tools, because they import libraries in the code that they write.

A workflow is durable TypeScript code that runs without a person. Use a workflow when one event starts several operations, and these operations must occur in sequence. Each operation can fail on its own. Compare a workflow with the other two objects: an agent answers a person, and a library does one function call.

You write a workflow as a coordinator class and a set of steps. You push the workflow like a library, then you register it. Registration sets the envelope. The envelope holds these permissions:

  • The secrets that a run can read
  • The hosts that the workflow declares
  • Permission to call a model
  • The integration connections that the run uses

The envelope belongs to the workflow. A trigger cannot make the envelope larger.

Runs are durable. The coordinator replays at each tick. The steps that are complete return their stored results. Urai keeps no connection open between the ticks. A run can therefore wait at awaitApproval() for one week, and then continue. Workflows shows the full procedure.

The widget is the chat interface that your users see. A widget belongs to one agent. A widget holds its own token, allowed origins, theme, layout, and behavior. The theme, the layout, and the behavior are presentation only. You can change the style or the welcome message from the console, and you do not change your application.

Two rules are more important than the other rules:

  • The token identifies the widget in public. Use the token as a public identifier. Do not use the token as a secret.
  • Urai checks each request against the allowed origins. This check includes the stream that sends the reply. A request from an origin that is not in the list receives a 403 error. Other sites therefore cannot use your widget. This rule is not authentication.

The visitor id that your page sends is opaque text. Urai does not compare this text with a user table. Vars come from the browser. The visitor id and the vars cannot carry authorization. Securing your app tells you what to do.

These steps occur when a visitor sends a message:

  1. The visitor types a message in the widget.
  2. The widget sends the message to the chat service. The widget also sends its token, the visitor id that you supply, and the vars for that thread.
  3. The agent runs, and it calls its tools. Each tool receives those vars.
  4. A tool can send a command on the open stream.
  5. The reply goes to the widget in small parts.
  6. Your page receives each command as an event, and your code can act on that event.