Minimal Terminal Agent

A minimalist terminal agent.

A succinct core for agentic research.

$ git clone https://github.com/Eric2i/minimal-terminal-agent && cd minimal-terminal-agent && uv sync

Designed for research

MTA keeps the core minimal so that every behaviour is easy to trace, change and study. Planning, memory and context management are built on top, not baked in.

Inference Engine Agnostic

Works with any OpenAI-compatible chat-completions endpoint: vLLM, SGLang, Ollama or a hosted API. Point MTA_BASE_URL at it and go.

Native Sandbox Environment

Every episode runs in a fresh Docker container that is removed afterwards, so each run starts from the same image and leaves nothing behind.

Token-level Replay

Each episode is one serializable trajectory: SFT-ready messages, plus per-step slots for token IDs, logprobs and loss masks.

How it works

The model sends a tool call, the sandbox returns what happened, and the loop repeats until the model calls submit. Below is a replay of a real run, step by step; every step is recorded in the trajectory.

task Numerically solve the Lorenz system (sigma=10, rho=28, beta=8/3) from (1, 1, 1) over t in [0, 40] with scipy, and write the maximum value of z to /root/zmax.txt with 3 decimals.
tool call observation submit Model Sandbox bash in Docker done
step 0 · tool call

        

     

    Each box is a small Python interface: swap the model, the sandbox or the tools without touching the loop. Read the design →

    Quick start

    Set MTA_BASE_URL and MTA_MODEL in .env, then hand the agent a task in plain language.

    $ uv run mta "Numerically solve the Lorenz system (sigma=10, rho=28, beta=8/3) from (1, 1, 1) over t in [0, 40] with scipy, and write the maximum value of z to /root/zmax.txt with 3 decimals." --image python:3.12-slim

    Every token, under control.

    The trajectory is written after every step, so even a crashed or timed-out run keeps everything up to its last step.

    {
      "task": "Numerically solve the Lorenz system (sigma=10, rho=28, beta=8/3) …",
      "exit_status": "submitted",          // | step_limit | error | cancelled
      "messages": [ /* 18 OpenAI-format messages: system, user, assistant (+ reasoning, tool calls), tool */ ],
      "steps": [ …, {
        "index": 4, "finish_reason": "tool_calls", "latency_s": 2.39,
        "usage": { "prompt_tokens": 1380, "completion_tokens": 366,
                   "prompt_tokens_details": { "cached_tokens": 1152 },
                   "completion_tokens_details": { "reasoning_tokens": 102 }, … },
        "tool_calls": [{ "name": "bash",
          "arguments": "{\"command\": \"cd /root && cat > check.py <<'EOF' …\"}",
          "output": "exit code: 0\nDOP853  nfev=  63539 zmax=47.8408286238 at t=0.3814\n…",
          "metadata": { "returncode": 0, "timed_out": false }, "duration_s": 21.31 }],
        // filled when the server returns token-level data (e.g. vLLM); null for this hosted API
        "prompt_token_ids": null, "completion_token_ids": null, "logprobs": null, "loss_mask": null
      }, … ]
    }

    Debug

    Every command, its raw output, exit code and timing, step by step.

    Fine-tune

    {"messages", "tools"} is a standard chat-with-tools example, ready for SFT.

    Reinforce

    When the server returns token-level data, each step keeps token IDs, logprobs and a loss mask for RL (experimental).

    Benchmarks

    An optional integration runs MTA as a Harbor agent, so it can be evaluated on Terminal-Bench locally or in cloud sandboxes. The core never imports Harbor.

    Run Terminal-Bench →