@@ -16,7 +16,7 @@ with a link to the PDF.
1616POST /prompt ──► RecipeAgent
1717 │ fetch_url https://openstove.org/... (host)
1818 │ write /workspace/card.md (host)
19- │ bash pandoc card.md -o card.pdf (container)
19+ │ exec pandoc card.md -o card.pdf (container)
2020 ▼
2121 R2 ──► signed link, good for a day
2222```
@@ -26,7 +26,7 @@ of the workspace. The `write` tool runs on the host, in the durable
2626object, and writes through the ` Workspace ` into durable object storage.
2727The container sees the same file on its FUSE mount at ` /workspace ` , so
2828` pandoc ` reads it as an ordinary file. The PDF ` pandoc ` writes syncs
29- back the other way when the ` bash ` call finishes, so the finished file
29+ back the other way when the ` exec ` call finishes, so the finished file
3030can be published straight from the workspace.
3131
3232The finished code is [ one file] ( src/index.ts ) . The rest of this page
@@ -86,11 +86,12 @@ wrangler r2 bucket create recipe-cards
8686npm install @cloudflare/computer @cloudflare/think agents ai zod
8787```
8888
89- - ` @cloudflare/computer ` is the filesystem, the container backend, and
90- the assets client that publishes to R2.
91- - ` @cloudflare/think ` provides the agent loop and its file, shell, and
92- fetch tools. ` agents ` provides ` getAgentByName ` for reaching an
93- instance by name; ` ai ` and ` zod ` satisfy Think's peer dependencies.
89+ - ` @cloudflare/computer ` provides the filesystem, container backend,
90+ AI SDK file and execution tools, and the assets client that publishes
91+ to R2.
92+ - ` @cloudflare/think ` provides the agent loop and fetch tools. ` agents `
93+ provides ` getAgentByName ` for reaching an instance by name; ` ai ` and
94+ ` zod ` satisfy Think's peer dependencies.
9495
9596## 3. Create the Dockerfile
9697
@@ -132,9 +133,9 @@ works in both places.
132133## 4. Give Think a Computer workspace
133134
134135The durable object owns a ` CloudflareContainerBackend ` , which is the
135- container the workspace is mounted in. The ` Workspace ` instance enables
136- Computer's Think-compatible methods so Think's built-in tools use the
137- same filesystem as the container .
136+ container the workspace is mounted in. Think receives the Computer
137+ ` Workspace ` ; agent tools use its ` workspace.fs ` and ` workspace.runtime `
138+ facades directly .
138139
139140``` ts
140141import {
@@ -144,23 +145,22 @@ import {
144145import { Think } from " @cloudflare/think" ;
145146import {
146147 type DurableObjectStorageLike ,
147- type ThinkWorkspaceCompatibility ,
148148 Workspace ,
149149} from " @cloudflare/computer" ;
150150
151151class RecipeBase extends Think <Env > {}
152152
153153export class RecipeAgent extends withWorkspaceContainer (RecipeBase ) {
154154 readonly #backend = new CloudflareContainerBackend ({
155+ id: " container" ,
155156 container : () => this ,
156157 workspace: { binding: " RecipeAgent" , id: this .ctx .id .toString () },
157158 });
158159
159160 override workspace = new Workspace ({
160161 storage: this .ctx .storage as unknown as DurableObjectStorageLike ,
161162 backends: [this .#backend ],
162- useThink: true ,
163- }) as Workspace & ThinkWorkspaceCompatibility ;
163+ });
164164
165165 override async fetch(request : Request ): Promise <Response > {
166166 return new URL (request .url ).pathname === " /ws"
@@ -178,13 +178,17 @@ hand `/ws` to the backend before the base class sees it.
178178
179179## 5. Hook the workspace up to the agent
180180
181- Think already has file and shell tools. The Computer workspace makes
182- those tools use the same filesystem as the container:
183- ` write ` calls Computer's host-side filesystem, while ` bash ` calls the
184- container shell. Think's fetch tool gets an allowlist of one host, so
185- the agent can read openstove.org and nothing else.
181+ Disable Think's legacy shell tool and return Computer's complete tool
182+ set from ` getTools() ` . ` write ` calls the host-side filesystem, while
183+ ` exec ` runs against the container backend. Think's fetch tool gets an
184+ allowlist of one host, so the agent can read openstove.org and nothing
185+ else.
186186
187187``` ts
188+ import { createAITools } from " @cloudflare/computer/tools" ;
189+ import type { ToolSet } from " ai" ;
190+
191+ override workspaceBash = false ;
188192override maxSteps = 10 ;
189193override fetchTools = {
190194 allowlist: [" https://openstove.org/**" ],
@@ -196,6 +200,20 @@ override getModel() {
196200 return " @cf/zai-org/glm-5.2" ;
197201}
198202
203+ override getTools (): ToolSet {
204+ return createAITools ({
205+ workspace: this .workspace ,
206+ shell: {
207+ defaultBackend: " container" ,
208+ backends: {
209+ container: {
210+ description: " Cloudflare Container with full Linux userland, including pandoc and typst." ,
211+ },
212+ },
213+ },
214+ });
215+ }
216+
199217override getSystemPrompt () {
200218 return [
201219 " You turn a cooking request into a one-page PDF recipe card." ,
@@ -209,13 +227,13 @@ override getSystemPrompt() {
209227 " and numbered Method steps. End with the source page URL spelled out," ,
210228 " not a markdown link: the card gets printed, and a link prints as its" ,
211229 " text alone." ,
212- " 3. Convert it with `bash `: `pandoc /workspace/card.md -o /workspace/card.pdf --pdf-engine=typst`." ,
230+ " 3. Convert it with `exec `: `pandoc /workspace/card.md -o /workspace/card.pdf --pdf-engine=typst`." ,
213231 " 4. Reply with one sentence naming the recipe you picked." ,
214232 ].join (" \n " );
215233}
216234```
217235
218- Nothing copies files between ` write ` and ` bash ` : the write goes into
236+ Nothing copies files between ` write ` and ` exec ` : the write goes into
219237durable object storage and the container reads it back out of the mount,
220238and the PDF ` pandoc ` leaves behind travels the same road in reverse.
221239
@@ -351,7 +369,7 @@ curl -X POST http://localhost:8787/prompt \
351369```
352370
353371The first request may be slow: the container has to boot before the
354- first ` bash ` runs. The link points at R2 rather than at the worker, so
372+ first ` exec ` runs. The link points at R2 rather than at the worker, so
355373it works the same whether the worker runs locally or deployed.
356374
357375` wrangler deploy ` works against any account with Workers AI and
0 commit comments