The ReAct Loop

CUST/OS uses a ReAct (Reason + Act) loop to handle operator messages. The model can request a tool, read its result, and decide what to do next. The loop ends when the model returns text without another tool call, or when a runtime limit stops it.

What is ReAct

ReAct is a published reasoning pattern (Yao et al., 2022) that interleaves an LLM's reasoning with real-world tool use. Instead of producing a single response, the model:

  1. Reasons about the task and decides what action to take.
  2. Acts by calling a tool.
  3. Observes the result.
  4. Repeats until the task is complete.

For a request such as "find the nearest hostile and place a track marker on it", the agent first needs current map positions. It can query those positions, use the result to choose a marker, and request the next action within the same conversation turn.

How CUST/OS uses it

For each operator message, the agent runs this cycle:

1. Skill selection

Before the first reasoning step, CUST/OS selects the 1-3 most relevant skills for the operator's message using keyword and semantic matching. Only those skills and their tools are included in the prompt. This keeps the context focused and efficient, even when dozens of skills are installed.

2. Context assembly

The system prompt, selected skill descriptions, conversation history, persistent memory, and the operator's message are assembled into a single context payload for the LLM.

3. Reasoning (streaming)

The context is sent to the LLM. With a streaming provider, the response appears in chat as tokens arrive.

4. Tool detection

As the response streams in, tool calls are detected. A tool call is the model's way of saying "I need to do something" -- query the map, place a marker, measure a distance, send an alert.

5. Security evaluation

Before any tool runs, it passes through the security pipeline:

  • Configurable rules (hooks) can allow, deny, or escalate specific tools.
  • Tools tagged as high-impact require explicit operator approval before execution.
  • The operator sees the tool name, arguments, and impact level, and can approve, deny, or modify the arguments.

6. Tool execution

Approved tools are executed in the sandboxed script runtime. The result -- success or failure, with data -- is appended to the conversation.

7. Loop or finish

The agent now sees the tool results and decides what to do next:

  • More tool calls needed -- the cycle repeats from step 3.
  • Task complete -- the agent responds with text only (no tool calls), and the loop ends.

The configuration sets the maximum number of loop iterations.

Streaming

Chat shows the model's streamed response and tool activity. The operator can read the output while it arrives and stop or correct the task. This display shows the response and requested actions; it is not a complete view of the model's internal reasoning.

Operator control

During a turn, the operator can:

  • Stop -- halt the agent mid-loop. The agent returns whatever partial work it has completed.
  • Steer -- inject a correction ("scratch that, do X instead") without restarting the conversation. The correction is woven into the context before the next reasoning step.
  • Approve / Deny / Modify -- review tools that require approval under the configured rules and impact levels.

Graceful degradation

Two safeguards prevent the loop from failing silently:

  • Loop detection -- if the agent calls the same tool with the same arguments repeatedly without making progress, the loop is terminated early.
  • Last-iteration failure -- if the agent is on its final allowed iteration and every tool in the previous turn failed, the runtime asks the model to explain the failed attempts.

When not to use the ReAct loop

Use Lua directly when the sequence of steps is already known:

  • Deterministic playbooks — if the steps are known in advance, write them directly as Lua. A skill script or automation can chain tools.call("step_one", ...) → tools.call("step_two", ...) without an LLM in the loop.
  • High-frequency monitors — a 15-second perimeter sweep shouldn't run a reasoning loop on every tick. Put it in an automation with a deterministic run(ctx) body.
  • Batch operations — same reasoning: compose the steps in Lua, call delegate only when you need the LLM to make a judgement call.

Use the ReAct loop when the model needs to interpret a result before choosing the next action. Use Lua to encode a fixed sequence and avoid repeated model calls.

See also