Skip to content

Autonomous Runs ​

Two commands run UGENT without you at the keyboard. /loop repeats a prompt. /goal pursues an objective until it is done. Both run from the TUI and the REPL.

Both are bounded: by iterations, by wall-clock time, by tokens, and — for /goal — optionally by your project's own checks.

/loop: repeat a prompt ​

/loop 5m check the build status

Fixed interval. The prompt runs every five minutes until you stop it. The interval can also trail the prompt:

/loop check the build status every 10 minutes
/loop watch for new issues and triage them

Self-paced. UGENT decides when to run next based on how the last iteration went, rather than on a clock, and stops when it schedules no further run.

CommandDoes
/loop status or /loop listShow this session's loop and goal runs
/loop stop <id> or /loop stop allStop one run or all of them. Id prefixes are accepted

/goal: pursue an objective ​

/goal migrate the auth module off the deprecated client

UGENT works toward it across turns, reporting progress as [GOAL] markers, and stops when it reports the objective met — or, with a verification gate, when your checks agree. One goal at a time: while a goal is active, paused or blocked, a new /goal is refused until you resume or clear it.

CommandDoes
/goal statusShow the goal, its progress, and its latest run, including what stopped it
/goal pausePause after the current iteration
/goal resumeResume a paused or blocked goal
/goal clearStop and delete the current goal

Verification gates ​

A goal ends when the agent reports it done. That is a self-assessment, and a confident agent can be wrong.

Configure goal_gate and your project's own checks must agree before the verdict is accepted:

toml
[autorun]
goal_gate = [
  "cargo fmt --all -- --check",
  "cargo clippy --all-targets -- -D warnings",
  "cargo test",
]
goal_gate_max_retries = 3      # default
goal_gate_timeout_secs = 600   # per command; default

Empty, the default, accepts the agent's own verdict.

Each time the agent reports the goal completed, the workspace as it stands is a new candidate. Every gate command runs against it, in order, stopping at the first failure, and the goal completes only when the decision recorded from those results is accepted. A pass for one claim never counts for the next, so whatever changed after a pass is checked again.

Every command must exit zero. If one fails, the completion verdict is downgraded to in-progress and the failing output — its last 6,000 characters, so you get the errors rather than the build banner — is fed back into the next iteration. The agent gets the real error, not a bare rejection.

When one command has failed more than goal_gate_max_retries times in a run, the goal stops as blocked rather than grinding against a check it cannot satisfy. /goal resume starts a new run with fresh attempts.

If the results cannot be recorded — the workspace has no exact digest (see below), or the database refuses the write — the gate fails rather than letting the goal complete unverified, and the agent is told the problem lies in the environment, not the code.

Use check-only commands

A command that changes the files it checks proves nothing about either version, so its result is not accepted, whatever it exited with. Use cargo fmt --all -- --check, not cargo fmt --all. The same goes for a file a check writes that git does not ignore, such as a coverage report at the repository root: make sure everything your checks write is ignored.

Gate commands are trusted ​

They come from your ugent.toml, not from the model, which cannot set, append to, or influence them. That is why they run without the confirmation prompt the bash tool applies to model-authored commands.

Each runs as a login shell in the workspace root, under the OS sandbox's tooling profile when the sandbox is in force. When a command exits or times out, its whole process group is stopped, so a server a test started does not outlive the check.

Put nothing there you would not run yourself unattended.

What counts as the workspace ​

A candidate is identified by a digest of the workspace's inputs:

  • In a git repository: the files git can see — tracked, and untracked but not ignored — plus ignored .env files, which tests often read. Everything else git ignores is output, so a check that writes to target/ or a coverage directory changes nothing.
  • Outside a repository: the whole tree, minus version-control metadata and output directories, recognised by name (target, node_modules, .venv, __pycache__ and the like) or by a CACHEDIR.TAG or pyvenv.cfg file inside them.

A symbolic link counts by the path it holds and is never followed. A workspace with more than 200,000 entries or 2 GiB of inputs, or with a file UGENT cannot read, has no exact digest, and the gate fails.

Reruns are skipped when nothing changed ​

If a gate fails and the agent then claims completion again without changing anything the digest covers, rerunning costs a full build to reach the identical failure. UGENT skips that, and tells the agent plainly that nothing changed.

The digest cannot see everything: installed dependencies, running services, the network, and ignored files other than .env. A fix to one of those — installing a missing tool, or regenerating an ignored config — can look like no change at all.

Counting ignored files would not fix this; it would break the feature. Build and test commands write ignored files on every run, so the digest would differ each time and the skip would never fire.

The harm is bounded instead: the attempt that would end the run always runs the command for real. A wrong skip can waste an iteration, but it can never be the reason a goal is declared blocked.

What is kept ​

Each claim's checks and the decision taken from them are kept with the goal: which command ran, how it ended, and the tail of its output with credentials removed, or withheld when they cannot be removed. Commands are named without storing their text, which can hold a credential. Clearing a goal, or starting a new one after it finished, deletes it and its record.

A check that was running when UGENT stopped is closed as interrupted after the restart. It is never rerun on its own.

Bounding the run ​

The defaults:

toml
[autorun]
# /loop
loop_max_iterations = 1000
loop_max_duration_hours = 168   # 7 days; also the wall-clock cap of a /goal run
loop_min_interval_secs = 30     # shortest fixed interval

# /goal
goal_max_iterations = 25
# goal_token_budget = 2000000   # unset means unlimited
goal_compress_every = 5         # compress the session every N iterations; 0 disables

# both
max_concurrent_runs = 3
max_consecutive_failures = 3

goal_max_iterations and goal_token_budget cover the goal's whole life: a resumed goal keeps the iterations and tokens it has already used. Once it has used every iteration, raise the cap or clear the goal.

goal_token_budget counts input, output, and cache-write tokens across each whole turn, and excludes cache reads — you are billed for what was written, not for what a cache hit saved you.

goal_compress_every matters on long runs: without periodic compression a goal that runs for dozens of iterations will spend its later turns mostly re-reading its own history.

Restarts ​

A loop ends with the process that runs it. For a schedule that survives a restart, use cron jobs.

A goal is kept. One that was running when UGENT stopped comes back paused, so nothing resumes on its own; /goal resume carries on from where it was.

Watching a run ​

/goal status and /loop status report from the TUI or the REPL. The Sub-Agents panel (Ctrl+F, or /subagents) shows anything the run delegated, and /btw gives a read-only summary that never enters the agent's own context. To have delegated coding work checked by your tests before it counts as done, see Tasks and Checks.

To stop a run that is going wrong, /goal pause or /loop stop — both take effect at the next iteration boundary rather than killing work mid-tool-call.

Released under the Private Beta License.