diff --git a/README.md b/README.md index e5a8853..c45a9d5 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ await sandbox.destroy(); | `AskUser` | Ask user clarifying questions | `askUser: true` | | `EnterPlanMode` | Enter planning/exploration mode | `planMode: true` | | `ExitPlanMode` | Exit planning mode with a plan | `planMode: true` | +| `codemode` | Write code to orchestrate selected tools via Cloudflare Codemode | `codemode: { executor }` | | `Skill` | Execute skills | `skill: { skills }` | | `WebSearch` | Search the web | `webSearch: { apiKey }` | | `WebFetch` | Fetch URL and process with AI | `webFetch: { apiKey, model }` | @@ -233,6 +234,10 @@ const { tools, planModeState } = createAgentTools(sandbox, { apiKey: process.env.PARALLEL_API_KEY, model: anthropic('claude-haiku-4'), }, + codemode: { + executor: new DynamicWorkerExecutor({ loader: env.LOADER }), + includeTools: ['Read', 'Glob', 'Grep'], // Opt into write tools explicitly + }, // Tool-specific config tools: { @@ -251,6 +256,49 @@ const { tools, planModeState } = createAgentTools(sandbox, { }); ``` +### Codemode + +BashKit can expose a Cloudflare Codemode tool so the model writes code that orchestrates BashKit tools instead of making many individual tool calls. Install `@cloudflare/codemode`, provide an executor, and enable `codemode`: + +Cloudflare Codemode currently targets AI SDK v6. BashKit's codemode adapter follows that path; AI SDK v5 support is intentionally not a compatibility goal for codemode. + +```typescript +import { DynamicWorkerExecutor } from '@cloudflare/codemode'; +import { createAgentTools, createPrepareStep } from 'bashkit'; +import { streamText } from 'ai'; + +const { tools, planModeState } = await createAgentTools(sandbox, { + planMode: true, + context: { executionPolicy: {} }, + codemode: { + executor: new DynamicWorkerExecutor({ loader: env.LOADER }), + includeTools: ['Read', 'Glob', 'Grep', 'Bash'], + tools: { + // Extra AI SDK tools exposed inside the default bashkit.* namespace + }, + providers: [ + { + name: 'repo', + tools: { + // Extra tools exposed as repo.* + }, + }, + ], + }, +}); + +await streamText({ + model, + tools, + messages, + prepareStep: createPrepareStep({ planModeState }), +}); +``` + +By default, generated code calls BashKit tools through BashKit's `bashkit.*` namespace, for example `await bashkit.Grep(...)`. Use `providers` when you want additional named tool groups such as `repo.SummarizeFile(...)`. + +Client-intervention tools are excluded from codemode automatically: `AskUser`, `EnterPlanMode`, `ExitPlanMode`, tools without `execute`, and tools with `needsApproval`. Top-level `includeTools` narrows the default `bashkit.*` namespace. Named providers can define their own `includeTools` or `excludeTools`. + ### Configuration Options #### Global Config @@ -1003,6 +1051,7 @@ Creates a set of agent tools bound to a sandbox instance. ### Optional Tools (also available via config) - `createAskUserTool(config?)` - Emit a deferred AskUser tool call for the client +- `createCodemodeTool(tools, config)` - Create a Cloudflare Codemode tool from a filtered tool set - `createEnterPlanModeTool(state)` - Enter planning/exploration mode - `createExitPlanModeTool(state, onPlanSubmit?)` - Exit planning mode with a plan - `createSkillTool(skills)` - Execute loaded skills diff --git a/bun.lockb b/bun.lockb index 97fe9ca..a289903 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docs/src/app/MobileNav.tsx b/docs/src/app/MobileNav.tsx index 9601b18..c607732 100644 --- a/docs/src/app/MobileNav.tsx +++ b/docs/src/app/MobileNav.tsx @@ -10,6 +10,7 @@ const links = [ { href: "/", label: "Overview" }, { href: "/getting-started", label: "Getting Started" }, { href: "/tools", label: "Tools" }, + { href: "/codemode", label: "Codemode" }, { href: "/sandboxes", label: "Sandboxes" }, { href: "/context", label: "Context" }, { href: "/api-reference", label: "API Reference" }, diff --git a/docs/src/app/SideNav.tsx b/docs/src/app/SideNav.tsx index 28a7807..4bdb61f 100644 --- a/docs/src/app/SideNav.tsx +++ b/docs/src/app/SideNav.tsx @@ -53,6 +53,17 @@ const links: { { id: "todowrite", text: "TodoWrite" }, ], }, + { + href: "/codemode", + label: "Codemode", + items: [ + { id: "overview", text: "Overview" }, + { id: "setup", text: "Setup" }, + { id: "batched-workflows", text: "Batched Workflows" }, + { id: "tool-selection", text: "Tool Selection" }, + { id: "policy", text: "Policy" }, + ], + }, { href: "/sandboxes", label: "Sandboxes", diff --git a/docs/src/app/api-reference/page.tsx b/docs/src/app/api-reference/page.tsx index 15cf511..f9e8cff 100644 --- a/docs/src/app/api-reference/page.tsx +++ b/docs/src/app/api-reference/page.tsx @@ -98,6 +98,13 @@ const { tools, budget } = await createAgentTools(sandbox, { Web search configuration. Presence enables WebSearch and WebFetch tools. + + Cloudflare Codemode adapter. Adds a single codemode{" "} + tool that can orchestrate selected executable tools. Excludes + client-intervention tools and tools with{" "} + needsApproval. Supports extra default-namespace tools + and named providers. + Enable tool result caching. Pass true for defaults or an object for fine-grained control. diff --git a/docs/src/app/codemode/page.tsx b/docs/src/app/codemode/page.tsx new file mode 100644 index 0000000..1a0e050 --- /dev/null +++ b/docs/src/app/codemode/page.tsx @@ -0,0 +1,289 @@ +"use client"; + +import { CodeBlock } from "../components/CodeBlock"; +import { Footer } from "../Footer"; + +export default function Codemode() { + return ( + <> +
+
+

Codemode

+

+ Let the model write code that orchestrates BashKit tools in batches. +

+
+ +
+

Overview

+

+ Codemode wraps selected BashKit tools behind one Cloudflare Codemode + tool. Instead of asking the model to call Glob, then{" "} + Grep, then Read one turn at a time, the + model can write a small JavaScript async arrow function that calls + those tools with loops, branches, and parallel work. +

+

+ BashKit does not run the code itself. It delegates to Cloudflare + Codemode's executor, while the code still calls BashKit's + policy-wrapped tools. That means context layers, plan-mode gates, + output policy, and sandbox restrictions keep applying. +

+
+ +
+

Setup

+

+ Install Cloudflare Codemode alongside BashKit, then provide an + executor through codemode config. +

+

+ Cloudflare Codemode currently targets AI SDK v6, so BashKit's + codemode adapter follows that path. +

+ + +
+ +
+

Batched Workflows

+

+ Codemode is useful when the model needs to fan out across files, + combine results, or retry/narrow a search. The generated code can + batch those steps into one tool call. +

+ { + const files = await bashkit.Glob({ + pattern: 'src/**/*.ts', + path: null, + }); + + const planModeMatches = await Promise.all( + files.matches.map((file) => + bashkit.Grep({ + pattern: 'PlanMode|planModeState|ExitPlanMode', + path: file, + glob: null, + type: null, + output_mode: 'content', + '-i': null, + '-n': true, + '-B': null, + '-A': null, + '-C': 2, + head_limit: 20, + offset: null, + multiline: null, + }), + ), + ); + + const interestingFiles = planModeMatches + .filter((result) => 'matches' in result && result.matches.length > 0) + .map((result) => result.matches[0].file); + + const snippets = await Promise.all( + interestingFiles.slice(0, 5).map((file_path) => + bashkit.Read({ + file_path, + offset: 1, + limit: 120, + }), + ), + ); + + return { + scanned: files.count, + matchedFiles: interestingFiles, + snippets, + }; +};`} + /> +

+ That would normally take many model/tool turns. With Codemode, the + model can express the workflow as code and return a structured + result. +

+
+ +
+

Tool Selection

+

+ Keep the inner tool set narrow. includeTools is the + safest default because generated code can loop and fan out calls. +

+ +

+ BashKit always excludes client-intervention tools from Codemode: + AskUser, EnterPlanMode,{" "} + ExitPlanMode, tools without an execute{" "} + function, and tools with needsApproval. +

+
+ +
+

Namespaces

+

+ The tool exposed to the model is named codemode by + default. Inside the generated JavaScript, selected BashKit tools are + exposed under BashKit's bashkit.* namespace. Tool + methods keep BashKit's public tool names, so the grep tool is{" "} + bashkit.Grep(...). +

+ { + const files = await bashkit.Glob({ + pattern: 'src/**/*.ts', + path: null, + }); + + return files.matches; + }\`, +});`} + /> +

+ Use providers when you want separate namespaces for + custom tool groups. Each provider gets its own object inside the + generated code, which keeps domain-specific helpers easier to scan. +

+ { + const todos = await bashkit.Grep({ + pattern: 'TODO|FIXME', + path: 'src', + glob: '*.ts', + type: null, + output_mode: 'content', + '-i': null, + '-n': true, + '-B': null, + '-A': null, + '-C': 2, + head_limit: 20, + offset: null, + multiline: null, + }); + + const issues = await github.ListIssues({ + labels: ['tech-debt'], + state: 'open', + }); + + return { todos, issues }; +};`} + /> +

+ Executable-only and needsApproval filtering applies to + every namespace. Top-level includeTools narrows the + default bashkit.* namespace; providers can define their + own includeTools or excludeTools. +

+
+ +
+

Policy

+

+ Codemode receives the same wrapped tools that normal model tool + calls receive. If plan mode is active, Bash,{" "} + Write, and Edit are still blocked by the + execution policy even when generated code calls them. +

+ +
+
+ +