Code plans
A code plan is an async JavaScript function body. It composes registered tools and returns data or optional UI.
Plan code
Section titled “Plan code”const { items } = await tools.menu.items.list({
menuId: input.menuId,
});
return items.filter((item) => item.price < input.maximumPrice);
The selected executor receives this runtime surface:
| Value | Purpose |
|---|---|
input |
Task data supplied by the application. |
tools |
Registered application capabilities. |
console |
Diagnostic logging through runtime events. |
html |
Tagged templates when generated UI is enabled. |
The built-in browser executors provide standard JavaScript values such as Promise for asynchronous calls. A plan does not receive imports, fetch, window, document, storage, or application services.
Use ordinary JavaScript for runtime decisions. Independent calls can use Promise.all(); dependent calls should be awaited in order. Executor-specific limits and trust boundaries are documented in Code-plan executors.
Plan input
Section titled “Plan input”Input is supplied separately from code:
await gruntend.runCodePlan(code, {
input: { menuId: "menu_1", maximumPrice: 10 },
handlers,
});
The plan reads those values from input. Keeping values out of generated source makes a plan easier to inspect and reuse.
Where types apply
Section titled “Where types apply”Gruntend is typed at the application boundary, but the generated plan is JavaScript, not TypeScript.
defineTools()preserves tool names and infers handler input and output types from the schemas.ToolHandlerMap<typeof tools>type-checks handler keys and return values in application code.- The model sees the optional
parametersandreturnsdescriptions in the tool manifest. - At runtime, Gruntend validates every plan-supplied tool input and every successful handler output.
The plan itself is not trusted to satisfy TypeScript types. Runtime schemas enforce the contract when interpreted JavaScript crosses into a handler.
Generated plan format
Section titled “Generated plan format”A model-generated plan uses a small JSON envelope:
{
"summary": "List inexpensive menu items",
"input": {
"menuId": "menu_1",
"maximumPrice": 10
},
"code": "const { items } = await tools.menu.items.list({ menuId: input.menuId }); return items.filter((item) => item.price < input.maximumPrice);"
}
| Field | Contract |
|---|---|
summary |
Non-empty human-readable description. |
input |
Becomes the runtime input value. |
code |
JavaScript async function body, without a wrapper or IIFE. |
Generate with Gruntend
Section titled “Generate with Gruntend”import { generateCodePlan, getModel } from "gruntend-sdk/generate";
const { plan } = await generateCodePlan({
model: getModel("openai", "model-id"),
tools,
task,
input,
});
Generation returns the envelope. It does not execute it.
Pass an application-owned prompt
Section titled “Pass an application-owned prompt”Build on the default or replace it completely, then pass the prompt as a first-class value:
import { createCodePlanPrompt, generateCodePlan } from "gruntend-sdk/generate";
const defaults = createCodePlanPrompt({ tools, task, input });
const prompt = {
system: `${defaults.system}\n\n${applicationPlanningPolicy}`,
user: defaults.user,
};
const { plan } = await generateCodePlan({
model,
tools,
task,
input,
prompt,
});
When prompt is supplied, Gruntend sends its exact system and user text. Model choice, domain instructions, examples, and generation quality remain application-owned.
Bring your own model
Section titled “Bring your own model”import {
createCodePlanPrompt,
parseGeneratedCodePlan,
} from "gruntend-sdk/generate";
const prompt = createCodePlanPrompt({ tools, task, input });
const text = await myModel.complete({
system: prompt.system,
user: prompt.user,
});
const plan = parseGeneratedCodePlan(text);
myModel.complete(...) is application code; Gruntend only requires the returned text. Invalid generated JSON is rejected rather than silently repaired.
Tagged HTML plans
Section titled “Tagged HTML plans”Pass ui: { kind: "tagged-html" } while building or generating a plan that should return UI. The selected executor must also receive ui: { html } when that plan runs.
See Generated UI.
Choose an executor
Section titled “Choose an executor”Plan format and execution strategy are separate. The application explicitly selects JailJS, QuickJS/WASM, or a custom browser executor before the complete plan starts.
See Code-plan executors for setup, trust profiles, limits, per-run overrides, generated UI ownership, and no-replay behavior.