Repository navigation
Home
A universal AI agent that runs inside a Roblox client. It works in any game, with Chat Completions and Anthropic Messages endpoints, including compatible local servers and relays.
Project UAI 3.6.6 — October 23, 2026: a provider gallery and a clearer queue.
Adding a provider opens a searchable gallery with Recommended, Paid and Local
tiers. Each card describes the route and what access costs, hybrid services are
labelled by their free half, and choosing a card seeds the editor with its URL,
protocol and auth style. The catalog grows to 66 presets, led by Project UAI Free,
AgentRouter, OpenCode Zen, OpenRouter and OpenCode Go. Queued follow-ups open as
Up next with numbered cards and per-message controls, and Settings opens from
one sidebar row. The client is 3.6.6; the bridge remains bridge-v3.6.6, and the
Windows and Android downloads remain 3.6.5 until their own builds.
The apps retain verified automatic downloads and updates. The public page has a responsive white monochrome design with an interactive 3D centerpiece and animated SVGs, with pause controls and reduced-motion support. Bridge responses put the answer first, with tools and thinking grouped into expandable activity. OpenCode discovery covers more installed CLI layouts and reports useful setup guidance. The redesigned Android companion makes starting and checking the connection simpler, and accepts compressed message submissions that previously failed JSON decoding after pairing. Both apps retain verified automatic updates, and Android installation still requires confirmation.
Start chatting with Project UAI Free without supplying an API key. It is the
default when no provider is configured. Its adaptive router measures small tool
requests and prefers verified fast HCNSEC models when compatible; otherwise it
keeps the provider's auto route. Free models have rounded green outlines.
Thank you for continuing to use and support Project UAI. Capacity and response
speed vary; more free providers may become available soon. Join
our Discord for help, provider news and updates.
OpenCode's direct desktop workaround uses the verified desktop identity and keeps its required compatibility tool declarations, without requiring the bridge. The Windows bridge adds an optional path through an installed OpenCode CLI server; the CLI binary is not packaged. Mobile OpenCode requires the UAI Relay app from Discord; install and start the app, then connect it in UAI. The retired OpenCode proxy is removed; only AgentRouter uses the proxy's proper domain. Saved OpenCode proxy URLs migrate back to Zen.
Go subscribers choose the separate OpenCode Go preset at
https://opencode.ai/inference/go/v1 and a key from their subscribed OpenCode
workspace. Go keeps its own catalog and routes; the optional free CLI backend
does not intercept it. Subscription and workspace region refusals identify the
setting to check. See provider setup.
The client is 3.6.6, the bridge is bridge-v3.6.6, and the Windows desktop and
Android companion remain 3.6.5.
UI LIB remains 1.4.0 and SDK 1.0.0.
Model pricing and context/reply limits use a refreshed provider-specific catalog;
included Project UAI Free access is counted at zero cost.
See the release notes, script development manual
and UI library guide. The client exposes the scripting
manual through scripting_docs: start with quickstart, then read only the
sections relevant to the task. It covers exact tool calls, writing and checking
saved scripts, debugging, cleanup and decompilation.
New configurations default to Low reasoning effort for faster responses. Existing saved effort stays unchanged; choose Settings → Agent → Effort → Low to lower it. The model modal edits the same global effort setting, including for other models when Options → Reasoning support is enabled. The Thinking row estimates visible text only. A long retained trace is labelled Excerpt, not a 5,999-token model limit. See provider behavior. See 3.6.6 verification, 3.6.5 verification, bridge 3.6.2 verification, 3.6.1 verification, 3.6.0 verification, mobile research and companion update checks. Native Android, mobile executor behavior, phone tool execution/replay and background survival remain unverified; the successful OpenCode text/tool/replay probes used the companion's Java HTTP library on Windows. A user later confirmed OpenCode inference on a Delta Android phone through the UAI Relay app, so mobile OpenCode now requires that app from Discord. The 3.3.5 verification record retains earlier bridge checks, and the 3.0.0 audit ledger retains its module and tool coverage.
loadstring(game:HttpGet("https://raw.githubusercontent.com/Project-Ptolemy/ProjectUAI/main/dist/uai.lua"))()Nothing about a specific game, gateway or host script is assumed. Under an
executor it uses the executor's HTTP function; in a plain client it falls back to
HttpService and says which capabilities it lost. Embedded in a host script it
takes a context table and adds that script's own instructions and hooks.
Running the same bundle again toggles the existing interface; embedding hosts can
pass { reuse = true } to reuse it without toggling. A changed bundle
reloads an idle client after saving its settings and conversations. If work, an
unsent draft, or an isolated conversation would be lost, the current instance stays
open with a notice; finish that work before running the updated loader again.
In Settings → Import & export → Full configuration, use Copy config · includes API keys to transfer all saved configuration, including providers, key pools, custom headers, permissions, preferences, and saved memory. On the other device, choose Paste configuration to import, paste the JSON, select Review, then Apply. This private export contains credentials. Conversations, activity history, workspace files, and skill files are separate from configuration.
Our own script UI library. Project UAI UI LIB provides
standalone, responsive windows and a complete set of script controls using UAI's
visual language. Agents read its bundled ui_library_docs reference and write
declarative controls plus application logic. The library handles layout, input,
configuration, cleanup, and the fixed Project UAI | UI LIB. footer.
The existing agent client interface is unchanged.
local UI = loadstring(game:HttpGet("https://raw.githubusercontent.com/Project-Ptolemy/ProjectUAI/main/dist/uai-ui.lua"))()See the starter, component showcase, full API and application guide, and complete assistant workbench. The embedding reference covers host scripts, custom UIs, sessions, tools, hooks, providers, state binding, and cleanup.
Embedding SDK 1.0.0. Load the client with { ui = false, reuse = true } to
use sessions and tools without mounting its application. uai.sdk provides
owned integration scopes and requests with structured results, cancellation,
completion callbacks, and optional waiting. Open named conversations without
changing the user's selection, and explicitly mount the app when needed with
uai.show(...) or uai.openSession(id). Start with the
UI-free SDK example and
SDK quickstart. Providers and permissions remain
shared client settings; runtime capabilities still depend on the host.
A real agent loop. Streaming, parallel tool calls, retry with backoff that
honours Retry-After, provider fallback, automatic context compaction with a
summary, permission gating, hooks, a task list, persistent memory, subagents that
run several at a time, token and cost accounting, abort, and a request log. Every
stage emits an event, and the interface is a subscriber -- the loop never touches a
GUI.
Context windows are learned automatically from provider context-length errors and
saved per model. A rejected turn compacts older history and retries once before
falling back. Repeated compactions merge the previous summary, and a failed summary
request preserves earlier facts. Open Message options → Context breakdown for a
colored usage bar, category totals, model window, and compaction point. Compaction
notices show the estimated token reduction. Individual saved facts can be deleted
in Settings → Skills → Memory. Prompts include a bounded index of saved memory
keys; memory_read retrieves relevant values by exact key or search, with
continuation offsets for longer results. Old workaround notes stay available
without being injected as instructions into every request.
The shared prompt favors direct action and focused verification. Specialized workflow sections follow each conversation's available tools; routine work needs no mandatory task list or delegation. This reduces prompt overhead without lowering the configured model context window or changing permissions.
A turn stops after twenty-four tool rounds by default, which is there to catch a runaway rather than to end the work; Unlimited tool calls in Settings removes that ceiling and the fifteen-minute turn deadline with it, leaving the repeat breaker, each tool's own timeout and Stop as what bounds a turn. A subagent keeps its own step and time budget under that switch -- it is the one session nobody is watching -- and Unlimited subagents is the separate switch that lifts a child's too, so a delegated job runs until it answers instead of coming back with "I reached this session's step limit before finishing".
Any provider. A provider is a base URL, an auth style, a key and a model.
Add a provider opens a gallery of 66 presets with three tiers -- Recommended,
Paid and Local -- and a search field that spans them. Recommended leads with
Project UAI Free, AgentRouter, OpenCode Zen, OpenRouter and OpenCode Go; hybrid
services are labelled by their free half rather than as paid, and "Custom endpoint"
takes anything that speaks /v1/chat/completions -- a relay, a self-hosted vLLM,
Ollama on localhost. Model lists are never guessed: they come from
GET /v1/models or from you typing one in.
Project UAI Free is ready when no provider is configured. Add it later from the Recommended tier, or from the start card's Project UAI Free row; existing providers and personal keys stay as configured. AgentRouter can recover from explicit unauthorized-client errors using the Project UAI proxy. The proxy receives the provider key and request and has a shared limit of 90,000 requests per day; the resulting Base URL remains editable.
AgentRouter uses the Anthropic Messages API and always sends its required Claude Code identity. Registration requires a GitHub account at least one year old.
The Claude Code identity. Providers that enable this compatibility identity carry
User-Agent: claude-cli/<version> (external, cli), x-app: cli and the
X-Stainless-* client-metadata set, applied inside the transport so no call site
can omit it accidentally. OpenCode Zen retains its OpenCode compatibility headers. Its free-tier access is
provider-controlled, so compatibility headers do not guarantee availability.
HttpService:RequestAsync refuses to send a custom User-Agent, so
on a host with no executor HTTP function the client says the identity did not
reach the wire rather than pretending it did.
Native tools for any game: the instance tree, properties with type-aware conversion, bounded Luau execution, files, HTTP, web search and page reading, players, your character, raycasts and lighting and the camera, remotes (discover, fire, watch), on-screen interfaces, diagnostics, place and account metadata, plus the agent's own task list, memory, searching and reading its earlier conversations, and subagent dispatch.
Network and payload capture. net_capture observes a loader's executor and
game HTTP, HttpService calls, loadstring/load/require chunks and queued
code with fixed forwarding hooks. It records bounded request/response bodies,
redacts credentials, links a response to the chunk it produced, flags code-like
responses that never compile, and excludes UAI's own traffic. Stop restores the
hooks; nothing is rewritten, replayed or executed by the capture. See
the capture guide.
Tool profiles, evidence and support. The default All tools profile keeps
the complete catalogue. Leaner profiles declare a core set for a smaller request,
while tool_search lists, describes and calls every hidden tool with the same
permissions and conversation scope. Large tool results (8 KiB+ by default) are
retained in a per-conversation evidence ledger (evidence list/search/read) so
compaction does not lose the full receipt. support_bundle writes a local, redacted diagnostic
file for bug reports, excluding keys, memory, conversation text, file contents
and captured payloads. See the tool context guide.
Code workspace — v1.7.0, September 24, 2026. Open Code and
choose Editor, Files, Explorer, Remotes, Output, History, Library, or Game changes.
The user and agent share live Luau documents and revisions. Source highlighting,
line numbers, Find, Go to line, indentation, explicit Run/Stop, retained output,
versions, comparison/proposals and typed reusable actions use the same model.
Focused editing uses the native TextBox for input, selection and IME, with visible
syntax colors and a caret while typing. Closing a view preserves its script in the
library. Source documents use two verified snapshots under UAI/code/.
Inspected Source and decompiled text are read-only snapshots. Extract editable copy creates a separate document; opening source never runs it or writes back to the live script. Source requests share bounded workers and a cache, with explicit provenance, refresh, cancellation and expiry. Search/selection use UTF-8 byte boundaries; displayed columns count Unicode code points.
Files browses the UAI workspace through expandable folders. Open a file in the
editor, then use Save or Save as; disk-conflict checks protect edited drafts.
Run and Save stay adjacent. Buttons and tabs retain padding across narrow layouts,
touch input and larger text, with horizontal scrolling for crowded action strips.
Explorer provides lazy child pages, name/class/tag search, multiple selection, supported properties, typed attributes/tags, guarded edits, hierarchy operations, bookmarks, world picking, source opening and selected metadata export. Stable object references survive renaming and moving. Game changes → Undo covers recorded property and attribute changes with conflict checks. Tags, hierarchy, remote effects and arbitrary Luau side effects are outside that Undo coverage. Inspector actions carry the displayed selection's IDs, primary object and revision. Search and branch pages show partial results and limits: 20,000 runtime children or scanned nodes, 4,000 displayed rows, and a 64-level reveal path.
Remotes starts only through an explicit scoped Start. It supports UAI calls, incoming events and host-dependent outgoing interception, with bounded capture lists, typed values, filters, replay drafts, reviewed one-shot replay, generated source and offline export/import. Excluding logs and blocking traffic are separate controls. Stop disarms traffic rules; capture stays visible on the minimized launcher. A timed-out InvokeServer may remain outstanding and must not be retried automatically. More → Capture coverage explains the actual backend and limits; intercepted Invoke outcomes remain unverified and incoming function callbacks are unavailable.
The default capture lasts 30 seconds and requires a selected target. Game-wide
capture and continuous capture are explicit choices. Caller actions resolve the
captured script identity after capture and can open read-only source with likely
remote name/path matches. Generated portable scripts require current review before
copy/export. Hooked traffic is labelled hooked_unknown unless attribution has
been verified; routing probes do not invoke the predecessor, but network behavior
depends on the host. Stop can retain an inert forwarding wrapper.
See the native feature contract for current features and limits and the native testing guide for desktop/mobile/gamepad scenarios. Native input, hook forwarding and performance require Roblox client validation.
Batch inspection and file workflows. Prefer a combined query or batch when the work is independent; this reduces model round trips and unnecessary local scanning without changing the inference provider's speed. The prompt asks for successive batches of normally 1–4 independent calls, with results inspected between batches. Tool-call limits and concurrency stay unchanged.
Script projects. Stage modular scripts with project_scaffold, inspect their
file hashes and dependencies with project_map, and apply coordinated edits with
project_patch. script_analyze reports syntax/dependency diagnostics;
project_build exports one runnable Lua file; script_test runs declared cases
through the managed client engine. Proposals include temporary, conditional
recovery checkpoints. See the script project guide for
the manifest, test API and limits. These are native tools, without an LSP or an
isolated test process.
Long inputs are files. Inputs over 8,000 UTF-8 bytes are saved intact under
UAI/pastes/, up to 10 MiB per file. The AI receives a compact path reference and
reads relevant sections with file_read, file_read_many, or file_search.
Saved-paste reads return at most 6,000 source bytes per slice with continuation
offsets. Ordinary messages stay inline. A large paste becomes an attachment while
keeping the text already around it editable, and an attachment can be sent by
itself. The browser transfers large files in separate chunks before sending the
message. Saving must succeed and the Files tools must be available; failures keep
the draft instead of sending the full source. These are files in the executor's
workspace, so the workflow works across inference providers without requiring a
provider-specific file API.
A folder per game. Game work is organised under UAI/files/<place name> (<PlaceId>)/: scripts the agent writes or edits sit in that folder's root, and
decompiled or dumped source lands in its dump/ subfolder, apart from the code
being authored. The name is taken from the live place and stripped of any
path-reserved characters. This is the default home, not a wall -- shared
utilities, another place's folder, cross-game notes, and pastes/ all stay
reachable, so the agent still reads and writes elsewhere under files/ when the
task or the user calls for it.
Project Gravity. Run the updated Project Gravity
loader and open its PROJECT UAI button. Both desktop and mobile pass their live
context; UAI can also discover Gravity when either application starts first, or run
the loader itself with gravity_launch when Gravity is not already present.
gravity_status and gravity_shapes inspect the real engine and shape controls.
gravity_control, gravity_configure, gravity_shape, and gravity_target use
Gravity's native handlers for physics, settings, shape switching, buttons, and
player targets. The Project Gravity tool group uses UAI's normal permissions.
gravity_parts pages through held parts with session-scoped IDs and their current
selection, targets, and overrides. Use those IDs with gravity_part_control to
select parts, pin them, assign a shape, move a group while preserving its spacing,
set rideability or physics, and release overrides. clear deselects;
release_all clears overrides even on unselected parts. Selection follows
Gravity's native 512-part ceiling. Read fresh IDs after Gravity reloads.
gravity_keybind sets core or shape shortcuts and rejects conflicting keys;
an empty key clears a binding. gravity_favorite manages the native favorites
list. gravity_configure also exposes interface and visual performance settings,
FPS (when supported), RGB core color, ignore tags, and Part Control panel defaults.
gravity_control supports reset_settings and manual Slingshot launch/charge.
The companion Gravity update applies settings to the live world and interface,
restores reset effects and bindings, and supplies the same shape/hotkey handlers
on desktop and mobile. Check gravity_status.capabilities after reloading it.
For custom shapes, read the guide with gravity_plugin_read, or request
section="template". Pass plugin="My Shape", path="pastes/..." to
gravity_plugin_write to reuse uploaded source without copying it into tool
arguments. It verifies the file in GravityShapes/, validates setup once, and
registers it with the running Gravity session. Select an inactive shape explicitly
with gravity_shape; replacing an existing shape requires overwrite=true.
load=false checks syntax and saves without executing the source. Gravity's live
physics and mobile touch behavior still need in-game testing.
| Tool | Use |
|---|---|
instance_query |
Filter by name, class, and tag while reading selected properties and attributes. Pages stop early and include a traversal offset. |
instance_get_many |
Inspect up to 20 known paths with individual errors and a continuation index. |
file_search |
Find literal text across workspace files, with filename globs, case selection, line numbers, byte offsets, and a cursor. |
file_read_many |
Read up to 12 files or saved pastes with a shared output budget, individual failures, and per-file continuation offsets. |
file_edit_many |
Apply up to 20 ordered exact edits to one file after all edits validate, using one write. |
Instance projections accept up to 12 property names and 12 attribute names.
Copy returned instance paths exactly: Workspace["Map.v2"]["Door[1]"] refers to
names that contain punctuation. Query pages inspect at most 20,000 nodes; narrow
the root if a scan is incomplete. Traversal offsets describe the live tree, so
restart after it changes.
File searches default to UAI/files/; pass an explicit pastes/ path to search
saved inputs. Both scopes exclude client configuration and conversations.
Searches skip binary files and files over 2 MB, bound inventories to
128 directories, 1,000 files, and 6,000 entries, and process at most 8 MB and 50,000
new lines per page. The read budget counts skipped data too and is checked between
whole-file reads; the host must read a file before its size is known.
Follow the returned cursor with the same query and restart
after editing sources. Batch reads support files up to 2 MB and reuse repeated
slices within that call. Edits preflight against the original file, refuse stale
contents, avoid no-op writes, and limit the original and resulting file to 2 MB.
The existing Files and Instance tree permissions and capability checks apply.
An interface built from tokens. No use site writes a colour or a number. One
warm neutral ramp and one accent, surfaces separated by two steps of lightness and
a hairline rather than by shadows, a cream fill for the single loud action per view,
and the accent kept for meaning -- inline code, a running turn, a risk level. Type
comes from FontFace where the client has it, so a family yields a real
regular/medium/semibold axis instead of whichever weights the legacy Enum.Font
happened to pair it with; the families on offer are probed against the engine rather
than declared, so the list is shorter on an older client instead of containing dead
entries. A reply is the page rather than a bubble on it: the agent's prose sits flat
on the canvas, tool rows and reasoning are lines of text rather than cards, reasoning
sits behind a rule as an aside, and sent prompts group their speaker and text inside a quiet bordered surface.
The compact composer stays pinned to the bottom edge, with context details behind
its overflow menu. The model stays inline when space permits; model, permission,
and usage controls are always available from the same menu. The contrast
of every pair the interface puts together is computed in the test suite, so a retune
cannot quietly make something unreadable.
Markdown tables and compact thinking. Replies support aligned pipe tables with formatted cells and horizontal scrolling on narrow screens. Long tables and thinking traces have bounded scroll areas without dropping their content. Thinking starts collapsed; consecutive trace updates share a disclosure until a tool separates them. The model picker keeps search and selection stable, and its provider selector and the composer's model chip size to their actual labels.
Mobile layout. The desktop header, sidebar, welcome view, composer, Quick Chat and Code tabs share one structure at 55% layout dimensions, spacing and radii before density settings. Controls have a 15px minimum, text starts at 10px before text scaling, and standard icons at 12px. Enter adds a line; the Send button submits. Expand the composer for a longer draft, or use Message options for models, attachments and context. The sidebar and app menu keep conversation search. Settings categories and providers reflow between columns and scrolling strips while preserving their mounted forms.
Auto keeps a compact placement; Sheet, Panel and Window select bottom, right and centred placement, with separate saved positions. Drag the title to move, use the corner grip to resize, or expand to fill the available screen. Rotation preserves drafts, selections and open forms; the keyboard temporarily lifts the window and keeps focused fields in view. The launcher returns when minimized.
Managed in-game chat loops. Ask for a quiz, rotating announcements, or keyword
replies. quiz_bot, auto_chat, and auto_reply return a background job immediately;
chat_loop_status reports progress and quiz scores, and chat_loop_stop stops jobs.
Active jobs also have a Stop all control in chat. Loops have configurable intervals,
counts, and durations, and stop when their conversation is cleared or removed, chat
is disabled, or the client unloads.
Infinite Yield control and plugin authoring. iy_control inspects and changes
IY's native event bindings, keybinds, aliases, waypoints, command prefix, and supported settings. It
supports OnExecute, OnSpawn, OnDied, OnDamage, OnKilled, OnJoin,
OnLeave, and OnChatted, including player/message/health filters, delays, and
$1/$2 command arguments. Changes refresh IY's editor and request its normal
save; results say when only live session state is available. Inspect first for
current binding indexes. stop_loops sends IY's breakloops command, which stops
repeat prefixes; event bindings and individual commands' own loops are managed
separately. The adapter follows the upstream
event editor and plugin loader,
reviewed September 20, 2026.
iy_cmds includes argument signatures and short descriptions when the running IY
exposes them. iy_players resolves selectors such as others, rad50, and
all-me to live names before a targeting command. It uses IY's own selector
engine, including comma-separated lists and @name username-only prefix matching.
Its text is bounded by limit; structured results retain the complete name list.
Use alias_add, alias_remove, or alias_clear to manage aliases. An alias
targets a command name, including an existing command alias; arguments are not
stored in it. waypoint_add accepts {x,y,z} coordinates or uses your character's
current root position, flooring each coordinate to match IY. waypoint_remove
and waypoint_clear affect the current place; waypoint_clear with
all_places=true also clears saved waypoints in other places. Inspect
section="aliases" or section="waypoints" and follow nextOffset for more.
Edits preserve the live table references used by IY's GUI and request its save.
configure also supports gui_scale (0.4–2) and logs_webhook (an HTTP(S) URL,
or an empty string to disable). These dispatch IY's own commands asynchronously;
inspect settings to confirm the resulting values. Those commands save through
IY, so they reject persist=false; other native edits support session-only changes.
iy_plugin_read without a filename returns a multi-command template. Pass a
filename to read an existing plugin, then use iy_plugin_write to create or
update it. Source can declare globals or shared locals above local Plugin,
and returns the normal IY table with PluginName, PluginDescription, and
Commands. Every command has ListName, Description, Aliases, and
Function(args, speaker). The writer checks syntax before saving, executes setup
once when loading, validates the returned table before replacing commands, and
reports their actual registered names, including IY's collision suffixes.
load=false saves without executing; replacing a file requires overwrite=true.
Plugin source and commands appear in the tool transcript. Plugin-owned connections
and loops should have their own cleanup command so reloads do not duplicate them.
For example, iy_control can bind speed 40 to your next spawn:
{"action":"event_add","event":"OnSpawn","command":"speed 40","conditions":{"player":"me"},"delay":0.5}Resolve nearby players with iy_players:
{"selector":"rad50-me","limit":20}Create an alias or a waypoint with iy_control:
{"action":"alias_add","alias":"quick","command":"speed"}{"action":"waypoint_add","name":"Home","position":{"x":100,"y":20,"z":-50}}Task and notification layout. Task markers share their text's line box at each font scale, long plans scroll within a bounded area, and disclosure choices stay with their conversation. Skipped steps are reported separately from completion. Notifications have a clear outcome, conversation title, and an Open chat action. Opening a conversation acknowledges its own notifications; other unread conversations keep their badge. Long notifications scroll, hover/focus pauses expiry, and the stack fits the available screen and keyboard space.
Independent in-game chatbot. chat_bot listens to new player messages and
answers with the selected AI provider/model using its own short conversation memory.
Set instructions for its personality, prefix (default [AGENT]), and optionally
user_ids to choose players. It returns immediately and appears in the chat-loop
indicator; chat_loop_status reports its progress and chat_loop_stop stops it.
By default it runs for 600 seconds, sends at most 100 replies, and waits at least
5 seconds between sends. Rapid messages are batched into one response. Incoming
message IDs and short-window repeats are deduplicated, self/system/tagged-bot
messages are ignored, and repeated reply text is suppressed for the entire run.
Only one managed job owns a channel, and manual chat_send calls are blocked there
while the chatbot runs. A failed or ambiguous chat send stops the bot without retrying.
Every listing the model produced, in the transcript. A tool call that carries code -- the Luau it is about to execute, the body it is about to write to a file, the property map it is about to apply -- draws it under the row as numbered, horizontally scrolled, copyable monospace, outside the fold and on by default. A call's listing folds at a dozen lines rather than sixty, because a turn produces several of them and the answer they were working towards has to stay on screen. Long blocks fold with a control that opens them rather than a sentence saying how much was hidden. The remaining arguments and the result sit behind the row's own caret, and a failure opens its own. Subagents forward their calls whole, so work delegated to a child is as readable as work done in the main conversation.
A turn's machinery is one block, not a wall. The calls, the thinking between them and any retry notice go into a single activity block with tight lines inside it and a paragraph of air around it -- so a turn that called eight tools reads as one thing that happened rather than as eight events with the reply lost at the bottom. The header counts the run and its duration, and a finished run of more than four folds itself away behind that line; anything still outstanding keeps it open.
Conversations run at the same time. Switching conversation does not stop the one you left: its loop is on its own thread, the sidebar spins on it while it works, the header says how many are going, and the launcher's dot pulses with the window closed. Each conversation keeps its own task list, and a permission prompt raised by one that is not on screen still appears -- named with the conversation that is asking, rather than timing out three minutes later and reporting to that model that you refused.
Subagents are managed, not just watched. The Subagents panel is the register: every dispatch this session made, running ones first, with the task, which conversation asked, what it is allowed to touch, the tools it has called, how long it has been going, and a stop for each one that does not stop the turn that dispatched it. Finished dispatches stay with their report. The four ceilings -- budget, how many run at once, steps per subagent, and how deep delegation may go -- are on the same panel, and the last two had no control anywhere before it.
A dispatch is a conversation, not one question. Every report carries the
subagent's id, and agent_followup sends that same child another message with
everything it found still in context: carry on where the step limit stopped you, now
check this too, quote that line exactly. The transcript shows the second turn as a
follow-up on the same subagent rather than as a new dispatch, and the newest few
finished dispatches keep their context so there is something to follow up on.
A sidebar holds the conversations, grouped by the place each happened in, with search across their transcripts and two arrows that walk where you have been. It collapses from the header and comes back the same way, and a conversation reopened after a restart shows what was said in it rather than a greeting -- the transcript is written to disk alongside the model's own context, which is the half that used to travel alone. An empty conversation opens with prompt starters and an expandable activity card: conversations, messages, tokens, active days, streaks, the busiest hour, the model that did the most work, and six months of daily activity as a grid. Every figure on it is counted from what this client observed and kept -- there is no sample data anywhere in the interface, and a number with nothing behind it is rendered as a zero and says why.
Long conversations keep dialogue separate from tool activity, so a busy group of workers cannot push out your questions and replies. History limits are shown when older detail is removed. Resizing and maximizing preserve the live view; layout rebuilds remember where you were reading. Message options → Refresh conversation redraws messages and current activity without restarting the script or losing the draft. Older saves recover missing text when it still exists in saved model context.
Providers are a list and a detail pane, not a card grid and a modal form. The detail names the endpoint a request will actually go to, the model-list route, the wire protocol, whether a socket is configured (and therefore whether a long reply can arrive at all), the health counters, the last observed latency, how long a benched provider has left, and the extra headers, body fields and query parameters the record sends. The API key is never rendered: it is set through a prompt and shown as its last four characters.
The layout follows the live viewport: compact desktop controls on handhelds,
a floating resizable window on a desktop, and a large centred panel on a console.
It reflows on rotation and resize, lifts above the on-screen keyboard, and
respects ReducedMotionEnabled. Handheld placement follows the selected Auto,
Sheet, Panel or Window mode.
init.lua bootstrap: builds env, mounts the app
src/runtime/ util, signal, clock, caps, place, fsx, log, config, dispose
src/net/ ua, http, sse, ws, bridge
src/provider/ catalog, registry, openai, anthropic, chat, models, traits
src/agent/ prompt, context, schema, registry, permissions,
hooks, state, usage, stats, loop, session, subagent
src/tools/ native tool groups behind one registry
src/ui/ theme, responsive, icons, primitives, controls,
overlay, markdown, window, sidebar, app,
settingsrows, settingspanes, chat/*, panels/*
bridge/ the optional web chat: node server plus its page
dist/uai.lua the built single file
SPEC.md is the contract every module is written against: the loader, the env
table, the provider record, the tool handler signature, the event stream, what is
counted and where it is persisted, and the authoring rules.
Complete all source, test, build-tool and documentation edits before verification. The native command runs build, freshness, static checks, the main and every focused native suite, performance contracts and the official Luau compiler, in that order. It stops at the first failed stage; after any fix, restart the entire command.
node tools/test_native.jsPrerequisites are Node.js, LuaJIT and the official luau-compile executable. Set
LUAJIT or LUAU_COMPILE to override paths. Stage logs and a behavioral coverage
summary are written to ignored refer/native-verification/. There is no line
coverage claim. Sources use a LuaJIT-compatible dialect; the native static checker
enforces it, and the official compiler checks actual Luau syntax. See the testing
guide for the exact stages and compiler setup.
Build-only commands, also after implementation is complete:
luajit tools/bundle.lua --native
node tools/build_site.js --bundle-only --checkThe bundle includes a deterministic build ID and per-module hashes in
dist/uai.manifest.json. Freshness is read-only and also detects stale embedded
icons. These hashes identify content; they are not cryptographic signatures.
test/run.lua loads dist/uai.lua -- the actual artifact -- into a mocked
client: a virtual clock so nothing sleeps, an in-memory filesystem, a programmable
HTTP layer that records every request, and an instance mock that resolves absolute
geometry, type-checks property assignments and reports any unknown property or
enum. The scenarios cover boot, capability degradation, the identity headers on
the wire, model discovery, the tool loop, parallel calls, SSE assembly, retry,
provider fallover, permissions, the repeat breaker, abort, context trimming,
payload shape, argument repair, path traversal, viewport changes, theme changes,
markdown, subagents and the parallel dispatch of several at once, a subagent
resumed with a follow-up in the context it already had, the unlimited step budget
for a turn and the separate one for a child, persistence, error surfaces, window
drag and resize, overlay interaction, the layout invariants every surface has to
hold, and the contrast of every colour pair the interface puts on screen. They
also cover the two failure
modes that are invisible from inside a single turn: that the sidebar's collapse
control actually collapses it and offers a way back, and that no string leaves this
client without being valid UTF-8 -- a scraped snippet with one Latin-1 byte in it
used to poison the message history and kill every following request with a
positionless "Can't convert to JSON".
They also pin the interface to real state, which is the part that is easy to fake: that the activity card counts only what was recorded and survives a restart, that a history from before the counters existed is recovered from the transcripts rather than invented, that the conversation list is the threads the client has, that the permission chip reads the mode actually in force, that an attached file travels with the message, that a tool family switched off leaves the wire, that every settings pane builds, and that search finds a conversation by something said inside it.
luajit test/run.lua --native identity # run one native scenario
luajit test/mock/selftest.lua # check the mocks themselvesLune can also compile all source modules and the shipped bundle with the real Luau compiler, then run a scenario file through the same offline harness:
lune run test/lune_runner.luau test/mobile_ui.lua
lune run test/lune_runner.luau test/mobile_workflows.lua
lune run test/lune_runner.luau test/shared_ui_layout.lua
lune run test/luacid_fallback.luauThe last check needs only Lune. It compiles the vendored luacid asset with real Luau, which the LuaJIT harness cannot, and verifies the fallback installs through its pinned environment and calls the service as the client expects.
These checks use mocked Roblox services. They verify code, geometry and interactions, but do not render native Roblox text or GUI layouts.
For landscape image review, test/mobile_snapshots.lua exports UI trees from the
built bundle under Lune. node test/render_mobile.js <snapshot directory> renders
those trees with Playwright/Chromium; set PLAYWRIGHT_MODULE to an external
Playwright installation if needed. These are approximate layout previews. Passing
a second snapshot directory compares desktop UI trees with the previous build.
Native rendering and keyboard behavior should also be checked in the Roblox client.
The public site lives in root index.html, style.css, and script.js. The
docs/ copies support GitHub Pages configured to publish from that directory.
Edit the root files, then regenerate the marked catalog and release regions:
luajit tools/bundle.lua
node tools/build_site.js
node tools/build_site.js --check
node --check script.js
python test/site_static.pyThe site build uses Node and LuaJIT without npm dependencies. It reads the actual bundled registry through the offline client harness and refuses stale bundles. The check mode does not write files. Static checks verify HTML structure, local links, accessibility references, copy targets, catalog coverage, and publishing parity without opening a browser or loading media.
For focused browser checks, install Playwright and its Chromium browser outside
this repository. Point PLAYWRIGHT_MODULE at that installation's package directory:
$env:PLAYWRIGHT_MODULE = 'C:\path\to\node_modules\playwright'
node test/site_browser.jsThe browser gate checks navigation and focus, exact loader copying and clipboard
fallback, catalog filters, official release targets, responsive geometry, reduced
motion, canvas pause/fallback, and usable content without JavaScript. Set
UAI_SCREENSHOTS to a directory to save review images. Downloads are checked as
links; the test does not fetch release binaries.
For manual browser review, check narrow and wide layouts, 200% zoom, keyboard navigation and Escape, repeated copying and denied clipboard access, search with no results, clearing filters, and video playback. The catalog and navigation remain usable without JavaScript; only search and copy controls need it.
Project UAI Free is ready on a fresh install. Open Chat and send a message, or add your own connection in Providers: choose a provider in the gallery or enter a base URL, then enter its authentication details and fetch or type an exact model ID. Shared free capacity and speed can vary; existing provider choices stay intact.
The introduction opens once when the interface is visible and ready, waiting while a dialog, text field or request is active. Its six short pages cover Chat, the workspace, providers, permissions and optional community support, with direct links to Chat and Providers. Close it any time; Tutorial in the profile menu reopens it. A dismissed Add provider form keeps your draft for this client session; Start over clears it.
New configurations default to Allow everything: reads, writes and execution
run without approval unless a tool rule says otherwise. Existing saved access
choices and rules are preserved. Read only, Ask first and Auto remain available;
the chip under the composer names the active mode. Ask first shows a tool's
arguments before changes or execution, including the code for run_luau.
Nothing on disk until then, and only three things after: config.json,
sessions/<id>.json per conversation, and stats.json for the activity counters.
A conversation can be marked isolated from the composer, which keeps it out of the
first two entirely.
Cowork gives your Roblox conversation more room in the browser. Keep Roblox and its local bridge running on the same computer. The Windows desktop launcher is a single UAI-Bridge-3.6.5-win-x64.exe download. Open it directly, complete Node setup if needed using Get Node.js or an existing installation, click Start Cowork, and load the updated UAI script. It discovers the app on dedicated port 28791 and reconnects automatically after app restarts. The executable includes .NET, the bridge, browser assets and matching client, and prepares its embedded files automatically. Node.js is detected or installed separately; no ZIP extraction or adjacent supporting files are needed.
For manual terminal setup, Node.js 18+ is required; there are no production npm dependencies. An enabled manual bridge takes priority over desktop discovery.
-
In Roblox, open UAI → Cowork → Download bridge files. Open a terminal in your executor workspace (the folder containing
UAI) and run:node UAI/bridge/start.txt
From a Git checkout, run
node bridge/server.jsfrom the repository folder. -
Paste the terminal's Token into UAI → Cowork, match the printed Port, and turn Enabled on.
-
Open the terminal's browser link, choose a provider and model, and send a message. Keep both Roblox and the terminal open.
The installer saves .txt files so executors that block executable extensions
can download them. Node verifies the package and restores its real filenames.
No manual renaming is needed. A failed download preserves the previous launcher.
After restarting the bridge, use its new token and browser link.
Cowork selects Web runtime automatically on authenticated connection and restores Game runtime on disconnect. Bridged requests have no overall time limit or timeout setting; individual status polls remain short and recoverable. Stop cancels the original request without submitting it again.
Bridge releases download automatically from GitHub and apply when work is idle. The desktop app also downloads its own updates and applies them on restart. Both verify package hashes and keep a working fallback. Existing desktop 1.0 users need one manual download of the current executable to gain the updater.
The browser includes conversations, provider/model controls, code attachments, permissions, tool forms, subagents, chat loops, memory, logs, exports and settings. Drafts are saved per conversation using asynchronous browser storage. System, light, dark, and Match Roblox themes work on desktop and small screens.
PNG, JPEG and WebP pictures can be pasted, dropped, or attached for models that support image input. The bridge inserts the actual bytes into Chat Completions or Anthropic Messages image blocks, including when Game runtime is selected. Reload the updated client and restart the updated bridge for this capability. Image bytes remain in bridge memory and expire after 15 idle minutes or restart; reattach an expired image when needed. Text/code attachments remain readable files.
Roblox remains in control of tools, permissions, memory, accounting, and saved conversation history. It must stay connected to advance a turn. Refreshing the browser reconnects to the conversation; use Stop to cancel work. Delivery receipts and streamed replies reconcile without creating duplicate messages.
The bridge binds to loopback, requires a token, checks browser origins, and loads local UI assets without a CDN. Full configuration exports include API keys; ordinary browser state does not. See the bridge guide for troubleshooting, limits, architecture, and organized Node/Lua/browser tests.
Install UAI Relay 3.6.5 on Android 8 or later, start the relay, and load the current UAI script in your executor. With Relay mode set to Auto, the client discovers and pairs with the companion on the same phone. The relay holds the provider connection while the executor makes short result polls. Long requests can continue beyond five minutes with the default unlimited setting; provider limits and explicit deadlines still apply. Each active request must keep polling: two minutes without its owner's polls cancels abandoned work.
The companion checks official releases at startup and every 15 minutes while its process is alive. Compatible updates download automatically and pass package, version, size, hash and signing-certificate checks. Choose Install update when the relay is idle, then approve Android's installation prompt. Manual checks and downloads remain available if automatic downloads are disabled. Read companion updates for interrupted-install recovery and the one-time transition from an old debug-signed APK.
The relay does not provide a mobile executor. Its Android networking, background survival, installer and actual executor compatibility still require phone testing. OpenCode on mobile requires the UAI Relay app from Discord; install and start it, then connect it in UAI. Project UAI Free remains an alternative, or use another supported provider; executor probes can measure loopback and delayed-request behavior without provider credentials. See the companion guide for setup and diagnostics.
Use UI LIB by itself for a script-owned interface, or load the full client and connect your controls to its sessions and tools. Read the comprehensive embedding guide and runnable workbench example.
local uai = loadstring(game:HttpGet(
"https://raw.githubusercontent.com/Project-Ptolemy/ProjectUAI/main/dist/uai.lua"
))({
prompt = "This host provides workbench_status and workbench_configure for its local settings.",
})
assert(uai and uai.alive, "UAI did not start; inspect the console error")
uai.show("providers")Register the named host tools through uai.tools.register before asking the
agent to use them; prompt text alone does not install capabilities. The returned
handle exposes env, app, sessions, config, providers, tools, caps,
bridge, and log. Runtime methods use dots; UI LIB methods use colons.
The full client mounts its standard app unless context.ui = false. An explicit
show, toggle, or openSession can mount it later. UI LIB is independent and
mounts nothing until CreateWindow. A same-build client loader rerun toggles the
live instance unless context.reuse = true, and ignores new context; reuse a saved
handle or getgenv().UAI when
attaching another view. Changed builds replace the client only after active and
unsaved work is protected. Pin all bundle/module URLs to one reviewed commit SHA
for reproducible host releases.
No Roblox HTTP API can read a response body incrementally, so stream: true does
not deliver tokens as they arrive -- the whole SSE body lands at once and is
replayed through the parser. It is still requested, because the streamed shape is
where providers put reasoning text and per-request usage. net/ws.lua does real
token streaming for a gateway that implements UAI's {path, headers, body}
envelope, when the executor exposes WebSocket.connect, websocket.connect or
syn.websocket.connect. It opens one socket per completion. This is a custom
gateway contract, distinct from OpenAI Responses/Realtime and ordinary local
HTTP servers; replacing http:// with ws:// does not enable it. Leave the socket
URL empty for normal provider connections. The gateway receives the provider's
authentication headers. Connect/setup failures before sending can fall back to
HTTP; once sending begins, an uncertain outcome cannot trigger a second request.
See Provider compatibility and WebSockets for the protocol contract, local-server setup, provider matrix and offline coverage. Ollama, LM Studio, vLLM, llama.cpp and SGLang presets use their Chat Completions APIs. Authentication defaults to none for these local servers; select Bearer when server authentication is enabled. Tools require a capable model and server template/parser. Compatibility fixtures require no models, keys or paid inference.
Actual socket frames update the transcript immediately, with coalesced live previews and one final message. Buffered replies render in full when received; there is no simulated typing delay. The assistant is instructed to give brief progress messages between work steps. No prompt can expose tokens still buffered by the host or force a provider to speak while it is reasoning internally.
Some executors stop HTTP requests after roughly 30–60 seconds even when given a
longer timeout. The old 8,192-token executor ceiling has been removed, including
HTTP fallback. Requests use agent.maxTokens (128,000 by default), explicit
overrides, documented model limits and limits learned from provider refusals.
Old saved agent.executorReplyCeiling values are ignored.
Native request deadlines and long unanswered
transport failures are terminal: the client does not resend a smaller request or
fall back to another provider after an unknown outcome. A dispatched socket
failure cannot silently send a second HTTP request. Explicit token-limit refusals
can still teach a smaller ceiling. Large prompts can still spend the request
window uploading and prefilling; agent.contextTokens remains 1,000,000 by
default and can be lowered when short replies also time out.
Automatic compaction writes a full handover -- goal, current state, decisions,
exact paths and IDs, completed checks, open threads and what not to redo -- before
a request would cross the budget. The budget adapts to the model: when its context
window is known, compaction starts at Compact at (agent.contextFraction,
80% by default) of that window, with agent.contextTokens as the hard ceiling.
The folded messages are kept in a bounded conversation archive, and a short system
line names it; context_archive lists, searches and pages exact earlier wording
when the handover is not enough. Pressure includes estimated messages, the rolling
handover, and the system prompt/tool schemas from the prepared request. Provider
usage calibrates that overhead against the history actually sent; changing
providers, endpoints or models does not reuse another measurement. The context
breakdown shares this calculation, labels estimates, and shows partial totals
until the first request is prepared.
Compact now, in the composer's message-options menu, folds older turns on
demand; the Summarise old turns switch turns the paid summary off while the
conversation is still trimmed to fit. The composer shows a live context-window
counter beside the model -- the share of that budget the next request is expected
to spend -- so the pressure is visible before a compaction happens.
check_luau checks syntax without executing code. Both it and run_luau accept
either inline code or a saved file path. Use check_luau with
{"path":"scripts/build.lua"} to validate a script you edited, then run it by
the same path. Its source stays on the client instead of being generated and sent again. Saved-paste
references resolve the same way as file_read.
run_luau captures print/warn, tables, and multiple return values, and inserts
cooperative checkpoints in loop
bodies without changing strings or comments. Its timeout argument accepts 1–60
seconds and defaults to 10. It waits for functions started through its
task.spawn, task.defer, and task.delay wrappers; errors, Stop, deadlines, and
unload stop those managed tasks cooperatively. An endless spawned task therefore ends at the
deadline rather than continuing silently after the tool returns.
This is not a security sandbox. Dynamically compiled code, blocking engine calls,
and callbacks registered on engine signals can bypass these controls. Later work
in a persistent engine callback is outside a successfully completed call. UAI leaves
native coroutines alive so pending Roblox/executor callbacks cannot resume a thread
it closed. Managed waits and delayed callbacks check cancellation flags, including
after a successful parent returns; task.cancel accepts only this script's task
handles. Work suspended outside managed waits can still resume before its next
checkpoint, and the result reports that uncertainty. Changes already made are not
rolled back.
file_read and script_source return contiguous slices and a continuation
offset when more remains. Offsets count bytes and preserve UTF-8 boundaries.
After reading a workspace file, use file_edit with exact old_text and new_text
to change it. The default requires a unique match; replace_all=true replaces all
non-overlapping matches. Empty replacement text deletes the match. The tool refuses
a file that changed while the edit was being prepared.
Prefer file_edit or file_edit_many for targeted changes to large files.
file_write is for new files or replacing most of a file; it and file_append
reject content over 2 MiB per call before writing.
Build large new scripts in small sections, waiting for each write before the next
append or edit to that file. Each section must be a complete tool call. Interrupted
write arguments are rejected instead of repairing them into a partial edit. If a
provider reports that its tool batch hit the token limit, none of those calls run;
the model receives results asking it to retry with smaller, complete calls.
See CHANGELOG.md or App menu → What's new (also under About) for the latest release notes. The in-game notes are bundled with the client; reload the updated bundle to see them. Updated notes restore the unread marker even when the client version stays the same.
Main and subagents read skills relevant to the task or explicitly requested by the
user before applying them. Unchanged bodies still in context are reused; unrelated
skills need no reads. The enabled inventory is bounded to 3 KiB and discloses omitted
entries. Use skills_list for the full inventory and skills_read for bodies;
both paginate within the tool result budget. Restricted subagents retain read-only
skill access. Disabled skills are skipped; denied or unavailable reads are reported
once. Needed bodies are reread when changed or removed by compaction.
The interface can be removed and everything it started stopped:
getgenv().UAI.destroy()or from Settings, at the bottom: Unload UAI. Destroying the ScreenGui on its
own is not enough -- timers keep ticking, input handlers stay bound to
UserInputService, and a later config write would rebuild a window that is no
longer on screen -- so anything outliving the instance tree registers a cleanup in
runtime/dispose and the unload drains it. A turn in flight is aborted first and
settings are flushed before the tree goes.
MIT. Do whatever you like with it, including commercially and in closed source -- the only condition is that the copyright notice and permission notice travel with any substantial portion of it. See LICENSE.
Project UAI is MIT licensed. These pages mirror the guides in the repository; the repository copy is the source of truth.
Project UAI
Repository