SIRT Triage Agent Workshop
~15 min

Dynamic Workers -- execute agent-generated code

Let the Response Agent generate a custom Worker at runtime (e.g. an IOC scoring helper) and execute it via Dynamic Workers, showing the generated code, execution time, and cost estimate.

Steps0 / 4
~15 min
  1. Navigate to the lesson-14-dynamic-workers directory

    Run `cd ..\lesson-14-dynamic-workers` from your current lesson directory.

  2. Replace REPLACE_WITH_YOUR_DATABASE_ID in wrangler.jsonc

    Open `wrangler.jsonc` and replace `REPLACE_WITH_YOUR_DATABASE_ID` with your D1 database_id from lesson 06.

  3. Redeploy the app

    Run `npm run deploy`.

  4. Verify agent-generated code execution

    Investigate any incident. After the triage card and action plan appear, you should see the Generated Worker panel with source code, execution result, and cost estimate.

OpenCode shortcut

Paste this prompt into the OpenCode TUI to let your AI agent walk you through this lesson:

 

Step 0: Navigate to the lesson directory

Open your terminal and navigate to the lesson-14-dynamic-workers directory:

cd ..\lesson-14-dynamic-workers

This directory’s wrangler.jsonc already sets STATE to state-7-dynamic-workers, which turns on the Generated Worker panel in the analyst console. The panel is a faithful simulation in this workshop build, so you can complete the lesson on any account without Worker Loader access.

Why agent-generated code matters

Up to now, the Response Agent produces structured action plans — but those actions are data. They describe what to do; they don’t do anything. What if the agent could write and execute bespoke logic tailored to the incident at hand?

Think about what an analyst does manually: they write a quick script to score a list of IOCs against a threat feed, check whether a set of IPs falls within a suspicious CIDR range, or validate that a blocklist entry matches the right format before pushing it. These are small, disposable utilities — different for every incident, rarely worth packaging into a permanent tool.

Dynamic Workers let the Response Agent do exactly this. Instead of a fixed library of tools, the agent generates a custom Worker — a few dozen lines of JavaScript — deploys it ephemerally, invokes it, and returns the result. The action space isn’t limited to what you anticipated at build time. It’s whatever the model can write.

What Dynamic Workers are

Dynamic Workers are the lowest-level primitive for spinning up a Worker at runtime. Your host Worker (the SIRT app) holds a Worker Loader binding. When the Response Agent needs to run generated code, it calls env.LOADER.load() with the source code, which creates a fresh isolate on the fly. The host sends a request to it, reads the response, and the isolate is discarded.

Key properties:

  • Sandboxed — the Dynamic Worker runs in its own V8 isolate, not in the parent Worker’s process. A bug or infinite loop in generated code can’t crash the host.
  • Constrained bindings — you choose what the Dynamic Worker can access. In our case: nothing. No KV, no D1, no secrets. Pass globalOutbound: null and the Dynamic Worker can’t even reach the network.
  • Ephemeralload() creates a one-off isolate. There’s nothing to clean up. No deployment artifact lingers after the request completes.

This is not the Dynamic Workers Playground (a persistent deployment tool). We use Dynamic Workers purely as a code-execution substrate: spin up, execute, return, discard.

Dynamic Workers execution flow

flowchart LR
  A[Response Agent] --> B[Generate Worker Code
— LLM —]
  B --> C[Worker Loader
env.LOADER.load]
  C --> D[Ephemeral
V8 Isolate]
  D --> E[Execute]
  E --> F[Result +
cpu_ms + cost]
  F --> G[UI Panel]

The flow

Here’s what happens when the Response Agent decides it needs custom logic:

Response Agent (Workers AI)

    ├── 1. Generates Worker source code (e.g. IOC scorer)

    ├── 2. Calls env.LOADER.load(generatedCode, { globalOutbound: null })

    ├── 3. Sends a request to the Dynamic Worker with incident data

    ├── 4. Reads response + captures cpu_ms from timing headers

    └── 5. Returns result + execution time + cost estimate to UI

The generated code is plain JavaScript — a fetch handler that receives incident data in the request body, runs whatever logic the model wrote (scoring, matching, validation), and returns a JSON response. The model produces the code, but your host Worker controls the execution environment.

Step 1: Replace your database ID and redeploy

This lesson is a STATE change — the directory is already at state-7-dynamic-workers, which switches on the Generated Worker panel. No new binding is required for the workshop build.

Replace REPLACE_WITH_YOUR_DATABASE_ID in lesson-14-dynamic-workers\wrangler.jsonc with your D1 database_id from lesson 06, then redeploy:

npm run deploy

Step 2: See the generated Worker

Open your deployed app and investigate any incident. When the action plan appears, look for the Generated Worker panel. It shows:

  • Source code — the actual JavaScript the model wrote, syntax-highlighted
  • Execution result — the output (e.g., an IOC risk score, a CIDR match verdict, a blocklist validation result)
  • Execution time — wall-clock milliseconds for the Dynamic Worker invocation
  • Estimated cost — computed from the request count and CPU time

Try investigating different incidents. The malware incident might produce an IOC scorer that rates the C2 indicators. The data exfiltration incident might produce a CIDR matcher that checks whether destination IPs fall within known cloud storage ranges. Different incidents, different generated code.

Wiring it live (optional, beyond the workshop)

A live integration adds the Worker Loader binding to wrangler.jsonc:

"worker_loaders": [{ "binding": "LOADER" }]

and generates + executes the Worker in the Response Agent:

const code = await generateWorkerSource(env.AI, triageCard); // model writes a fetch handler
const worker = await env.LOADER.load(code, { globalOutbound: null }); // no bindings, no network
const res = await worker.fetch(
  new Request("https://x/", { method: "POST", body: JSON.stringify(incident) }),
);
const result = await res.json();
// capture cpu_ms from timing, then cost = requests * $0.30/M + cpu_ms * $0.02/M

The generated Worker runs in its own isolate with no bindings and globalOutbound: null, so it cannot reach KV, D1, secrets, or the network — isolation by construction, regardless of what the model wrote.

What you’ve built

With Dynamic Workers, the Response Agent’s action space is no longer limited to a fixed set of tools. It can generate and execute bespoke logic per incident — scoring, matching, validating, transforming — in a sandboxed environment with full visibility into the generated code, its output, and its cost. The analyst sees everything: what code ran, what it returned, how long it took, and what it cost.

This completes the advanced execution layer. In the final lesson, you’ll step back and compare your deployed app against the reference demo, review the five blueprint agents you didn’t build, and explore extension paths for taking this further.

Key takeaways

Before moving on, make sure these ideas land. They are the reason this lesson matters in the larger triage system.

  1. 1

    Dynamic Workers execute generated code at runtime

    The Response Agent can produce a small helper Worker, load it into a fresh isolate, execute it, and discard it. This is runtime code execution, not a permanent deployment.

  2. 2

    Isolation comes from constrained bindings

    A host Worker can load generated code with no bindings and no outbound network access. That makes the generated code useful while sharply limiting what it can touch.

  3. 3

    Code generation extends the action plan

    Instead of only recommending fixed steps, the agent can create one-off helpers like IOC scorers or allowlist checks. The generated code and result stay visible for analyst review.

Knowledge checkRequired to continue