Skip to documentation
Open Workflow Studio
On this page

Local browser JIT execution

Local Just-in-Time (JIT) Tool supplies model-callable JavaScript tools without deploying an Azure Function or using an Azure execution profile. Add it in Workflow Studio and connect its output to a Model, Responses, Chat Completions or Multi-Turn step. Preserve the capability descriptor as JSON if routing it through intermediate steps.

The model calls the same run-scoped jit_create_* and jit_invoke_* protocol as the Azure-backed step. Creation registers immutable tool definitions; invocation runs the selected definition with its explicit JSON input. Tools are not automatically executed just because a model mentions code in a final answer. Local tools retain schema validation, examples, replacement versions, invocation budgets, root-coordinator attribution and execution evidence.

This avoids Azure provisioning, cloud function startup and function HTTP requests. Server-hosted preview still exchanges source/input/results with the connected browser through Blazor; model requests still go to the configured provider. No latency benchmark or model-cost reduction is implied.

JavaScript contract

Every tool must use toolCodeLanguage: "JavaScript" and toolExpectedDuration: "short":

{
  "toolSpecification": "Accept {\"values\": [finite numbers]} and return {\"sum\": number}.",
  "toolCodeLanguage": "JavaScript",
  "toolCode": "module.exports = async function(toolInput) { if (!Array.isArray(toolInput.values) || !toolInput.values.every(Number.isFinite)) throw new Error('Finite values required'); return { sum: toolInput.values.reduce((sum, value) => sum + value, 0) }; };",
  "toolExpectedDuration": "short"
}

The create call takes a tools array of these definitions. Its returned toolId must be used for invocation, with an input JSON object. Standard JavaScript is available, but the CommonJS-style entry point does not supply Node.js: there is no require, package installation, filesystem, process, network, Web Crypto, DOM, persistent storage, application state or credential access. All required data must be passed explicitly. Each invocation uses a fresh worker; global variables do not persist across calls.

Version 1 uses exactly the four fields above. Version 2 also requires bounded inputSchema, outputSchema and 1-8 examples with input and expectedOutput JSON values. Examples execute in the same browser sandbox before a tool is ready and count toward the execution budget. A corrected version can use replacesToolId within the replacement and unique-tool budgets. Unsupported languages and long-running tool definitions are rejected; they never trigger a cloud fallback.

Outputs may be plain objects, arrays, strings, numbers, booleans or null. Undefined values, functions, BigInt, cycles, non-finite numbers, Map/Date instances, accessors and custom toJSON methods are not valid tool results. Convert these to plain JSON values first. Execution errors are distinct from a valid domain result such as {"passed":false}.

Limits and lifetime

Setting Default Allowed range
Unique tool versions 8 1-16
Invocation timeout 30 seconds 1-120 seconds
Executions, including examples 64 1-6,400
Concurrent executions 2 1-8, no more than the execution budget
Replacement versions 1 0-8

Source is limited to 128 KiB UTF-8. Input-object JSON and output JSON are each limited to 256 KiB UTF-8. Shared workflow text/memory budgets and the run-wide eight-execution concurrency ceiling still apply. Unique tools across all JIT capabilities in one run remain capped at 16.

Each run gets its own browser session with at most eight active workers and 6,400 single-use execution IDs. Browser-wide protection caps active workers at 32 and live sessions at 16, retaining at most 1,024 session-ID tombstones until the page is reloaded. Capacity exhaustion fails explicitly; it never reuses a closed session, evicts replay protection or falls back to Azure.

Root-coordinator-only authority and structured execution errors are enabled by default. Native multi-agent root authorization uses provider attribution, not model-written claims. Ordinary non-multi-agent function calls are supported too.

Optional asynchronous operations expose start, await-many and cancel. Start returns an operation ID, not a computation result. Every operation must be observed before the model finishes. Cancellation requests stop the relevant worker; await observes the terminal state. Operations are not durable jobs.

Stop, timeouts and finalization terminate active workers and remove their frames, ports and timers. Canceled queued invocations do not start a browser worker, even when a running invocation releases a slot during cancellation or cleanup. The browser also enforces a deadline independently of the .NET caller. Keep the page connected: browser suspension, crashes and network disconnection can interrupt the run, and disconnected cleanup is reported as unconfirmed rather than as a successful remote deletion. JIT checkpoints cannot resume stale capability IDs; start a new run after failure.

Isolation and its limits

Generated code is never evaluated in the application page, .NET host or workflow server. A same-app static bootstrap document is framed with sandbox="allow-scripts" and without allow-same-origin, giving it an opaque origin. A fresh dedicated worker is created inside that document. Only generated source, explicit input and execution limits are sent into it.

The bootstrap has a separate restrictive Content Security Policy: network and nested frames are denied, and dynamic JavaScript compilation is permitted only inside this isolated sandbox response. The application response policy is not relaxed. A correlated frame handshake and private message channel route results to the correct invocation. Returned data remains untrusted and is bounded and JSON-validated again by the host.

Origin isolation and CSP are the security boundary; removing worker APIs is additional hardening, not a replacement for them. Workers make non-yielding loops terminable without running them on the UI thread. This is not a hard per-tool heap quota, a dedicated virtual machine or a defense against browser-engine vulnerabilities. Large allocations can still exhaust browser memory. Use bounded computations and a supported, updated browser; do not weaken the sandbox or grant it same-origin privileges.

Do not pass credentials or unnecessary sensitive data as task input. Model calls and user-supplied input have their own data-handling requirements, independent of local tool execution.

Hosting, samples and exports

Both template pickers provide local JavaScript copies of the calculation baseline, Toolsmith, Resilience Replay, CommonGround and the asynchronous job observatory. The observatory's local workload is bounded below the browser's per-invocation limit; it does not simulate a long-lived Azure service. Original Azure templates and saved JitTool documents retain their original settings and semantics.

The local Bounded Search Observatory caps search at 20,000 states and each invocation at 60 seconds. Its assignmentChecksum, and Replay's jobsChecksum/inputChecksum, are explicitly non-cryptographic FNV-1a comparison checksums, not security digests or durable checkpoints. Toolsmith preserves the intentionally defective zero-quantity adapter and one immutable repair; CommonGround preserves its independent negative controls. The inventory baseline uses exact cents with a $10,000 unit-cost ceiling. Only generated computations become local: the models remain remote.

Local steps work in server preview and the WebAssembly PWA. PWA model requests remain subject to the configured provider and CORS/proxy settings. The module and sandbox entry are bundled assets rather than third-party runtime downloads.

The Aspire dashboard export includes the browser executor and isolated assets. Start local workflows from the interactive dashboard; leaving that page cancels its browser-dependent runs. Headless hub/API clients cannot provide that page's execution context and fail explicitly. Foundry Agent, Microsoft 365 Copilot and Copilot Studio headless exports reject local steps with instructions to choose Aspire or replace them with approved server-side tools.

When hosting behind another proxy, preserve the exact /js/workflowLocalJitSandbox.html response policy from WorkflowLocalJitSandboxPolicy, including sandbox allow-scripts, no outbound connections, same-app framing and Referrer-Policy: no-referrer. Do not apply its eval permission to other responses. The supplied server, development host, PWA nginx configuration and Aspire package apply the isolated policy themselves.

An unhandled error has occurred. Reload 🗙