Skip to content

On-demand workspace

Bonus assignment: attach the workspace on demand

Section titled “Bonus assignment: attach the workspace on demand”

Time: 5–8 minutes · Start after the finish-line verification

Checkpoint 5 attaches Sandbox tools on every agent render. The main workshop keeps that behavior through checkpoint 6. This optional assignment adds a new capability boundary: the model must explicitly request a workspace before it can use file and shell tools.

In src/agents/field-trip.ts:

  1. Add a persistent workspace flag, initially false for each conversation.
  2. Register an open_workspace tool that sets the flag to true.
  3. Call useSandbox(...) only when the flag is true.
  4. Update the itinerary instructions to call open_workspace before writing when the file tools are not yet available.

Flue re-renders the agent before the next model call. After the tool updates state, that next render exposes the Sandbox tools. The decision stays scoped to the conversation, independently of the trip brief.

If your agent already has open_workspace, verify the existing implementation below rather than registering a duplicate tool.

The diff starts from the guide’s completed checkpoint 6, whose agent is still the checkpoint-5 version. Only the workspace gate and itinerary rule change; configuration, migrations, weather tools, and subagent behavior stay in place.

src/agents/field-trip.ts+12−3

Changes from Checkpoint 6 (guide) → On-demand workspace bonus

src/agents/field-trip.ts
===================================================================
--- a/src/agents/field-trip.ts Checkpoint 6 (guide)
+++ b/src/agents/field-trip.ts On-demand workspace bonus
@@ -59,8 +59,17 @@
// fresh context with its own tools, and only its final answer comes back.
useSubagent(venueScout);
- // A Linux container per conversation (adds read/write/edit/bash/grep/glob tools).
- useSandbox(cloudflareSandbox(getSandbox(env.Sandbox, id)));
+ // Attach file and shell tools only after this conversation requests a workspace.
+ const [workspace, setWorkspace] = usePersistentState('workspace', false);
+ useTool({
+ name: 'open_workspace',
+ description: 'Attach the file workspace (read/write/bash tools).',
+ async run() {
+ setWorkspace(true);
+ return 'Workspace attached.';
+ },
+ });
+ if (workspace) useSandbox(cloudflareSandbox(getSandbox(env.Sandbox, id)));
// The agent re-renders before every model call, so these instructions
// always reflect the latest saved brief.
@@ -77,7 +86,7 @@
b. Pick the 3 places that best fit the brief (skip stations, offices, hospitals, embassies, companies, events).
c. Call \`task\` ONCE with agent \`venue-scout\` for all 3 places. The scout cannot see this conversation, so the prompt must be a complete briefing: the exact place titles, the city, the headcount, and the interests.
d. Combine the results into a short plan, keeping the links. If you know the forecast, suggest outdoor places for dry days and indoor ones for rainy days.
-5. For an itinerary: \`write\` it to itinerary.md (one section per day: places, timing, weather), then \`read\` it to check. Do not repeat the file in your reply (the user sees the read result); reply in one sentence.
+5. For an itinerary: if you have no \`write\` tool yet, call \`open_workspace\` first. Then \`write\` it to itinerary.md (one section per day: places, timing, weather), then \`read\` it to check. Do not repeat the file in your reply (the user sees the read result); reply in one sentence.
6. Keep replies short: at most 120 words unless the user asks for more detail.
Today is ${today}.

Use fresh conversation IDs so a previously saved workspace: true flag cannot hide the initial gate. Trip dates are filled automatically.

Terminal window
npm run typecheck
npm run deploy
sleep 5
npm run smoke -- https://field-trip-agent.<subdomain>.workers.dev workspace-live-greeting "Hi, who are you? One sentence."
TIMEOUT_S=200 npm run smoke -- https://field-trip-agent.<subdomain>.workers.dev workspace-live "Plan a simple Lisbon itinerary from <START> to <END> for 14 people. Write it to itinerary.md and read it back."
npm run smoke -- https://field-trip-agent.<subdomain>.workers.dev workspace-live "Read itinerary.md again."

Save your workers.dev subdomain to open this test ↗https://field-trip-agent.<subdomain>.workers.dev/?id=workspace-live

This opens the same conversation as the smoke test. If you repeat this checkpoint, before copying its commands.

Verification gate

Prove it works

  • The fresh greeting completes without calling open_workspace.
  • The first itinerary request calls open_workspace before write and read.
  • A later message in that conversation can read the same file without opening the workspace again.
  • The trip brief, weather tools, and venue-scout delegation still work.

Replace the workspace state, open_workspace tool, and conditional attachment with the original checkpoint-5 block:

// A Linux container per conversation (adds read/write/edit/bash/grep/glob tools).
useSandbox(cloudflareSandbox(getSandbox(env.Sandbox, id)));

Restore the itinerary rule to start directly with write, removing the open_workspace instruction. Run npm run typecheck, then redeploy from the Live deploy tab (or restart Vite for Local dev). Use a fresh conversation for the next experiment.