Dynamic Workflow
Let an Agent generate JavaScript that calls, combines, and summarizes its Session tools.
On this page
Enable Dynamic WorkflowExecution contractWhat the generated program looks likeWhich tools are availableClient function handoffLifetime, cancellation and recoveryIsolation and resultsA Dynamic Workflow is a short JavaScript program the Agent writes for the current task. The program can call several tools, 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 a Rebyte extension to the Agents API. The JavaScript example uses the official openai@7.15.0 package. CLI version 0.2.3 or later supports this tool declaration.
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 OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.REBYTE_API_KEY,
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 official TypeScript types do not include dynamic_workflow. For typed code, use the official client’s post() method with an explicit response type and OpenAI-Beta: agents=v1, or configure the saved Agent through the CLI and reference its ID. The JavaScript example above sends the extension without modifying the SDK.
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 Rebyte CLI 0.2.3 or later, a saved Agent manifest can include:
model = "gpt-5.6-luna"
instructions = "Use Dynamic Workflow to combine web searches sequentially."
[[tools]]
type = "dynamic_workflow"
[[tools]]
type = "web_search"
Execution contract
- The Agent calls
run_code({code})with an async JavaScript function. - The program calls configured tools as
await tools.NAME(arguments). Await each call before making the next one; parallel tool calls are not supported. - Server tools execute on Rebyte. Application functions pause the program and
appear in the Session's
required_actions. - Your application supplies a function result using the normal Session events endpoint. The same program continues with that result.
- The program's return value becomes the
run_coderesult. The Agent can then answer or continue its Turn.
The program uses the Session's selected model and tools. Enabling Dynamic Workflow adds no credentials or permissions. Ordinary direct tool calls remain available.
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 command, stdin,
patch and image tools. Commands operate in that Session's environment; its files
follow the normal environment lifecycle.
They survive the end of an individual program.
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 | Yes | Directly available functions use 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.
Deferred application functions must first be discovered by the Agent before they
are available inside a program. A configured MCP tool or application function
may call another model; there is no implicit call_model helper.
Client function handoff
For a configured function named get_number, the program may contain:
async () => {
const number = await tools.get_number({ account: 'account-123' });
return { doubled: number * 2 };
}
Read required_actions from GET /v1/agents/sessions/:sessionId, or observe
agent.session.requires_action. Execute the requested function in your
application and submit the exact turn_id and call_id:
POST /v1/agents/sessions/:sessionId/events
Authorization: Bearer <organization-api-key>
OpenAI-Beta: agents=v1
Idempotency-Key: number-result-123
Content-Type: application/json
{
"events": [{
"type": "agent.session.input.tool_result",
"turn_id": "turn_...",
"call_id": "call_...",
"success": true,
"output": "21"
}]
}
Submit a nested function result in its own request, with exactly one event.
Do not combine it with messages, cancellation or other results. To report a
failure, use success: false and an error string instead of output. The
program can catch that error; an uncaught error fails run_code and returns the
failure to the Agent. The Agent may continue or choose another action.
Keep the same Idempotency-Key and identical body when retrying a submission.
A conflicting key, wrong Turn, stale call or expired result is rejected with 409.
Only execute calls listed in required_actions: the outer run_code Item itself
is server-executed and does not ask your application for a result.
Lifetime, cancellation and recovery
The enclosing Turn waits while its program is running or waiting for a function. A program finishing does not finish the Turn: the Agent receives its result and continues. Cancelling the Turn also cancels its program. Cancellation is asynchronous and does not undo completed external actions. Disconnecting an HTTP request or event stream does not cancel execution.
| Boundary | Contract |
|---|---|
| Waiting for one application function | Up to 24 hours from that call's pause |
| A later function in the same program | A new 24-hour waiting deadline |
| One active execution segment, before completion or the next pause | At most 60 seconds; client waiting is outside this limit |
| Whole ordinary Turn or whole program | No separate fixed total lifetime; 24 hours is not a total runtime allowance |
| Scheduled Turn | The Schedule's timeout_seconds also covers preparation, execution and waiting |
| Program source | At most 32,768 JavaScript string code units |
A pending call survives service restarts. Resume with its existing IDs rather than submitting the original prompt again. An expired nested wait fails the program and returns an error to the Agent. An ordinary direct function wait has the separate Turn timeout behavior.
When continuing after a pause, completed calls return their recorded results. JavaScript computation may run again to reach the pending call, so call tools sequentially and capture nondeterministic values explicitly:
const requestId = await codemode.step('request-id', () => crypto.randomUUID());
const result = await tools.create_order({ request_id: requestId });
Use stable step names and JSON-compatible step values. Do not depend on an unrecorded random value or current time to choose tool arguments or call order. If an active execution fails with an ambiguous tool outcome, Rebyte does not blindly restart the program. Your application and tool providers remain responsible for idempotency of external side effects.
Isolation and results
Programs cannot directly use the network, filesystem, Node.js packages or subprocesses. External actions go through the configured tools, with the same organization, Session and tool permissions as ordinary calls. Tool credentials are never exposed to program source. Separate invocations do not share JavaScript globals; use an environment or your own service for persistent business data.
The outer run_code call is a function_call Item containing arguments.code.
Nested application functions have their own call/result Items and required actions.
Server calls inside the program are not separate public Session Items. Returning
an image from a nested tool gives the program image data; it does not automatically
attach it to the outer model's visual context.
For code you want to run repeatedly without an outer model, create a Workflow Agent. It has its own run resource, client-result endpoint, and explicit create, test and publish lifecycle.