Dynamic Workflow
Let an Agent generate JavaScript that calls, combines, and summarizes its Session tools.
On this page
Enable Dynamic WorkflowHow it worksWhat the generated program looks likeWhich tools are availableAuthentication and isolationLifetime and limitsResults and current scopeA Dynamic Workflow is a short JavaScript program the Agent writes for the current task. The program can call several tools, run independent calls in parallel, branch on results, and return a compact answer to the model. You configure the available tools; the model chooses how to combine them.
Dynamic Workflow is available through the Managed Agent API. Use @rebyteai/agent-sdk or @rebyteai/cli version 0.2.3 or later.
Enable Dynamic Workflow
Add { type: 'dynamic_workflow' } to the Agent's tools. It is opt-in and adds the model-facing run_code tool. It does not add any external tools or allocate a Session VM by itself.
This complete example connects to a public MCP server and asks the Agent to discover and call a tool inside one program. Set REBYTE_API_KEY to a key for your organization with tasks:read and tasks:write.
import Rebyte from '@rebyteai/agent-sdk';
const client = new Rebyte({
baseURL: 'https://api.rebyte.ai/v1',
maxRetries: 0,
});
const stream = await client.beta.agents.sessions.create({
agent: {
model: 'gpt-5.6-luna',
instructions: 'Use run_code exactly once: within that program, call tools.search_tools with server docs and query read_wiki_structure, then call tools.call_tool with the returned server/name and arguments { repoName: "modelcontextprotocol/python-sdk" } to read its actual output. Return { output: actualToolResult } and summarize it. Do not call MCP tools outside the program.',
tools: [
{ type: 'dynamic_workflow' },
{
type: 'mcp',
server_label: 'docs',
connection_origin: 'service',
required: true,
allowed_tools: ['read_wiki_structure'],
transport: {
type: 'http',
server_url: 'https://mcp.deepwiki.com/mcp',
},
},
],
},
input: 'Find the documentation structure for modelcontextprotocol/python-sdk.',
stream: true,
});
let sessionId;
try {
for await (const event of stream) {
if (event.type === 'agent.session.created') sessionId = event.session.id;
if (event.type === 'agent.session.turn.output_text.delta') {
process.stdout.write(event.delta);
}
if (event.type === 'agent.session.turn.failed' ||
event.type === 'agent.session.failed') {
throw new Error(JSON.stringify(event));
}
}
} finally {
if (sessionId !== undefined) {
await client.beta.agents.sessions.delete(sessionId);
}
}
The SDK sets OpenAI-Beta: agents=v1 automatically. Raw HTTP requests need that header and Authorization: Bearer <REBYTE_API_KEY>. The same tool declaration works on saved Agents and Session overrides. An explicit Session agent.tools array replaces the entire saved tool list; include both dynamic_workflow and the tools it should use.
With Toolkit CLI 0.2.3 or later, a saved Agent manifest can include:
model = "gpt-5.6-luna"
instructions = "Use Dynamic Workflow to combine independent web searches."
[[tools]]
type = "dynamic_workflow"
[[tools]]
type = "web_search"
How it works
- Select tools. Rebyte takes the server-executable tools from the current Session's Tool Registry and includes their descriptions and argument schemas in
run_code. Before executing a program, Rebyte prepares the Session environment and MCP connections when configured. - Generate a program. The selected model calls
run_code({ code })with an async JavaScript function. The function decides the order of calls, parallelism, conditions, and aggregation. - Create an execution. Rebyte establishes a short-lived identity for this Session and sends the program to a Cloudflare host Worker. The host creates a fresh V8 isolate through the Cloudflare Code Mode SDK.
- Execute tools.
await tools.NAME(arguments)invokes a host-provided RPC function. The host calls Rebyte Relay, which checks the execution's identity and invokes the same tool implementation used by ordinary Agent calls. - Return to the model. Tool results resolve the program's
awaitexpressions. Its final return value goes back to the model, which can answer the user or continue the Turn. Each nested tool call does not require another planning step by the outer model.
The JavaScript execution interface is model-independent. It uses the Session's selected model and normal tool-calling adapter; it does not depend on an OpenAI-specific program execution API. Code quality and tool-calling support still depend on the selected model.
What the generated program looks like
For the MCP configuration above, a program could be:
async () => {
const matches = await tools.search_tools({
server: 'docs',
query: 'read_wiki_structure',
limit: 5,
});
const match = matches.find(tool => tool.name === 'read_wiki_structure');
if (!match) throw new Error('The wiki tool is unavailable');
const result = await tools.call_tool({
server: match.server,
name: match.name,
arguments: { repoName: 'modelcontextprotocol/python-sdk' },
});
return result;
}
tools.call_tool returns an object with output containing the MCP result and error: null on success. Tool failures reject the call. For example, text content is under result.output.content.
The SDK normalizes and parses the source before execution. Syntax errors and tool errors are returned to the Agent as execution errors. Tool arguments are checked by the existing tool handlers. These checks do not prove that a valid program solves the user's task correctly.
A Session with an openai_hosted environment also exposes its environment tools. For example, the model can generate two independent command calls:
async () => {
const results = await Promise.all([
tools.exec_command({ cmd: 'printf FIRST', max_output_tokens: 100 }),
tools.exec_command({ cmd: 'printf SECOND', max_output_tokens: 100 }),
]);
return results.map(result => result.output);
}
The JavaScript runs in the temporary isolate. The shell commands run in the Session's own VM. The VM and its files follow the normal environment lifecycle; they do not disappear when the JavaScript isolate ends.
Which tools are available
| Tool category | Available inside the program | Configuration |
|---|---|---|
| Commands, stdin, patches, images | Yes | Session has a managed environment. |
| MCP discovery, calls, and resources | Yes | Corresponding MCP connections are configured for this Session. Server allowlists and credential rules still apply. |
| Web search | Yes | web_search is configured in live mode. |
| Application/client functions | No | Continue using normal required_actions and client tool results. |
Client function discovery (tool_search) | No | Remains in the outer Agent loop. |
run_code itself | No | A program cannot recursively start another Dynamic Workflow through this tool. |
A Dynamic Workflow does not automatically get the platform's complete tool catalog. Adding the feature alone exposes no new commands, connectors, or customer credentials. Ordinary direct tool calls remain available alongside run_code.
If a server-side MCP tool calls another language model, the program can invoke it like any other configured MCP tool. There is no implicit call_model function. An application-side function that calls a model still uses the normal client-function path.
Authentication and isolation
Your application uses its normal Rebyte API key to create and operate a Session. Rebyte handles the internal Worker authentication; customers do not create Cloudflare credentials or pass a token into generated JavaScript.
Each execution is bound to an organization, Session, Turn, model step, parent tool call, and allowed tool list. Relay creates a random short-lived token and stores its hash with that identity in the existing Redis service. A separate service credential authenticates Relay to the Cloudflare host Worker.
Only the host's tool closures receive the execution token. Generated code receives RPC functions, without the token or host credentials. When a function calls back, Relay derives the identity from its stored execution record and checks the active Session and tool permissions. The callback cannot choose a different organization or Session.
The isolate has no direct filesystem, Node.js packages, subprocesses, or outbound network access. External work goes through the supplied tools and their existing credential handling. Every program gets fresh JavaScript state. One host Worker deployment serves many executions; each execution has its own isolate and bound identity.
Lifetime and limits
| Limit | Current value |
|---|---|
| Maximum execution lifetime | 300 seconds |
| Nested tool calls per execution | 32, including calls whose arguments fail validation |
| Source length | 32,768 JavaScript string code units |
| Serialized JSON output | 512 KiB per nested result and overall result envelope |
| JavaScript state between executions | None |
The 300-second maximum comes from one shared constant. Relay calculates one absolute deadline when execution begins, after Session environment and MCP setup. Redis, the Worker and all tool callbacks use that deadline. Tool calls, result writes and duplicate requests never extend it. This limits one run_code execution, not the whole Session or Turn.
A single Redis key holds the execution's identity and all call receipts. Completion or failure deletes it; cancellation immediately makes further callbacks invalid and deletes it when cancellation settles. Expiration removes the whole key. Late results cannot recreate it. A missing key always rejects the callback.
Repeated internal call IDs with identical arguments return a recorded result while the execution is active; conflicting or still-running calls are rejected. The host does not automatically retry a program or tool call. A later model step may decide to generate a new program; instructions can prohibit that when needed. Cancellation and failure do not undo external changes already made.
Results and current scope
The outer run_code call appears as a function_call Item containing the generated source in arguments.code. It executes on the server and does not request a client tool result. The program's result or error enters model history; the final assistant response is available through the normal Session Items and text events.
Nested calls are not separate public Session Items in this version. Their Redis receipts expire with the execution. Returning an image from a nested tool gives the program image data; it does not automatically attach an image to the outer model's visual context.
This version covers task-specific execution within the current Turn. It does not provide saved/static workflows, program replay after a crash, persistent JavaScript state, or client-function pause/resume inside the program.