Use githosted in an agent
The pattern that makes githosted earn its keep inside an agent loop: one repo per session. Every run of the agent gets its own repo. Every file the agent writes is a real Git commit. When the agent fails — and it will — you don’t lose state. You diff, roll back, or branch a new attempt off the last good one.
Auth: one workspace token
Section titled “Auth: one workspace token”Mint a workspace + write token from the dashboard. The agent
container reads it from GITHOSTED_TOKEN:
GITHOSTED_TOKEN=gw_…from githosted import Clientclient = Client() # reads GITHOSTED_TOKENWorkspace scope is right because the agent will create new repos on every run. A repo-scoped token can’t create new repos. See Authenticate for the wider picture and Sandbox auth for the “how do I get the token into a Modal / Daytona / E2B sandbox” question.
Pattern 1: one repo per session
Section titled “Pattern 1: one repo per session”The headline. The agent gets a run_id from somewhere (a UUID, a
job ID, a timestamp), creates a repo named after it, and writes
its work into that repo.
import uuidfrom githosted import Client
client = Client()run_id = uuid.uuid4().hex[:8]
repo = client.create_repo(f"agent-run-{run_id}")The repo’s slug becomes part of a stable URL the rest of your system can reference:
app.githosted.dev/<workspace>/agent-run-<run_id>From here, every file the agent produces is a commit:
repo.write( "plan.md", plan_text, message="Initial plan",)
repo.write( "src/handler.py", generated_code, message="Generated handler",)A successful run produces a repo full of artifacts plus a clean commit history. A failed run produces the same — except you can see exactly where it went wrong.
When to delete the repo
Section titled “When to delete the repo”Don’t, by default. Keeping the run repos around is the point. A
weekly cron that prunes repos older than N days is a fine
cleanup story when storage matters; in early development just
leave them.
Pattern 2: shared repo, branch per session
Section titled “Pattern 2: shared repo, branch per session”When runs are tightly related (e.g. iterating on the same project, each run is a step) it’s often more useful to share one repo across many runs and branch per session:
repo = client.repo("project-x") # existing reporepo.create_branch(f"run/{run_id}", from_ref="main")
repo.write( "src/handler.py", generated_code, ref=f"run/{run_id}", message="Tweak handler logic",)The branch name preserves the per-run isolation. git log run/<run_id> shows what that run changed. If the run is good,
merge into main. If not, delete the branch.
# good runrepo.merge(source=f"run/{run_id}", into="main")
# bad runrepo.delete_branch(f"run/{run_id}")Pattern 3: read prior runs to inform the next one
Section titled “Pattern 3: read prior runs to inform the next one”The agent’s prior commits are first-class context for the next prompt. Read them back exactly the way an editor would:
last_attempt = repo.read("src/handler.py", ref="run/abc123").contentlast_diff = repo.diff("run/abc123", "run/def456")Feed these into the next LLM call so the agent knows what’s already been tried, what changed between attempts, and what worked. This is the move that makes “agentic coding” work without turning into a goldfish that forgets every five minutes.
Diff what changed between attempts
Section titled “Diff what changed between attempts”The killer move. When you have two runs of the same task, compare them:
delta = repo.diff( base_ref="run/attempt-1", head_ref="run/attempt-2",)
for f in delta.files: print(f"{f.status} {f.path} (+{f.additions}/-{f.deletions})")Surface the diff to the agent itself for self-critique, surface it to a human reviewer for spot-checks, or feed it into a tests-pass / tests-fail comparison.
Multi-file commits with transactions
Section titled “Multi-file commits with transactions”Every repo.write is a commit by default. When a single agent
step produces several files that belong together — src/foo.py,
its tests, and an updated README.md — wrap them in a transaction
so they land as one commit:
with repo.transaction("Add new handler with tests") as tx: tx.write("src/handler.py", code) tx.write("tests/test_handler.py", tests) tx.write("README.md", readme)One commit, one rollback unit, one diff target. See Transactions.
Roll back when a run goes off the rails
Section titled “Roll back when a run goes off the rails”The simplest rollback is “don’t merge that branch.” More direct, when the run wrote to a long-lived branch and you want to undo:
# Find the SHA you want to roll back tolog = repo.log(limit=20)good = next(c for c in log if "Generated handler" in c.subject)
# Reset by force-creating a branch at the good SHA, and# re-pointing main to it via merge.repo.create_branch("rollback", from_ref=good.sha)repo.merge(source="rollback", into="main")Real-world: cheap, scriptable, requires no human approval flow.
Errors and retries
Section titled “Errors and retries”The two errors you’ll actually hit:
ConflictError(HTTP 409) — another writer holds the branch lease. Wait briefly and retry. Different branches never collide, so per-session branches sidestep this entirely.StaleHeadError(HTTP 412) — you passedexpected_headtorepo.writeand the branch moved underneath you. Re-read the current head, recompute, retry. The transaction API uses this pattern automatically insidewith repo.transaction(...).
For raw repo.write calls in a tight retry loop:
import time
for attempt in range(3): try: repo.write("src/main.py", code, message="Update") break except ConflictError: time.sleep(2 ** attempt)See Errors for the full list.
Worked example
Section titled “Worked example”The agent app builder example — a CLI that takes a prompt, calls Claude, and writes the generated app into a fresh repo seeded from a public Vite + React template — uses every pattern on this page in ~200 lines. Bring your own LLM key and your own githosted workspace token; the README walks through cloning, configuring, and running it locally.
What this gives you that bare LLM loops don’t
Section titled “What this gives you that bare LLM loops don’t”- A persistent record of what the agent did, in the standard format (Git) every other tool already speaks.
- A diff between any two runs in two lines. No “remember to serialize state” boilerplate.
- A rollback that’s a
mergeaway. - A shareable URL per run when the repo is public.
- Quickstart — sign in, mint a token, write your first file
- Authenticate — picking the right token for the agent
- Transactions — atomic multi-file commits
- Errors — what to retry, what to bubble up