Skip to content

feat(examples): give alchemy-cloudflare sessions a workspace, a bash tool and a Linux container - #50

Merged
K-Mistele merged 7 commits into
mainfrom
implement-chat-session-with-durable-objects-and-alchemy-tyntlz
Oct 1, 2026
Merged

K-Mistele merged 7 commits into
mainfrom
implement-chat-session-with-durable-objects-and-alchemy-tyntlz

Conversation

@K-Mistele

Copy link
Copy Markdown
Contributor

Follow-up to #48. Sessions in examples/alchemy-cloudflare get a workspace and a way to run commands in it.

What changes

  • Workspace: a session clones the repos from its first message into a Computer, a Durable Object from @cloudflare/computer. The file tools and disk skills work on it.
  • Bash tool: commands run in the in-Worker shell (just-bash) by default.
  • Linux container: backend: "container" runs a command in a Debian container attached to the Computer, as root in a bash login shell, with git, node 22, bun, python3, ripgrep, fd and tmux. It runs on standard-4 (4 vCPU, 12 GiB). The session starts it in the background, and deleting the workspace stops it.
  • Idle expiry: idle sessions expire.
  • fold-agent: narrow entry points for the file tools and disk skills.
  • fold-core: tool handlers keep the session's platform services.

The @cloudflare/computer fork

The container runs computerd from K-Mistele/computer, built from a pinned commit in src/computer/container/Dockerfile. The Worker uses @cloudflare/computer from a release of the same fork, through the root catalog. The fork:

On Cloudflare (standard-4), bun install in fold takes 147s and bun run test takes 2m01s in the container; before the fork's fixes, tests timed out at 10 minutes.

Testing

  • bun run typecheck, bun run lint, bun run format:check and bun run test pass.
  • Deployed and tested in live sessions: install, tests, gitignored files staying in the container, and edits and deletes made in the container reaching the file tools.

🤖 Generated with Claude Code

K-Mistele and others added 7 commits September 30, 2026 13:07
A fold chat host on Cloudflare deployed with Alchemy v2. Each session is a
Durable Object named by its SessionId, with the fold event log stored in the
object's SQLite. An alarm heartbeat keeps the object alive while a turn runs,
and a turn cut off by a restart is nudged to continue (at most 3 times).
HttpRouter routes reach the objects through a ChatSessions service. The model
is OpenAI gpt-5.6-terra on medium reasoning, with the API key bound from the
deploy environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0ef41-7f9a-7c78-83e6-b468fe8c440e
Effect AI runs a tool handler with the services in scope where the turn
runs merged over the ones its toolkit was built with, so the turn's win.
A host whose request handling carries its own FileSystem shadowed the one
the session started with: on Cloudflare, Alchemy provides Node's services
to every Durable Object call, so the file tools read the Worker's empty
temporary disk instead of the session's workspace.

Each tool handler now merges the session's platform services last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9
`@humanlayer/fold-agent/tools/files` exports read, write, edit and
apply_patch (with a `fileTools()` bundle that `codingTools` now uses), and
`@humanlayer/fold-agent/skills` exports the disk skill loader. Hosts such
as Cloudflare Workers can use them without pulling in bash, the provider
packages, or config loading.

The read tool's TextDecoder now passes `ignoreBOM: false`, its default,
because Cloudflare's type definitions require the field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9
…d repos

Each session gets a @cloudflare/computer workspace: a filesystem with git
in a Computer Durable Object, hosted by a separate plain Worker and named
by the session's id. The first message's repos ([{ url, name?, ref? }])
clone into /workspace/<name> before the session starts; a failed clone
is a 422 and starts nothing.

The agent gets fold-agent's file tools and skill loader over the
workspace, through an Effect FileSystem backed by the Computer's file
methods. Skills load from each repo's .claude/skills and .agents/skills,
and the system prompt lists the repos.

The log route moves to GET /sessions/:id/log. Clones and file calls log
through Effect's logger, visible with `bun alchemy tail`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9
… sessions

bash: the Computer's workspace gets @cloudflare/computer's worker shell,
just-bash in a Worker started through a Worker Loader binding, which
reads and writes the same filesystem as the file tools. The agent's bash
tool keeps fold-agent's parameters and output handling: stdout then
stderr, trimmed to the last 2000 lines or 50KB, failing with the output
on a non-zero exit or timeout. It covers the common text commands and
git; it cannot install, build, or run tests.

Idle expiry: a session with no messages for 14 days deletes its
workspace, then its own log, and restarts empty, so its id starts a new
session. The deadline shares the object's alarm with the keepalive
heartbeat. The workspace also sets its own backup alarm a day later, so
it is deleted even when its session never deletes it. A session that
wakes past its deadline skips resuming and waits for the alarm, so it
cannot reach into the workspace mid-delete or bring it back empty.

Also:
- A resumed session counts only folders in /workspace as repos.
- ChatSessions passes through failures other than RepoCloneError instead
  of replacing them with a SchemaError.
- Shell commands log `workspace.exec`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9
…iner

The bash tool gains a backend: the in-Worker shell, as before, or a
Debian container attached to the Computer, where commands run as root
in a bash login shell with git, node, bun, python3, ripgrep, fd and
tmux. The session starts the container in the background when it
starts, and deleting the workspace stops it.

The container runs computerd from a fork of cloudflare/computer
(K-Mistele/computer), built from a pinned commit, and the Worker uses
@cloudflare/computer from a release of the same fork. The fork keeps
gitignored files in the container instead of sending them to the
Computer, so installs and builds don't fill its storage, and fixes
the slow path lookup, disk syncs and uncached "not found" answers
that made installs and test runs slow on the mount.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9
…n-with-durable-objects-and-alchemy-tyntlz

HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a0f3fc-13b0-758f-a343-fae04e83a7a9

# Conflicts:
#	bun.lock
#	examples/alchemy-cloudflare/README.md
#	examples/alchemy-cloudflare/alchemy.run.ts
#	examples/alchemy-cloudflare/package.json
#	examples/alchemy-cloudflare/src/Api.ts
#	examples/alchemy-cloudflare/src/ChatSession.ts
#	examples/alchemy-cloudflare/src/ChatSessions.ts
#	examples/alchemy-cloudflare/src/Keepalive.ts
#	examples/alchemy-cloudflare/test/Api.test.ts
#	package.json
@K-Mistele
K-Mistele merged commit f66bceb into main Oct 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant