Skip to content

refactor(codemode): modular SDK — remove internal LLM, add Executor interface#879

Merged
mattzcarey merged 17 commits into
mainfrom
feat-remove-llm-codemode
Feb 20, 2026
Merged

refactor(codemode): modular SDK — remove internal LLM, add Executor interface#879
mattzcarey merged 17 commits into
mainfrom
feat-remove-llm-codemode

Conversation

@mattzcarey

@mattzcarey mattzcarey commented Feb 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Rewrites @cloudflare/codemode from a monolithic experimental_codemode() + CodeModeProxy entrypoint into a modular, composable SDK with proper test coverage, documentation, and examples.

What changed

Package (@cloudflare/codemode):

  • Delete src/ai.ts — removes experimental_codemode(), CodeModeProxy, the internal LLM call (generateObject with hardcoded gpt-4.1). The package no longer owns an LLM call or model choice.
  • New @cloudflare/codemode/ai exportcreateCodeTool() returns a standard AI SDK Tool. Users wire their own model via streamText/generateText. The /ai subpath isolates the ai peer dependency — the root export (@cloudflare/codemode) contains executor, type generation, and utilities with no ai dependency.
  • New Executor interface — minimal execute(code, fns) → ExecuteResult contract. Implement it for any sandbox. Ships DynamicWorkerExecutor for Cloudflare Workers.
  • ToolDispatcher replaces CodeModeProxy — both use Workers RPC, but ToolDispatcher is passed as a parameter to evaluate(dispatcher) instead of being injected as an env binding. No service bindings needed.
  • globalOutbound: null by defaultfetch() and connect() are blocked at the runtime level. Configurable per-executor.
  • AST-based code normalization — uses acorn to parse LLM output into async arrow functions. Handles edge cases regex couldn't.
  • sanitizeToolName() — converts MCP-style tool names (hyphens, dots) into valid JS identifiers.
  • needsApproval filtering — tools with needsApproval are automatically excluded from codemode (both from type generation and the executor). They should be used via standard AI SDK tool calling instead. Full approval support is planned.
  • Console captureconsole.log/warn/error captured and returned in ExecuteResult.logs.
  • Execution timeout — configurable via DynamicWorkerExecutorOptions.timeout (default 30s).
  • Build aligned with other packagesfixedExtension: false, skipNodeModulesBundle: true, .js/.d.ts output, require condition in exports.

Tests:

  • 55 unit/integration testsvitest-pool-workers with a real WorkerLoader binding. No mocks. Tests cover executor, tool creation, type generation, code normalization, needsApproval filtering.
  • 5 e2e tests — Playwright + wrangler dev with a real AI binding (@cf/zai-org/glm-4.7-flash). Full pipeline: user prompt → LLM generates code → DynamicWorkerExecutor runs it → tools called via RPC → result returned.

Examples:

  • Codemode example (examples/codemode/) — rewritten with AIChatAgent + useAgentChat, Kumo design system, @cloudflare/agents-ui (ConnectionIndicator, ModeToggle, PoweredByAgents, ThemeProvider). Settings panel for switching executors. SQLite-backed project management tools.
  • Playground demo (examples/playground/) — new Codemode demo under the AI category. Uses AIChatAgent, Kumo components, collapsible tool cards with code/result/console output.

Documentation:

  • docs/codemode.md — full rewrite covering createCodeTool, DynamicWorkerExecutor, Executor interface, network isolation, API reference, agent integration, MCP tools, limitations.
  • packages/codemode/README.md — updated for the new API with architecture diagram, configuration tables, and usage examples.

Architecture

User message
  → User calls streamText/generateText with their own model + codemode tool
  → LLM generates code
  → DynamicWorkerExecutor spins up dynamic Worker via WorkerLoader
  → ToolDispatcher (extends RpcTarget) passed directly to Worker's evaluate() method
  → Tool calls: codemode.tool() → dispatcher.call(name, args) → RPC back to host

Required wrangler config:

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

One binding (WorkerLoader) instead of three.

The Executor interface

interface Executor {
  execute(
    code: string,
    fns: Record<string, (...args: unknown[]) => Promise<unknown>>
  ): Promise<ExecuteResult>;
}

interface ExecuteResult {
  result: unknown;
  error?: string;
  logs?: string[];
}

Import paths

// AI-dependent (createCodeTool)
import { createCodeTool } from "@cloudflare/codemode/ai";

// AI-independent (executor, types, utilities)
import { DynamicWorkerExecutor, generateTypes, sanitizeToolName } from "@cloudflare/codemode";

Test plan

  • npm run test in packages/codemode/ — 55 tests pass (executor, tool, types, needsApproval) via vitest-pool-workers
  • npm run test:e2e in packages/codemode/ — 5 e2e tests pass via Playwright + wrangler dev + real AI binding
  • npm run build — package builds with clean .js/.d.ts output
  • npx tsc --noEmit — no type errors in package, examples, or playground
  • Playground builds and type-checks with new Codemode demo
  • Codemode example builds and type-checks with AIChatAgent + Kumo

@changeset-bot

changeset-bot Bot commented Feb 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d9e8e4c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/codemode Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mattzcarey
mattzcarey force-pushed the feat-remove-llm-codemode branch 2 times, most recently from 2726434 to ba5a1e9 Compare February 18, 2026 13:55
@mattzcarey
mattzcarey marked this pull request as ready for review February 18, 2026 13:55
@threepointone

Copy link
Copy Markdown
Contributor

if this is ready for review, then please write a PR description that details what this is, what reviewers should look out for, just help me out here haha. Also please add a detailed changeset.

…e app

Replace the monolithic ai.ts with a modular structure:
- executor.ts: Executor interface + DynamicWorkerExecutor using Workers RPC
  via ToolDispatcher (RpcTarget) instead of broken globalOutbound fetch
- tool.ts: createCodeTool with AST-based code normalization (acorn),
  console log capture, timeout support, and error handling
- types.ts: generateTypes + sanitizeToolName for type generation
- index.ts: clean public API surface

Key improvements:
- globalOutbound defaults to null (runtime-enforced fetch/connect blocking)
- Callers can pass a Fetcher for controlled outbound access
- String concatenation module building (no template literal injection)
- Comprehensive test suite (58 tests across 3 files)

Redesign example app as "Planwise" task tracker with chat-first UI,
collapsible tool cards, settings panel, and refined dark theme.
Tools use AI SDK tool() wrapper.
@mattzcarey
mattzcarey force-pushed the feat-remove-llm-codemode branch from 6be31f7 to fb31717 Compare February 19, 2026 17:27
@mattzcarey
mattzcarey force-pushed the feat-remove-llm-codemode branch from 47600ad to f7ed0e0 Compare February 19, 2026 17:46
@mattzcarey mattzcarey changed the title codemode sdk rewrite refactor(codemode): modular SDK with Workers RPC and Executor interface Feb 19, 2026
@pkg-pr-new

pkg-pr-new Bot commented Feb 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/agents@879
npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/ai-chat@879
npm i https://pkg.pr.new/cloudflare/agents/@cloudflare/codemode@879
npm i https://pkg.pr.new/cloudflare/agents/hono-agents@879

commit: d9e8e4c

Replace mock-based tests with real Workers runtime tests:
- Use defineWorkersConfig with a WorkerLoader binding
- Delete cloudflare:workers mock — RpcTarget is now real
- Executor tests use real DynamicWorkerExecutor + real WorkerLoader
- Tests verify actual sandboxed execution, console capture,
  fetch blocking, timeout, template literals, and tool dispatch
@mattzcarey mattzcarey changed the title refactor(codemode): modular SDK with Workers RPC and Executor interface refactor(codemode): modular SDK — remove internal LLM, add Executor interface Feb 19, 2026
@mattzcarey
mattzcarey force-pushed the feat-remove-llm-codemode branch from 17289b5 to 516070e Compare February 19, 2026 18:15
mattzcarey and others added 6 commits February 19, 2026 18:15
…test.d.ts

Rename src/test/ to src/tests/ for consistency. Add cloudflare-test.d.ts
declaring ProvidedEnv with LOADER binding for IDE type resolution.
Fix Mock contravariance in executor tests by using explicit ToolFns type.
Replace broken @cloudflare/codemode/ai re-export in agents package with
a runtime error directing users to createCodeTool().
Use Streamdown component for assistant text and reasoning blocks,
replacing raw text rendering with proper markdown streaming support.
Refactor Codemode API and update docs and example app. Move the AI-dependent createCodeTool export to @cloudflare/codemode/ai and remove experimental_codemode/CodeModeProxy; introduce ToolDispatcher/DynamicWorkerExecutor and a minimal Executor/ExecuteResult interface. Revise docs to explain the new createCodeTool flow, network isolation, and configuration changes. Revamp the codemode example: replace UI with kumo/agents-ui components, simplify index.html, update client/server code (including renaming onStateUpdate -> onStateChanged), shrink CSS, and add e2e/playwright test scaffolding. Also update package metadata and build scripts to match the new structure.
Introduce a full Codemode example and playground demo, refactor agent/server logic, and ensure tools that require approval are excluded from code-execution toolsets.

Key changes:
- Add Codemode UI/demo to examples/playground and link it in the sidebar and routes; include a new CodemodeAgent for the playground.
- Expand examples/codemode README and update client to use useAgentChat, ThemeProvider, and improved streaming/UI handling.
- Refactor examples/codemode server: migrate Codemode to AIChatAgent, replace stateful DO message plumbing with agent chat streaming, use pruneMessages, and expose executor/type helpers.
- Update createCodeTool to skip tools with needsApproval (helper hasNeedsApproval) and add tests covering this behavior.
- Update packages/codemode exports/types and build script (d.ts handling and formatting), and add related dependency entries in example package.json files and config tweaks (vite, wrangler).
- Document the current limitation: tools with needsApproval are not supported for codemode execution and should be invoked via the standard AI SDK flow.

@threepointone threepointone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving, but please test all the changes I've made

@threepointone

Copy link
Copy Markdown
Contributor

I updated the PR:

  • rewrote the example with kumo and AIChatAgent, expanded README
  • added a codemode section to the playground
  • rewrote docs/codemode.md
  • moved createCodeTool to /ai
  • documented a limitation that this won't work with tools with needsApproval (and we'll figure that out later)
  • added e2e tests
  • some misc cleanups

please test all my changes and try out your example and the playground. tentatively stamping, land when you're happy with it

Update examples/codemode/wrangler.jsonc to remove the "experimental" entry from compatibility_flags so the example only uses "nodejs_compat". This cleans up the example configuration and disables experimental compatibility features for the codemode demo.
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.

2 participants