@engenty/wizards-runtime 0.7.5 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/apps/runtime/dist/authoring/guide.js +5 -1
  2. package/apps/runtime/dist/cli/environment.js +2 -0
  3. package/apps/runtime/dist/cli/main.js +10 -1
  4. package/apps/runtime/dist/cli/sandbox.js +77 -0
  5. package/apps/runtime/dist/cli/status.js +9 -0
  6. package/apps/runtime/dist/debug.js +2 -1
  7. package/apps/runtime/dist/docs/dev/index.md +6 -1
  8. package/apps/runtime/dist/docs/dev/integration/channels.md +11 -0
  9. package/apps/runtime/dist/docs/dev/integration/claude-code-plugin.md +10 -0
  10. package/apps/runtime/dist/docs/dev/integration/embed.md +10 -0
  11. package/apps/runtime/dist/docs/dev/integration/index.md +36 -0
  12. package/apps/runtime/dist/docs/dev/integration/link-and-share.md +10 -0
  13. package/apps/runtime/dist/docs/dev/integration/mcp-apps.md +10 -0
  14. package/apps/runtime/dist/docs/dev/integration/mcp.md +10 -0
  15. package/apps/runtime/dist/docs/dev/integration/meta.json +20 -0
  16. package/apps/runtime/dist/docs/dev/integration/openui.md +181 -0
  17. package/apps/runtime/dist/docs/dev/integration/own-ui.md +10 -0
  18. package/apps/runtime/dist/docs/dev/integration/results.md +11 -0
  19. package/apps/runtime/dist/docs/dev/integration/wordpress.md +10 -0
  20. package/apps/runtime/dist/docs/dev/meta.json +1 -1
  21. package/apps/runtime/dist/docs/user/command-line.md +3 -0
  22. package/apps/runtime/dist/docs/user/server.md +1 -1
  23. package/apps/runtime/dist/engine/steps.js +2 -1
  24. package/apps/runtime/dist/env.js +10 -26
  25. package/apps/runtime/dist/routes/runs.js +14 -1
  26. package/apps/runtime/dist/sandbox/agentos-package.js +73 -0
  27. package/apps/runtime/dist/sandbox/agentos.js +162 -0
  28. package/apps/runtime/dist/sandbox/docker.js +32 -62
  29. package/apps/runtime/dist/sandbox/index.js +37 -8
  30. package/apps/runtime/dist/sandbox/install.js +122 -0
  31. package/apps/runtime/dist/sandbox/mastra.js +71 -0
  32. package/apps/runtime/dist/services/spaces.js +2 -1
  33. package/apps/runtime/dist/tools/code.js +133 -0
  34. package/apps/runtime/dist/tools/connector.js +6 -0
  35. package/apps/runtime/dist/tools/index.js +14 -3
  36. package/apps/web/dist/assets/{AboutDialog-D4mvc-zu.js → AboutDialog-BP8-Ugxf.js} +1 -1
  37. package/apps/web/dist/assets/{ChangelogDialog-DLqpJsF6.js → ChangelogDialog-CD6CQNW9.js} +1 -1
  38. package/apps/web/dist/assets/{ChatView-3laypRcE.js → ChatView-4T0QarEr.js} +1 -1
  39. package/apps/web/dist/assets/{EditorPage-BalA5U2Y.js → EditorPage-CWdV7Xdn.js} +3 -3
  40. package/apps/web/dist/assets/{FlowPreview-CusP7ci9.js → FlowPreview-Dj2gAfuf.js} +1 -1
  41. package/apps/web/dist/assets/{Harness-iYLtOLaK.js → Harness-SLbsntQF.js} +1 -1
  42. package/apps/web/dist/assets/{NewWizardChannels-CbwJ9A56.js → NewWizardChannels-CsDMlji7.js} +1 -1
  43. package/apps/web/dist/assets/{NewWizardPage-BOBYiB9i.js → NewWizardPage-_bFYjlgh.js} +1 -1
  44. package/apps/web/dist/assets/{OnboardingPage-CUwCiVIr.js → OnboardingPage-DObQeCDg.js} +1 -1
  45. package/apps/web/dist/assets/{ProjectPage-Bb16MdYG.js → ProjectPage-DX-oNLWy.js} +1 -1
  46. package/apps/web/dist/assets/{PublicRunner-BCt7Q6C-.js → PublicRunner-Bn9rVNEb.js} +1 -1
  47. package/apps/web/dist/assets/{ReadOnly-kIHX2TEy.js → ReadOnly-CK0piLUV.js} +1 -1
  48. package/apps/web/dist/assets/{RunDrawer-hZ9oeSXu.js → RunDrawer-CVMTLse_.js} +1 -1
  49. package/apps/web/dist/assets/{RunnerView-DUS9fPmx.js → RunnerView-Cjhuu9ua.js} +1 -1
  50. package/apps/web/dist/assets/{SettingsPage-BzwR50fi.js → SettingsPage-DTzuiSAj.js} +1 -1
  51. package/apps/web/dist/assets/{SetupPage-gAp7rPxp.js → SetupPage-D9jlhgiP.js} +1 -1
  52. package/apps/web/dist/assets/{SharePage-DqZWHva_.js → SharePage-Cc3Opj2F.js} +1 -1
  53. package/apps/web/dist/assets/changelog-CJNkd1_L.js +1 -0
  54. package/apps/web/dist/assets/{esm-DXeMu8Lb.js → esm-CfnFE_jE.js} +1 -1
  55. package/apps/web/dist/assets/{index-C5Sm7tTR.js → index-B5F1_qA_.js} +4 -4
  56. package/apps/web/dist/assets/{lock-R_CfU0Xo.js → lock-DAvRJY4B.js} +1 -1
  57. package/apps/web/dist/assets/oss-credits-X2JPiMOR.js +1 -0
  58. package/apps/web/dist/assets/{phone-CgrIS4_-.js → phone-BWOB67tX.js} +1 -1
  59. package/apps/web/dist/assets/{settings-menu-2IMOOlLl.js → settings-menu-CEQlVs1O.js} +1 -1
  60. package/apps/web/dist/assets/{ui-dzGvc3xk.js → ui-BmR_4eL2.js} +1 -1
  61. package/apps/web/dist/assets/{upload-BSuTilFF.js → upload-pBKn2EFp.js} +1 -1
  62. package/apps/web/dist/index.html +2 -2
  63. package/apps/web/dist/mcp/flow.html +2 -2
  64. package/node_modules/@engenty/wizards/dist/openui/Wizard.d.ts +20 -0
  65. package/node_modules/@engenty/wizards/dist/openui/Wizard.js +62 -0
  66. package/node_modules/@engenty/wizards/dist/openui/index.d.ts +9 -0
  67. package/node_modules/@engenty/wizards/dist/openui/index.js +9 -0
  68. package/node_modules/@engenty/wizards/dist/openui/message.d.ts +11 -0
  69. package/node_modules/@engenty/wizards/dist/openui/message.js +69 -0
  70. package/node_modules/@engenty/wizards/dist/openui/run.d.ts +8 -0
  71. package/node_modules/@engenty/wizards/dist/openui/run.js +137 -0
  72. package/node_modules/@engenty/wizards/dist/openui/server.d.ts +37 -0
  73. package/node_modules/@engenty/wizards/dist/openui/server.js +77 -0
  74. package/node_modules/@engenty/wizards/package.json +4 -1
  75. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +4 -1
  76. package/node_modules/@engenty-wizards/shared/dist/definition.js +1 -0
  77. package/node_modules/@engenty-wizards/shared/dist/fit.js +7 -2
  78. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +14 -0
  79. package/node_modules/@engenty-wizards/shared/dist/run.js +2 -0
  80. package/package.json +4 -3
  81. package/apps/web/dist/assets/changelog-DugmCNSf.js +0 -1
  82. package/apps/web/dist/assets/oss-credits-cZ9NUaLZ.js +0 -1
@@ -133,7 +133,7 @@ type AgentStep = {
133
133
  // (fields name the keys later steps read). Use it where a model would only fetch and copy
134
134
  // data (free appointment times) — it answers in a second instead of half a minute.
135
135
  // String inputs are templates; a filled-in whole number or true/false is passed as one.
136
- tools: ("web_search"|"web_fetch"|"browser"|"sandbox"|"image"|"http"|"pages")[]
136
+ tools: ("web_search"|"web_fetch"|"browser"|"sandbox"|"image"|"http"|"pages"|"code")[]
137
137
  mcp?: string[] // ids of the project's MCP servers this step may use
138
138
  connections?: string[] // ids of wizard connections this step may read (mail → mail_search, mail_read, mail_save)
139
139
  output: { format: "text"|"markdown"|"json", fields?: { id, kind: "text"|"number"|"list"|"table"|"yesno"|"choice"|"score", description, columns?: string[], options?: string[] }[], schema?: Schema, surface?: SurfaceComponent[] | true }
@@ -156,6 +156,10 @@ type AgentStep = {
156
156
  // project_list (by categories), page_read / table_read / project_document (an item whole, by its path).
157
157
  // "pages" brings: page_read / page_write — Markdown pages of the space the wizard keeps for every run (notes, a log
158
158
  // that grows with append), shown to the admin under Space → Data; page_read also reads Knowledge by path.
159
+ // "code" brings: execute_typescript — the model writes one program that calls the step's other tools (not the browser) as
160
+ // functions: loops, Promise.all, filtering, sums; only its answer reaches the model. For many calls of one kind (fetch 30
161
+ // pages, read every row of a list, search a mailbox month by month). It needs other tools to call; a change the person
162
+ // must allow first is never made from a program.
159
163
  // "browser" brings: browser_open/read/click/type/select/scroll/press/back/wait, browser_screenshot (the model looks at the page),
160
164
  // browser_run_fast (a decision model clicks through a page's mechanical part, where one is reached),
161
165
  // browser_request_credentials (the PERSON types a login into the page — the model never sees it; sign-ins can be remembered),
@@ -36,6 +36,8 @@ const INHERITED = new Set([
36
36
  "SANDBOX",
37
37
  "SANDBOX_ENABLED",
38
38
  "SANDBOX_IMAGE",
39
+ "SANDBOX_ALLOW_HOSTS",
40
+ "SANDBOX_ENGINES_DIR",
39
41
  "PLUGINS_DIR",
40
42
  "PLUGINS_WATCH",
41
43
  "ACCOUNT_URL",
@@ -8,6 +8,7 @@ import { CLIENTS, nodeIsCurrent } from "./machine.js";
8
8
  import { mcp } from "./mcp.js";
9
9
  import { menu } from "./menu.js";
10
10
  import { logsCommand, reportCommand } from "./report.js";
11
+ import { sandboxCommand } from "./sandbox.js";
11
12
  import { setup } from "./setup.js";
12
13
  import { open, start, studio } from "./start.js";
13
14
  import { autostart, restart, status, stop } from "./status.js";
@@ -40,6 +41,8 @@ ${line(`${command()} restart`, "restart it, e.g. to switch to a newly installed
40
41
  ${line(`${command()} update`, "install the latest version")}
41
42
  ${line(`${command()} autoupdate`, "updates by themselves: add on or off (WIZARDS_DISABLE_AUTOUPDATE=1 is off too)")}
42
43
  ${more("(downloaded in the background, used from the next start; never during a run)")}
44
+ ${line(`${command()} sandbox`, "the sandbox where AI steps run code, without Docker: add or remove")}
45
+ ${more("(agentOS unless you name another engine, about 340 MB; used from the next start)")}
43
46
  ${line(`${command()} autostart`, "start it when you log in to this computer: add on or off")}
44
47
  ${more("(macOS: a LaunchAgent; Linux: a systemd user service)")}
45
48
  ${line(`${command()} connect`, "add your wizards to your AI apps: every app found, or the ones you name:")}
@@ -70,6 +73,7 @@ function parse(argv) {
70
73
  const args = {
71
74
  command: "",
72
75
  value: "",
76
+ engine: "",
73
77
  apps: [],
74
78
  paths: [],
75
79
  json: false,
@@ -116,10 +120,13 @@ function parse(argv) {
116
120
  else if (arg.startsWith("-")) {
117
121
  return `Unknown option ${arg}.`;
118
122
  }
119
- else if (["autostart", "autoupdate", "logs", "account"].includes(args.command) &&
123
+ else if (["autostart", "autoupdate", "logs", "account", "sandbox"].includes(args.command) &&
120
124
  !args.value) {
121
125
  args.value = arg;
122
126
  }
127
+ else if (args.command === "sandbox" && !args.engine) {
128
+ args.engine = arg;
129
+ }
123
130
  else if (["connect", "disconnect", "features"].includes(args.command)) {
124
131
  args.apps.push(arg);
125
132
  }
@@ -199,6 +206,8 @@ async function main(argv) {
199
206
  return autoUpdateCommand(paths, args.value);
200
207
  case "account":
201
208
  return accountCommand(paths, args.value, { yes: args.yes });
209
+ case "sandbox":
210
+ return sandboxCommand(paths, args.value, args.engine);
202
211
  case "connect":
203
212
  case "disconnect":
204
213
  return connectCommand(paths, args.apps, args.command === "disconnect");
@@ -0,0 +1,77 @@
1
+ import { delimiter, dirname, join } from "node:path";
2
+ import { ENGINE_PACKAGES, engineList, installEngine, installedVersion, removeEngine, } from "../sandbox/install.js";
3
+ import { runtimePath } from "./environment.js";
4
+ import { runLogged } from "./exec.js";
5
+ import { findOnPath } from "./machine.js";
6
+ import { busy, cmd, no, ok, tilde } from "./ui.js";
7
+ /** What `sandbox add` installs when no engine is named. */
8
+ const DEFAULT_ENGINE = "agentos";
9
+ /** Where an engine goes: `SANDBOX_ENGINES_DIR/<name>`, else `engines/<name>` in the install's folder. */
10
+ export const engineDirOf = (paths, name) => join(process.env.SANDBOX_ENGINES_DIR?.trim() || join(paths.home, "engines"), name);
11
+ /** The engines installed here, with their versions and the versions this release installs. */
12
+ export function installedEngines(paths) {
13
+ return Object.entries(ENGINE_PACKAGES).flatMap(([name, engine]) => {
14
+ const version = installedVersion(engineDirOf(paths, name));
15
+ return version ? [{ name, label: engine.label, version, current: engine.version }] : [];
16
+ });
17
+ }
18
+ /**
19
+ * `wizards sandbox [add|remove] [engine]`: the sandbox where AI steps run code, without Docker.
20
+ * The engines are not part of engenty wizards; `add` downloads one beside the install.
21
+ */
22
+ export async function sandboxCommand(paths, value, engine = "") {
23
+ const name = engine.toLowerCase() || DEFAULT_ENGINE;
24
+ if ((value === "add" || value === "remove") && !ENGINE_PACKAGES[name]) {
25
+ console.error(`${no} There is no engine “${name}”. ${engineList()}`);
26
+ return 2;
27
+ }
28
+ if (value === "add") {
29
+ return add(paths, name);
30
+ }
31
+ if (value === "remove") {
32
+ removeEngine(engineDirOf(paths, name));
33
+ console.log(`${ok} The sandbox ${ENGINE_PACKAGES[name].label} is removed. It goes away with the next ${cmd("restart")}.`);
34
+ return 0;
35
+ }
36
+ if (value) {
37
+ console.error(`Use ${cmd("sandbox add")} or ${cmd("sandbox remove")}.`);
38
+ return 2;
39
+ }
40
+ const installed = installedEngines(paths);
41
+ if (!installed.length) {
42
+ console.log(`${no} No sandbox is installed. AI steps can’t run code unless Docker has the engenty-sandbox image. To add one: ${cmd("sandbox add")}`);
43
+ return 0;
44
+ }
45
+ for (const { name, label, version } of installed) {
46
+ console.log(`${ok} The sandbox ${label} ${version} is in ${tilde(engineDirOf(paths, name))}.`);
47
+ }
48
+ console.log(`AI steps run code in ${installed.length === 1 ? "it" : "the first one"}, unless Docker has the engenty-sandbox image or SANDBOX names an engine.`);
49
+ return 0;
50
+ }
51
+ async function add(paths, name) {
52
+ const engine = ENGINE_PACKAGES[name];
53
+ const unsupported = engine.unsupported?.();
54
+ if (unsupported) {
55
+ console.error(`${no} ${unsupported}`);
56
+ return 1;
57
+ }
58
+ const npm = findOnPath("npm", [dirname(process.execPath), process.env.PATH ?? ""].join(delimiter));
59
+ if (!npm) {
60
+ console.error(`${no} npm wasn’t found next to this Node.js.`);
61
+ return 1;
62
+ }
63
+ const env = { ...process.env, PATH: runtimePath(paths, process.env) };
64
+ const logFile = join(paths.logs, "install.log");
65
+ const dir = engineDirOf(paths, name);
66
+ const spinner = busy(`Installing the sandbox (${engine.label} ${engine.version}, ${engine.size})…`);
67
+ try {
68
+ await installEngine(name, dir, (args) => runLogged(npm, args, { env, logFile }));
69
+ }
70
+ catch (err) {
71
+ spinner.fail(`The sandbox couldn’t be installed: ${err.message}`);
72
+ return 1;
73
+ }
74
+ spinner.stop(`${ok} The sandbox is installed in ${tilde(dir)}.`);
75
+ console.log(`AI steps run code in it from the next start: ${cmd("restart")}`);
76
+ return 0;
77
+ }
@@ -6,6 +6,7 @@ import { autoUpdatePolicy, updateNotice } from "./autoupdate.js";
6
6
  import { installedByScript, isCheckout, packageRoot, packageVersion } from "./home.js";
7
7
  import { detectClients, findChrome, findFfmpeg, nodeIsCurrent } from "./machine.js";
8
8
  import { findWizardsProcesses, stopProcesses } from "./processes.js";
9
+ import { installedEngines } from "./sandbox.js";
9
10
  import { entryUrl, runningRuntime, settings, startInBackground } from "./start.js";
10
11
  import { bad, badge, cmd, cyan, dim, no, ok, tilde } from "./ui.js";
11
12
  /**
@@ -45,6 +46,14 @@ export async function status(paths, checks) {
45
46
  }
46
47
  row("Browser", chrome ? `${ok} ${dim(tilde(chrome))}` : `${no} not found (needed for PDF and PNG)`);
47
48
  row("ffmpeg", ffmpeg.path ? `${ok} ${ffmpeg.version ?? ""}` : `${no} not found (needed for MP4)`);
49
+ const engines = installedEngines(paths);
50
+ row("Sandbox", engines.length
51
+ ? engines
52
+ .map(({ label, version, current }) => version === current
53
+ ? `${ok} ${label} ${version}`
54
+ : `${no} ${label} ${version} is outdated (${cmd("sandbox add")} installs ${current})`)
55
+ .join(", ")
56
+ : `${no} not installed (${cmd("sandbox add")} lets AI steps run code)`);
48
57
  const { updated, pending } = updateNotice(paths, packageVersion());
49
58
  if (updated) {
50
59
  console.log(`\n ${ok} Updated to ${updated.to}. What changed: ${cyan(updated.notes)}`);
@@ -8,6 +8,7 @@ import { logState, startedAt, tail } from "./log.js";
8
8
  import { managed } from "./manage.js";
9
9
  import { modelSettings } from "./models.js";
10
10
  import { loadedPlugins } from "./plugins/registry.js";
11
+ import { sandboxEngine } from "./sandbox/index.js";
11
12
  import { runIndexOf, tenantOfRun } from "./tenants/control.js";
12
13
  import { LOCAL_TENANT } from "./tenants/tenant.js";
13
14
  /**
@@ -41,7 +42,7 @@ export async function status() {
41
42
  appUrl: env.appUrl,
42
43
  manageUrl: env.manage.url || null,
43
44
  gatewayUrl: (managed ? env.manage.gatewayUrl : env.local.gatewayUrl) || null,
44
- sandbox: env.sandbox,
45
+ sandbox: sandboxEngine()?.name ?? "off",
45
46
  browser: env.browserCdpUrl ? "cdp" : "local",
46
47
  files: env.s3.bucket ? "s3" : "data folder",
47
48
  database: env.databaseUrl ? "libsql" : "file",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: Developer
3
3
  description: Extend engenty wizards with plugins – tools for AI steps, routes with their own tables, pages in the studio.
4
- version: 0.5.0
4
+ version: 0.7.3
5
5
  ---
6
6
 
7
7
  You extend engenty wizards with **plugins**. A plugin adds to a runtime without changing its
@@ -21,6 +21,11 @@ code, and the runtime loads it from its files while it runs.
21
21
  A wizard is JSON, and it travels as a `.wizard` file. [The wizard format](./wizard-format.md)
22
22
  describes the format, the JSON Schema for your editor and `wizards validate`.
23
23
 
24
+ ## Wizards in other systems
25
+
26
+ [Integration](./integration/index.md) shows how a published wizard reaches other systems: people
27
+ over a link, a website or a channel, AI apps over MCP, and your own code.
28
+
24
29
  ## What you need
25
30
 
26
31
  - engenty wizards on your computer, or a checkout of
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Channels
3
+ description: Run a wizard as a chat, a conversation or a call, and over WhatsApp, SMS or the phone.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [Channels](../../user/test-and-share.md#channels) in the user guide describes how a
10
+ wizard runs as a chat, a conversation or a call, and over WhatsApp, SMS or the phone. A plugin
11
+ adds a channel of its own with a [runner](../plugins/runners.md).
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Claude Code plugin
3
+ description: Build and use wizards from Claude Code with the /wizard command and its skills.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [The Claude Code plugin](../../user/ai-apps.md#the-claude-code-plugin) in the user
10
+ guide shows how you install it.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Embed
3
+ description: Put a published wizard on your website with one script tag.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [Embed in a website](../../user/test-and-share.md#embed-in-a-website) in the user
10
+ guide shows the script tag and its options.
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Integration
3
+ description: Bring a published wizard into other systems – for people, for AI apps and for your own code.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ A published wizard runs wherever a person can reach it. These pages show how to bring it into
8
+ other systems, from a plain link to a chat that starts the wizard by itself.
9
+
10
+ ## For people
11
+
12
+ | | |
13
+ |---|---|
14
+ | [Link and QR code](./link-and-share.md) | The wizard’s own page, as a link or a QR code |
15
+ | [Embed](./embed.md) | The wizard on your website, inline, as a button or a chat |
16
+ | [WordPress](./wordpress.md) | The plugin “engenty wizards” places wizards and reads their results |
17
+ | [Channels](./channels.md) | A chat, a conversation or a call, and WhatsApp, SMS or the phone |
18
+
19
+ ## For AI apps
20
+
21
+ | | |
22
+ |---|---|
23
+ | [MCP](./mcp.md) | AI apps list, run and build wizards over the Model Context Protocol |
24
+ | [MCP Apps](./mcp-apps.md) | AI apps that support MCP Apps show the wizard itself, page by page |
25
+ | [Claude Code plugin](./claude-code-plugin.md) | The `/wizard` command and the skills to build and use wizards |
26
+ | [OpenUI](./openui.md) | A chat built with OpenUI runs the wizard inside the conversation |
27
+
28
+ ## For your code
29
+
30
+ | | |
31
+ |---|---|
32
+ | [Your own UI](./own-ui.md) | Your app draws the run with `@engenty-wizards/runner` |
33
+ | [Results to your systems](./results.md) | What a run produces goes on to your own systems |
34
+
35
+ Each way starts from a published wizard. [Test and share](../../user/test-and-share.md) in the
36
+ user guide explains how you publish one.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Link and QR code
3
+ description: Hand out a published wizard as a link or a QR code.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [Share](../../user/test-and-share.md#share) in the user guide describes the link, the
10
+ QR code and who can open them.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: MCP Apps
3
+ description: AI apps that support MCP Apps show the wizard itself as a widget, page by page.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [What an app can do](../../user/ai-apps.md#what-an-app-can-do) in the user guide lists
10
+ what an app with MCP Apps shows.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: MCP
3
+ description: AI apps list, run and build wizards over the Model Context Protocol.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [AI apps](../../user/ai-apps.md) in the user guide shows how you add engenty wizards
10
+ to Claude Desktop, Cursor, Codex and other AI apps.
@@ -0,0 +1,20 @@
1
+ {
2
+ "title": "Integration",
3
+ "defaultOpen": true,
4
+ "pages": [
5
+ "index",
6
+ "---For people---",
7
+ "link-and-share",
8
+ "embed",
9
+ "wordpress",
10
+ "channels",
11
+ "---For AI apps---",
12
+ "mcp",
13
+ "mcp-apps",
14
+ "claude-code-plugin",
15
+ "openui",
16
+ "---For your code---",
17
+ "own-ui",
18
+ "results"
19
+ ]
20
+ }
@@ -0,0 +1,181 @@
1
+ ---
2
+ title: Wizards in OpenUI chats
3
+ description: A Wizard component for OpenUI’s React renderer. The chat’s model decides that a wizard runs, and the wizard runs its own pages inside the conversation.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ A chat built with [OpenUI](https://github.com/thesysdev/openui) composes its answers as it
8
+ goes: cards, charts, forms and buttons, whatever the model finds fitting. A wizard is the one
9
+ thing in such a chat that runs the same way every time, with the pages, steps and result its
10
+ author defined. `@engenty/wizards/openui` puts the two together: the chat decides that a wizard
11
+ fits and what it already knows, the wizard then runs inside the conversation, and the chat
12
+ hears the result.
13
+
14
+ It is part of `@engenty/wizards`, the package for code outside engenty wizards
15
+ ([`packages/wizards/src/openui`](https://github.com/engenty/wizards/tree/main/packages/wizards/src/openui)
16
+ in the repository, under the MIT licence). A whole chat to try it with is `examples/openui-chat`.
17
+
18
+ ## What OpenUI is
19
+
20
+ OpenUI (Thesys, MIT) is a way to let a model draw. Three parts matter here:
21
+
22
+ - **A language the model writes.** An answer is a short program: `root = Card([…])` with
23
+ components as calls, such as `TextContent("…")` or `Form(…)`. The model streams it like text.
24
+ - **A React renderer** (`@openuidev/react-lang`) that parses the program as it streams and draws
25
+ it with a **component library**: React components with a name, a description and typed props.
26
+ - **A chat kit** (`@openuidev/react-ui`): the library of chat components, the thread, the
27
+ composer and an `AgentInterface` that puts them together.
28
+
29
+ The model learns the language from the system prompt the library makes. The chat library’s
30
+ prompt is about 14,000 tokens: every component with its props and description, the rules, and
31
+ 5 worked examples of whole answers. The model imitates the examples. Nothing in that prompt is
32
+ a fixed flow: every answer, and every form in it, is composed anew.
33
+
34
+ A wizard is the only fixed definition in such a chat. The model writes one line, and from then
35
+ on the wizard’s pages, AI steps, review and result run as its author defined them.
36
+
37
+ ## How the Wizard component works
38
+
39
+ ```mermaid
40
+ sequenceDiagram
41
+ participant S as Chat server
42
+ participant M as Model
43
+ participant B as Browser (OpenUI renderer)
44
+ participant R as Runtime
45
+ S->>R: GET /api/public/wizards/:token
46
+ R-->>S: title, description, fields
47
+ S->>M: system prompt + wizard rules
48
+ M-->>B: root = Card([TextContent("…"), Wizard("<token>", "<note>", {plate: "W-12345"})])
49
+ B->>R: POST /api/public/wizards/:token/runs
50
+ R-->>B: run id
51
+ loop every step
52
+ R-->>B: the run’s view (stream)
53
+ B->>R: a page’s answers, a review, back
54
+ end
55
+ B->>S: continue_conversation: “The wizard finished.” + result
56
+ S->>M: the thread
57
+ M-->>B: an answer in plain words
58
+ ```
59
+
60
+ It has two halves, one for each side of the chat.
61
+
62
+ ### The server half
63
+
64
+ `@engenty/wizards/openui/server` draws nothing, so a server can import it alone.
65
+
66
+ ```ts
67
+ import { addWizard, loadWizards, wizardRules } from "@engenty/wizards/openui/server";
68
+ import { openuiChatLibrary, openuiChatPromptOptions } from "@openuidev/react-ui/genui-lib";
69
+
70
+ const wizards = await loadWizards("http://localhost:24368", ["<token>", "<token>"]);
71
+ const prompt = addWizard(openuiChatLibrary).prompt({
72
+ ...openuiChatPromptOptions,
73
+ additionalRules: [...(openuiChatPromptOptions.additionalRules ?? []), ...wizardRules(wizards)],
74
+ });
75
+ ```
76
+
77
+ - `loadWizards(runtimeUrl, tokens)` reads each published wizard from the runtime
78
+ (`GET /api/public/wizards/:token`): its title, description and the fields its pages ask, in
79
+ order. A wizard the runtime does not answer for is left out, so the chat keeps working when
80
+ one is unpublished.
81
+ - `addWizard(library)` extends an OpenUI library with the Wizard component, among a Card’s
82
+ children. Its prompt then has `Wizard(wizard: string, note?: string, prefill?: …)`.
83
+ - `wizardRules(wizards)` are the rules that go with it: when to answer with a wizard, that the
84
+ wizard asks its own questions (never a Form in its place), what may be prefilled, and the list
85
+ of wizards with their tokens and fields.
86
+
87
+ With that, the model answers a fitting request with a Card that holds one short text and
88
+ `Wizard("<token>", "<note>", {prefill})`. The prefill holds answers the person already gave in
89
+ the conversation, by field id; the person still sees and sends each page.
90
+
91
+ ### The component, in the browser
92
+
93
+ ```tsx
94
+ import "@engenty/wizards/openui/styles.css";
95
+ import { addWizard, configureWizards, Wizard } from "@engenty/wizards/openui";
96
+ import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
97
+
98
+ configureWizards({ runtime: "/wizards-runtime" });
99
+ const library = addWizard(openuiChatLibrary, Wizard);
100
+ ```
101
+
102
+ - `configureWizards({ runtime })` names the runtime once, as the browser reaches it: a path the
103
+ host proxies to it, or the runtime’s address. A proxy on the host’s own address keeps the
104
+ runner’s visitor cookie first-party. `lang` sets the language of the runner’s own words
105
+ (`en` unless given), `Mascot` draws the wizard’s mascot above its pages.
106
+ - `addWizard(library, Wizard)` is the same extension as on the server, with the drawing
107
+ component. Both make the same prompt.
108
+
109
+ The component draws the run with the runner, the same code as the wizard’s own page
110
+ (`packages/runner`), in styles scoped to `.engenty-runner`, so the chat’s styles and the
111
+ runner’s never meet. Every field kind works as on the wizard’s own page, the camera and the
112
+ signature included. The page is not a frame, so nothing else is needed for them.
113
+
114
+ What happens in the message:
115
+
116
+ 1. **The run starts** once the model’s line has finished streaming, so a half-written token never
117
+ starts a run: `POST /api/public/wizards/:token/runs`, as a visitor of the runtime.
118
+ 2. **The run id stays with the message**, in the state OpenUI keeps for it. A message shown again
119
+ continues the same run.
120
+ 3. **The run shows live** over the runtime’s stream of the run’s view (`/api/runs/:id/stream`), and
121
+ the person’s answers go to the run’s routes (`/api/runs/:id/pages/:step`, `…/reviews/:step`,
122
+ `…/back`, `…/retry`). These are the routes the wizard’s own page uses.
123
+ 4. **The result goes back to the chat.** When the component sees the run finish, it sends OpenUI’s
124
+ `continue_conversation` action: a turn of the person that says the wizard finished, with the
125
+ result as context for the model: the wizard’s title, the run id and the texts of the result
126
+ steps, each cut at 4,000 characters. The model answers in plain words.
127
+
128
+ The chat hears of a run it saw finish. A message shown again with a done run shows the result
129
+ and tells the chat nothing new.
130
+
131
+ ### Who runs, who pays
132
+
133
+ The browser talks to the runtime as a visitor, as on the wizard’s own page: the runtime sets a
134
+ visitor cookie, lets a visitor start 6 runs an hour
135
+ ([`LIMIT_VISITOR_RUNS_PER_HOUR`](../../user/command-line.md#settings)) and spends the wizard’s own
136
+ credits or model keys. The chat’s model and the wizards’ model are two different things: the
137
+ chat picks the model that writes its answers, and each wizard’s steps run on what its space
138
+ has.
139
+
140
+ ## Try it
141
+
142
+ `examples/openui-chat` is an OpenUI chat (`AgentInterface`) with any OpenAI-compatible model,
143
+ the wizards it may start and a proxy to the runtime. In short:
144
+
145
+ 1. Start the runtime from a checkout (`pnpm dev`) and publish a wizard or two in the studio.
146
+ Each one’s token is the part of its link after `/w/`.
147
+ 2. Put an `.env.local` in `examples/openui-chat`: `WIZARDS=<token>,<token>`, `CHAT_BASE_URL`,
148
+ `CHAT_API_KEY` and `CHAT_MODEL` for the chat’s model.
149
+ 3. `pnpm --filter @engenty-wizards/example-openui-chat dev` and open `http://localhost:5190`.
150
+ 4. Ask for something a wizard does, in your own words.
151
+
152
+ The example’s README has the details, the endpoints that work and what to set while you try
153
+ things.
154
+
155
+ ## Exports
156
+
157
+ | From | Export | What it is |
158
+ |---|---|---|
159
+ | `@engenty/wizards/openui/server` | `loadWizards(runtimeUrl, tokens)` | The published wizards behind the tokens, as `WizardInfo[]` |
160
+ | | `wizardRules(wizards)` | The rules a system prompt adds |
161
+ | | `addWizard(library, component?)` | The library with the Wizard component among a Card’s children |
162
+ | | `WizardSpec`, `wizardProps`, `WizardProps` | The component as a prompt sees it, and its props |
163
+ | | `WIZARD_COMPONENT`, `WIZARD_DESCRIPTION` | Its name and description |
164
+ | `@engenty/wizards/openui` | all of the above | |
165
+ | | `configureWizards(config)` | Names the runtime, the language and the mascot |
166
+ | | `Wizard` | The drawing component |
167
+ | | `useWizardRun(runId, prefill)` | One run, live, for a host that draws runs itself |
168
+ | `@engenty/wizards/openui/styles.css` | | The runner’s styles, scoped to the wizard |
169
+
170
+ `@openuidev/react-lang`, `react`, `react-dom` and `zod` are peer dependencies: the chat brings
171
+ them. The package is written against `@openuidev/react-lang` 1.0 and the chat kit
172
+ `@openuidev/react-ui` 0.18.
173
+
174
+ ## Good to know
175
+
176
+ - **One runtime per page.** `configureWizards` names it once, before the first render.
177
+ - **Other hosts.** The component sits on the runtime’s public run routes, not on OpenUI’s
178
+ internals. A host with its own components can draw a run with `useWizardRun` and the runner’s
179
+ parts, and another UI format can follow the same way.
180
+ - **OpenUI’s Inspect button** at the bottom right of a page in development is OpenUI’s own
181
+ (`@openuidev/react-lang` adds it and loads it from jsDelivr). A production build leaves it out.
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: Your own UI
3
+ description: Draw a wizard’s run in your own app with @engenty-wizards/runner and the runtime’s public run routes.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [OpenUI](./openui.md) shows one app that does it: it draws the run with
10
+ `@engenty-wizards/runner` (`packages/runner`) over the runtime’s public run routes.
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: Results to your systems
3
+ description: Send what a run produces to your own systems.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [Results, contacts, appointments](../../user/wordpress.md#results-contacts-appointments)
10
+ shows how the WordPress plugin receives results. A plugin can also answer other services with
11
+ its own [routes](../plugins/routes.md).
@@ -0,0 +1,10 @@
1
+ ---
2
+ title: WordPress
3
+ description: Put published wizards on a WordPress site with the plugin “engenty wizards”.
4
+ version: 0.7.4
5
+ ---
6
+
7
+ This page is not written yet.
8
+
9
+ Until then, [WordPress](../../user/wordpress.md) in the user guide shows how you connect a site,
10
+ place a wizard and read its results in the admin.
@@ -3,5 +3,5 @@
3
3
  "description": "Extend engenty wizards with plugins: tools for AI steps, routes with their own tables, pages in the studio.",
4
4
  "root": true,
5
5
  "icon": "SquareTerminal",
6
- "pages": ["index", "wizard-format", "plugins"]
6
+ "pages": ["index", "wizard-format", "plugins", "integration"]
7
7
  }
@@ -21,6 +21,7 @@ version: 0.5.0
21
21
  | `wizards stop` | Stops it, with any other copy of it still running in the background. Your data is kept. AI apps that use your wizards start it again when you use one there |
22
22
  | `wizards restart` | Stops it and starts it again; after an update, that is the new version. If it isn’t running, it starts in the background |
23
23
  | `wizards update` | Installs the latest version |
24
+ | `wizards sandbox add` / `remove` | Adds the sandbox where AI steps run code, or removes it. It is agentOS, about 340 MB, and needs no Docker. Without a sandbox, steps can’t run code. `wizards sandbox` alone shows whether it is there. Name an engine to add another one: `wizards sandbox add agentos` |
24
25
  | `wizards autostart on` / `off` | Starts it when you log in, or stops doing so. It is a LaunchAgent on macOS and a systemd user service on Linux |
25
26
  | `wizards connect [app …]` | Adds your wizards to your [AI apps](./ai-apps.md): every app found, or the ones you name |
26
27
  | `wizards disconnect [app …]` | Takes them out again |
@@ -51,6 +52,7 @@ running, `wizards start` opens the running one instead of starting a second.
51
52
  | `~/.engenty/wizards/.env` | The settings file |
52
53
  | `~/.engenty/wizards/plugins` | [Plugins](./plugins.md) on this computer |
53
54
  | `~/.engenty/wizards/logs` | Logs: `install.log`, `update.log`, `runtime.log` when it runs in the background, `autostart.log` when a Mac starts it at login |
55
+ | `~/.engenty/wizards/engines` | The sandbox, once `wizards sandbox add` put it there: one folder per engine |
54
56
  | `~/.engenty/wizards/tools`, `runtime` | Its own Node.js, and engenty wizards itself |
55
57
  | `~/.engenty/wizards/bin/wizards` | The command; `~/.local/bin/wizards` links to it |
56
58
 
@@ -65,6 +67,7 @@ the studio doesn’t: one `NAME=value` per line. Restart engenty wizards after a
65
67
  | `API_PORT` | The port | `24368`; if it is taken, the next free one up to `24387` |
66
68
  | `CHROME_PATH` | The browser for PDF and PNG exports and steps on websites: Chrome, Edge, Chromium or Brave | The first one found: on a Mac Google Chrome, Microsoft Edge, Chromium or Brave in `/Applications` or `~/Applications`; on Linux `google-chrome`, `chromium`, `chromium-browser` or `microsoft-edge` on the PATH. `wizards status` names it |
67
69
  | `FFMPEG_PATH` | ffmpeg for MP4 videos | `ffmpeg` |
70
+ | `SANDBOX_ALLOW_HOSTS` | Addresses the sandbox reaches besides the npm and PyPI registries, comma-separated: `api.example.com`. It reaches nothing else | not set |
68
71
  | `OLLAMA_URL` | Where Ollama answers | `http://127.0.0.1:11434/v1` |
69
72
  | `PLUGINS_DIR` | Folders with plugins, comma-separated. If you set it, it replaces the default | `~/.engenty/wizards/plugins` |
70
73
  | `PLUGINS_WATCH` | With `0`, plugins no longer load again when their files change | `1` |
@@ -35,7 +35,7 @@ The logs show a link (“Open the studio: …”). Open it once: it signs your b
35
35
  | Data | Databases and files are on the `/data` volume. Back that volume up |
36
36
  | Models | Your own API keys or Ollama. The subscriptions of AI clients live on your own computer, not on a server |
37
37
  | PDF, PNG, browser steps, MP4 | Chromium and ffmpeg are in the image |
38
- | Code & shell | The sandbox is off in this image, so steps can’t use that tool. `compose.yaml` shows how to switch it on |
38
+ | Code & shell | The sandbox is off in this image, so steps can’t use that tool. `compose.yaml` shows how to switch it on: with Docker, or with agentOS built into the image |
39
39
  | Plugins | Put them in a folder that you mount into the container and name in `PLUGINS_DIR` |
40
40
 
41
41
  ## Update
@@ -483,7 +483,7 @@ export async function runAgentStep(step, ctx) {
483
483
  }
484
484
  const resolved = await textModel(step.model ?? "high", { ...ctx.call, effort: step.effort });
485
485
  const uploads = await personUploads(ctx);
486
- const { tools, assets, close } = await buildStepTools(step, ctx, resolved, uploads);
486
+ const { tools, assets, instructions, close } = await buildStepTools(step, ctx, resolved, uploads);
487
487
  // A step without tools of its own and without uploads: the run's device may answer it alone.
488
488
  attachTools(resolved, tools, step.tools.length === 0 && uploads.length === 0);
489
489
  try {
@@ -501,6 +501,7 @@ export async function runAgentStep(step, ctx) {
501
501
  const system = [
502
502
  groundRules(ctx),
503
503
  step.tools.includes("browser") ? BROWSER_RULES : "",
504
+ instructions ?? "",
504
505
  `Today is ${today(ctx)}.`,
505
506
  brandBlock(ctx),
506
507
  // Knowledge: what the space holds for its wizards, and how to find and read it.