wizards 0.5.0 → 0.5.2

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 (160) hide show
  1. package/README.md +45 -42
  2. package/apps/runtime/dist/agents/studio-assistant.js +3 -3
  3. package/apps/runtime/dist/app.js +7 -4
  4. package/apps/runtime/dist/auth/index.js +2 -2
  5. package/apps/runtime/dist/authoring/guide.js +1 -1
  6. package/apps/runtime/dist/cli/account.js +31 -28
  7. package/apps/runtime/dist/cli/autostart.js +5 -3
  8. package/apps/runtime/dist/cli/clients.js +1 -1
  9. package/apps/runtime/dist/cli/connect.js +24 -24
  10. package/apps/runtime/dist/cli/features.js +6 -6
  11. package/apps/runtime/dist/cli/main.js +51 -39
  12. package/apps/runtime/dist/cli/mcp.js +5 -4
  13. package/apps/runtime/dist/cli/menu.js +32 -30
  14. package/apps/runtime/dist/cli/report.js +7 -9
  15. package/apps/runtime/dist/cli/setup.js +42 -38
  16. package/apps/runtime/dist/cli/start.js +16 -15
  17. package/apps/runtime/dist/cli/status.js +20 -20
  18. package/apps/runtime/dist/cli/ui.js +9 -2
  19. package/apps/runtime/dist/cli/update.js +6 -6
  20. package/apps/runtime/dist/cli/validate.js +1 -1
  21. package/apps/runtime/dist/connectors/external.js +3 -2
  22. package/apps/runtime/dist/connectors/index.js +5 -5
  23. package/apps/runtime/dist/connectors/plugin-connections.js +2 -2
  24. package/apps/runtime/dist/connectors/via-account.js +5 -5
  25. package/apps/runtime/dist/db/control-schema.js +14 -0
  26. package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
  27. package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
  28. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
  29. package/apps/runtime/dist/docs/dev/index.md +16 -14
  30. package/apps/runtime/dist/docs/dev/plugins/connections.md +27 -24
  31. package/apps/runtime/dist/docs/dev/plugins/events.md +19 -17
  32. package/apps/runtime/dist/docs/dev/plugins/examples.md +13 -12
  33. package/apps/runtime/dist/docs/dev/plugins/index.md +29 -24
  34. package/apps/runtime/dist/docs/dev/plugins/layout.md +20 -19
  35. package/apps/runtime/dist/docs/dev/plugins/quick-start.md +16 -15
  36. package/apps/runtime/dist/docs/dev/plugins/reference.md +52 -40
  37. package/apps/runtime/dist/docs/dev/plugins/reloading.md +24 -23
  38. package/apps/runtime/dist/docs/dev/plugins/routes.md +26 -22
  39. package/apps/runtime/dist/docs/dev/plugins/runners.md +49 -44
  40. package/apps/runtime/dist/docs/dev/plugins/server.md +95 -43
  41. package/apps/runtime/dist/docs/dev/plugins/shipping.md +19 -16
  42. package/apps/runtime/dist/docs/dev/plugins/space.md +75 -69
  43. package/apps/runtime/dist/docs/dev/plugins/starters.md +22 -17
  44. package/apps/runtime/dist/docs/dev/plugins/studio.md +80 -37
  45. package/apps/runtime/dist/docs/dev/plugins/styling.md +44 -41
  46. package/apps/runtime/dist/docs/dev/plugins/tables.md +11 -11
  47. package/apps/runtime/dist/docs/dev/plugins/tools.md +26 -25
  48. package/apps/runtime/dist/docs/dev/wizard-format.md +35 -23
  49. package/apps/runtime/dist/docs/user/ai-apps.md +46 -43
  50. package/apps/runtime/dist/docs/user/assistant.md +41 -36
  51. package/apps/runtime/dist/docs/user/build-a-wizard.md +64 -39
  52. package/apps/runtime/dist/docs/user/command-line.md +47 -44
  53. package/apps/runtime/dist/docs/user/connectors.md +40 -35
  54. package/apps/runtime/dist/docs/user/create-account.md +27 -22
  55. package/apps/runtime/dist/docs/user/data.md +29 -29
  56. package/apps/runtime/dist/docs/user/first-start.md +26 -28
  57. package/apps/runtime/dist/docs/user/index.md +16 -17
  58. package/apps/runtime/dist/docs/user/install.md +28 -27
  59. package/apps/runtime/dist/docs/user/local-only.md +21 -17
  60. package/apps/runtime/dist/docs/user/models.md +92 -84
  61. package/apps/runtime/dist/docs/user/plugins.md +28 -23
  62. package/apps/runtime/dist/docs/user/run.md +51 -50
  63. package/apps/runtime/dist/docs/user/server.md +9 -9
  64. package/apps/runtime/dist/docs/user/space.md +87 -65
  65. package/apps/runtime/dist/docs/user/steps.md +73 -69
  66. package/apps/runtime/dist/docs/user/test-and-share.md +127 -105
  67. package/apps/runtime/dist/docs/user/troubleshooting.md +40 -38
  68. package/apps/runtime/dist/docs/user/wordpress.md +62 -54
  69. package/apps/runtime/dist/documents/parse.js +2 -2
  70. package/apps/runtime/dist/engenty/shims/web-ingest.js +17 -1
  71. package/apps/runtime/dist/engine/input.js +4 -4
  72. package/apps/runtime/dist/engine/push.js +2 -2
  73. package/apps/runtime/dist/engine/runner.js +8 -8
  74. package/apps/runtime/dist/engine/steps.js +2 -2
  75. package/apps/runtime/dist/engine/surface.js +1 -1
  76. package/apps/runtime/dist/film/step.js +3 -3
  77. package/apps/runtime/dist/harness/claude.js +1 -1
  78. package/apps/runtime/dist/harness/codex.js +1 -1
  79. package/apps/runtime/dist/harness/model.js +2 -2
  80. package/apps/runtime/dist/harness/terminal.js +3 -3
  81. package/apps/runtime/dist/mcp/handler.js +2 -2
  82. package/apps/runtime/dist/mcp/tools.js +9 -9
  83. package/apps/runtime/dist/models.js +15 -15
  84. package/apps/runtime/dist/plugins/claims.js +56 -0
  85. package/apps/runtime/dist/plugins/loader.js +2 -0
  86. package/apps/runtime/dist/plugins/public.js +2 -3
  87. package/apps/runtime/dist/plugins/runs.js +13 -8
  88. package/apps/runtime/dist/render/chromium.js +24 -11
  89. package/apps/runtime/dist/routes/api.js +15 -9
  90. package/apps/runtime/dist/routes/debug.js +1 -1
  91. package/apps/runtime/dist/routes/delivery.js +46 -0
  92. package/apps/runtime/dist/routes/lang.js +7 -0
  93. package/apps/runtime/dist/routes/marketplace.js +2 -1
  94. package/apps/runtime/dist/routes/plugin-public.js +2 -1
  95. package/apps/runtime/dist/routes/plugins.js +13 -14
  96. package/apps/runtime/dist/routes/runs.js +59 -49
  97. package/apps/runtime/dist/routes/studio.js +53 -23
  98. package/apps/runtime/dist/services/access.js +3 -3
  99. package/apps/runtime/dist/services/assistant-chats.js +1 -1
  100. package/apps/runtime/dist/services/brand.js +2 -2
  101. package/apps/runtime/dist/services/cloud.js +5 -4
  102. package/apps/runtime/dist/services/errors.js +2 -1
  103. package/apps/runtime/dist/services/knowledge-model.js +2 -2
  104. package/apps/runtime/dist/services/marketplace.js +2 -2
  105. package/apps/runtime/dist/services/package-file.js +12 -9
  106. package/apps/runtime/dist/services/package.js +2 -2
  107. package/apps/runtime/dist/services/project-files.js +10 -12
  108. package/apps/runtime/dist/services/projects.js +13 -8
  109. package/apps/runtime/dist/services/runners.js +4 -3
  110. package/apps/runtime/dist/services/runs.js +3 -3
  111. package/apps/runtime/dist/services/site.js +2 -2
  112. package/apps/runtime/dist/services/space-categories.js +4 -4
  113. package/apps/runtime/dist/services/space-data.js +13 -13
  114. package/apps/runtime/dist/services/space-origin.js +1 -1
  115. package/apps/runtime/dist/services/spaces.js +7 -7
  116. package/apps/runtime/dist/services/wizards.js +5 -5
  117. package/apps/runtime/dist/tools/net-guard.js +135 -24
  118. package/apps/runtime/dist/tools/plugin.js +1 -1
  119. package/apps/runtime/dist/tools/store.js +1 -1
  120. package/apps/runtime/dist/widgets/step.js +1 -1
  121. package/apps/web/dist/assets/AboutDialog-hRgrS60N.js +1 -0
  122. package/apps/web/dist/assets/{ChangelogDialog-BXpkgatF.js → ChangelogDialog-BUSpLhC6.js} +1 -1
  123. package/apps/web/dist/assets/{ChatView-t7z44uFN.js → ChatView-DO7P5Vv_.js} +1 -1
  124. package/apps/web/dist/assets/EditorPage-BabV35Bj.js +3 -0
  125. package/apps/web/dist/assets/{FlowPreview-WpS8DCPl.js → FlowPreview-CKrwLPN0.js} +1 -1
  126. package/apps/web/dist/assets/{Harness-DL7Bse40.js → Harness-o8IIQXZp.js} +1 -1
  127. package/apps/web/dist/assets/{NewWizardChannels-Crt6PzH2.js → NewWizardChannels-DqU79Myj.js} +1 -1
  128. package/apps/web/dist/assets/{NewWizardPage-COiVmNsl.js → NewWizardPage-D1UCvmDp.js} +1 -1
  129. package/apps/web/dist/assets/{OnboardingPage-BwluEqXq.js → OnboardingPage-C70bqPnq.js} +1 -1
  130. package/apps/web/dist/assets/{ProjectPage-BIRU_AlN.js → ProjectPage-BOLvmQWZ.js} +1 -1
  131. package/apps/web/dist/assets/{PublicRunner-BxMb4gEt.js → PublicRunner-B0PGyHI9.js} +1 -1
  132. package/apps/web/dist/assets/{ReadOnly-D9r5ze5S.js → ReadOnly-B0mJBD1q.js} +1 -1
  133. package/apps/web/dist/assets/{RunDrawer-DmJl2QOJ.js → RunDrawer-BsnsIJ0Y.js} +1 -1
  134. package/apps/web/dist/assets/{RunnerView-CF_1rC-7.js → RunnerView-B56i3nKO.js} +1 -1
  135. package/apps/web/dist/assets/SettingsPage-BvfRUqb2.js +1 -0
  136. package/apps/web/dist/assets/{SetupPage-BGKtbS7j.js → SetupPage-BvAFPu1J.js} +1 -1
  137. package/apps/web/dist/assets/{SharePage-C4MqXKDA.js → SharePage-hV5pYrO2.js} +1 -1
  138. package/apps/web/dist/assets/changelog-BftXQzUA.js +1 -0
  139. package/apps/web/dist/assets/{esm-CsY8TkyE.js → esm-DfapVSy6.js} +1 -1
  140. package/apps/web/dist/assets/{index-BjQvGQp_.js → index-BuFrRzvG.js} +4 -4
  141. package/apps/web/dist/assets/{index-Bi2x2g7n.css → index-DSbQiZY8.css} +1 -1
  142. package/apps/web/dist/assets/{lock-CI6TpWL-.js → lock-B-tJwE4p.js} +1 -1
  143. package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
  144. package/apps/web/dist/assets/{phone-BrtQz-fG.js → phone-DRHxY0l_.js} +1 -1
  145. package/apps/web/dist/assets/{settings-menu-BtEKL72n.js → settings-menu-OPrJTW8a.js} +1 -1
  146. package/apps/web/dist/assets/{ui-4IaTD7LB.js → ui-DftgEA2k.js} +3 -3
  147. package/apps/web/dist/assets/{upload-CWJAtPRo.js → upload-B73coFNw.js} +1 -1
  148. package/apps/web/dist/index.html +3 -3
  149. package/apps/web/dist/mcp/flow.html +4 -4
  150. package/bin/wizards.mjs +1 -1
  151. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +21 -2
  152. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  153. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +48 -48
  154. package/node_modules/@engenty-wizards/shared/dist/run.js +48 -48
  155. package/package.json +5 -4
  156. package/apps/web/dist/assets/AboutDialog-A_ILyeue.js +0 -1
  157. package/apps/web/dist/assets/EditorPage-4qxNJgKr.js +0 -3
  158. package/apps/web/dist/assets/SettingsPage-3xSInE-A.js +0 -1
  159. package/apps/web/dist/assets/changelog-CM_GdIVI.js +0 -1
  160. package/apps/web/dist/assets/oss-credits-BQQX0kvk.js +0 -1
@@ -1,24 +1,24 @@
1
1
  ---
2
2
  title: The space
3
3
  description: Knowledge a plugin writes, a block and tools for every AI step of a space, texts in its search index, tools and cards of its assistant.
4
- version: 0.3.7
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
- A space holds what all its wizards share: a title, logos, colours, facts, and Knowledge —
8
- pages, tables and files with their Kategorien. A plugin writes Knowledge, adds to what the
9
- space's AI steps know, to what its search finds, and to what its assistant can do. The runtime's
10
- own tables call a space a project: `wizard.projectId` in a [run event](./events.md) is a space's
11
- id.
7
+ A space holds what all its wizards share: a name, its brand (logos, colours, fonts), facts, and
8
+ Knowledge – pages, tables and files with their categories. A plugin can write Knowledge and add to
9
+ what the space’s AI steps know, to what its search finds and to what its assistant can do. The
10
+ runtime’s own tables call a space a project: `wizard.projectId` in a [run event](./events.md) is
11
+ a space’s id.
12
12
 
13
13
  ## Knowledge a plugin writes
14
14
 
15
- `server.spaceData` writes pages, tables and files into a space's Knowledge under keys of the
16
- plugin's own, with their Kategorien. Every write is indexed: `project_search` finds it a moment
17
- later, filtered by its Kategorien and checked like everything else in Knowledge. `space` is the
18
- space's id.
15
+ `server.spaceData` writes pages, tables and files into a space’s Knowledge under keys of the
16
+ plugin’s own, with their categories. Every write is indexed: `project_search` finds it a moment
17
+ later, filtered by its categories and checked like everything else in Knowledge. `space` is the
18
+ space’s id.
19
19
 
20
20
  ```ts
21
- // The source's Kategorien, once. `proposed: true` shows them to the person to take first.
21
+ // The source's categories, once. `proposed: true` shows them to the person to confirm first.
22
22
  await server.spaceData.putCategories({
23
23
  space,
24
24
  categories: [
@@ -36,7 +36,7 @@ await server.spaceData.putPage({
36
36
  title: "Weekly meeting, 14 March 2026",
37
37
  markdown,
38
38
  categories: { Team: "Sales", "Meeting date": "2026-03-14", Duration: 1.5 },
39
- fill: true, // a model fills the Kategorien the entry did not bring
39
+ fill: true, // a model fills the categories the entry did not bring
40
40
  });
41
41
 
42
42
  // A file is read as an upload is: a page, sub-pages, a table per sheet, or a file.
@@ -45,20 +45,20 @@ const doc = await server.spaceData.putDocument({ space, key, label, name, mime,
45
45
 
46
46
  | `server.spaceData.` | Does |
47
47
  |---|---|
48
- | `putCategories({ space, categories, proposed? })` | Adds Kategorien, or values to known ones. Types: `choice` (with `values`, `multiple`, `ordered`), `date`, `number` (with `unit`), `text`, `boolean`. `column: { table, column }` makes a column of a table the plugin wrote one |
48
+ | `putCategories({ space, categories, proposed? })` | Adds categories, or values to known ones. Types: `choice` (with `values`, `multiple`, `ordered`), `date`, `number` (with `unit`), `text`, `boolean`. `column: { table, column }` takes a category’s values from a column of a table the plugin wrote |
49
49
  | `putPage(input)` | A page: `title`, `markdown`, `parent` (the key of a page to stand under), `original` (the file it was read from), `categories`, `fill`, `review` (why a person should look) |
50
50
  | `putTable(input)` | A table: typed `columns`, `rows` (cells by column id, they replace the rows the key had), `format: "faq"` |
51
- | `putFile(input)` | A file that is no page: found by its name, `description` and Kategorien; `text` is what a step reads of it |
52
- | `putDocument(input)` | A file read as an upload is. Gives what it became: `items` (pages, tables), `review` when it read badly or not at all |
53
- | `readPages(space, { parent? })` | The space's own pages, whoever wrote them, with their Markdown: `{ id, title, path, parentId, markdown, origin, originKey, updatedAt }` |
54
- | `files(space, kind?)`, `readFile(space, id)` | The space's files of a kind (`logo`, `asset`, `document`; `document` when none is named), newest first, and one with its bytes: a price list someone dropped into Knowledge |
51
+ | `putFile(input)` | A file that is not a page. It is found by its name, `description` and categories; `text` is what a step reads of it |
52
+ | `putDocument(input)` | A file, read the way an upload is. Returns what it became: `items` (pages, tables), and `review` when it was hard to read or could not be read |
53
+ | `readPages(space, { parent? })` | The space’s own pages, whoever wrote them, with their Markdown: `{ id, title, path, parentId, markdown, origin, originKey, updatedAt }` |
54
+ | `files(space, kind?)`, `readFile(space, id)` | The space’s files of a kind (`document`, `logo`, `asset`, `pattern`; `document` when none is named), newest first, and one file with its bytes, such as a price list someone dropped into Knowledge |
55
55
  | `list(space)` | Everything the plugin wrote, by key, with its path and `state` |
56
- | `remove(space, key?)` | Takes out one key, or everything of the plugin in the space |
57
- | `summarize(space, category?, value?)` | Writes the Overview of values with three items or more; a model call each. Gives how many it wrote |
56
+ | `remove(space, key?)` | Takes out one key, or everything the plugin wrote in the space. Returns how many items it removed |
57
+ | `summarize(space, category?, value?)` | Writes the “Overview” of one value, or of every value with three items or more. Each costs a model call. Returns how many it wrote |
58
58
 
59
- - `label` is what the person knows the source as; Knowledge shows it beside the item.
60
- - A person who changes an item keeps it: the plugin's next write answers `state: "kept"` and
61
- changes nothing, and `remove` leaves it.
59
+ - `label` is what the person knows the source as. Knowledge shows it beside the item.
60
+ - When a person changes an item, it is theirs: the plugin’s next write returns `state: "kept"`
61
+ and changes nothing, and `remove` leaves it.
62
62
  - What a step reads of an item is its path: `pages/…`, `tables/…`, `files/…`.
63
63
 
64
64
  ## A block for every AI step
@@ -82,40 +82,40 @@ server.registerSpaceContext({
82
82
  });
83
83
  ```
84
84
 
85
- - Every agent step of the space's wizards gets the block in its instructions, after what the
85
+ - Every agent step of the space’s wizards gets the block in its instructions, after what the
86
86
  space says about itself and its documents, and gets the tools with it. No step lists them.
87
- - `block` is asked once per step. `null` or an empty text: the step gets neither the block nor
88
- the tools.
87
+ - `block` is called once per step. If it returns `null` or an empty text, the step gets neither
88
+ the block nor the tools.
89
89
  - A block is cut at 8,000 characters. A block that throws is logged, and the step runs without
90
90
  it.
91
91
  - The tools are [tools](./tools.md) like the ones a step lists; the model sees
92
- `<plugin>_<name>`. Their names and the names of the plugin's other tools are one list.
93
- - A model on the device calls no tools: Apple Intelligence on a Mac, or the phone a run is
94
- followed on, which answers only steps that list no tools themselves. The step then gets the
95
- block without the tools.
96
- - `steps` says which steps get it: `["agent"]` when left out. `"html"` is the steps that write a
97
- document or a dashboard as HTML; they get the block without the tools, after the space's
98
- brand: how the brand's elements look, say.
92
+ `<plugin>_<name>`. Their names and the names of the plugin’s other tools are one list.
93
+ - A model on the device calls no tools. That is Apple Intelligence on a Mac, or on the phone a
94
+ run is followed on, which answers only steps that list no tools themselves. Such a step gets
95
+ the block without the tools.
96
+ - `steps` says which steps get the block: `["agent"]` when left out. `"html"` stands for the
97
+ steps that write a document or a dashboard as HTML. They get the block without the tools,
98
+ after the space’s brand – a good place to say how the brand’s elements look.
99
99
 
100
- ## The space's brand
100
+ ## The space’s brand
101
101
 
102
- `server.spaces.brand(space)` reads what Info & brand holds of the space's look, for a block, a
102
+ `server.spaces.brand(space)` reads what Info & brand holds of the space’s look, for a block, a
103
103
  page or a model call in it:
104
104
 
105
105
  ```ts
106
106
  const brand = await server.spaces.brand(space.id);
107
- // { name: "Mira Kaffee", colors: [{ name: "Primär", value: "#7a3e1d" }, …],
107
+ // { name: "Mira Kaffee", colors: [{ name: "Primary", value: "#7a3e1d" }, …],
108
108
  // fonts: [{ family: "Fraunces", role: "Headings", weights: [600] }, …] }
109
109
  ```
110
110
 
111
- - The first colour is the accent. A font's `role` is what it is for, in the person's words.
112
- - `null` for a space the tenant does not have. The studio half gets the same as
111
+ - The first colour is the accent. A font’s `role` is what it is for, in the person’s words.
112
+ - It returns `null` for a space the tenant does not have. The studio half gets the same from
113
113
  `studio.useSpace().brand`.
114
114
 
115
115
  ## The search index
116
116
 
117
- Every agent step can search the space with `project_search`. It finds Knowledge — written by
118
- people and by `server.spaceData` — and the texts plugins put in themselves, outside Knowledge:
117
+ Every agent step can search the space with `project_search`. It finds Knowledge, written by
118
+ people and by `server.spaceData`, and the texts plugins put in themselves, outside Knowledge:
119
119
 
120
120
  ```ts
121
121
  await server.index.put({
@@ -131,21 +131,23 @@ await server.index.remove(space.id); // every text of the plugin in the space
131
131
  ```
132
132
 
133
133
  - Putting a key again replaces what it held.
134
- - A text is cut into passages. Its title is searched with it, and a hit is called by it. The test
135
- search under Knowledge shows hits as a step gets them.
136
- - The index searches by words and by trigrams (codes, parts of compounds); with an embedding model
137
- set up, also by meaning. A classifier then judges each candidate.
138
- - Such a text has no Kategorien: a search with a filter leaves it out. Write Knowledge with
134
+ - A text is cut into passages. Its title is searched with it, and a hit is named by it. The test
135
+ search under Knowledge (“Try a search…”) shows hits as a step gets them.
136
+ - The index searches by words and by trigrams (codes, parts of compounds). With an embedding
137
+ model set up, it also searches by meaning. A classifier then judges each candidate.
138
+ - Such a text has no categories, so a search with a filter leaves it out. Write Knowledge with
139
139
  `server.spaceData` when it should be filtered.
140
- - A step gets `project_search` as soon as the space has Knowledge or a plugin's text.
141
- - A tenant without the plugin does not find its texts. They go with the space when it is
142
- deleted.
140
+ - A step gets `project_search` as soon as the space has Knowledge or a plugin’s text.
141
+ - A tenant without the plugin does not find its texts. They are deleted with the space.
143
142
 
144
143
  ## Tools of the space assistant
145
144
 
146
- The studio's assistant stands on every page of the studio but the editor. Where a space is open
147
- and may be changed, it fills it in from what the person says, a website or files. A plugin gives
148
- it tools:
145
+ The studio’s assistant, Goose, stands on every page of the studio except the editor. Where a
146
+ space is open and the person may change it, the assistant fills the space in from what the person
147
+ says, a website or files. A plugin gives it tools:
148
+
149
+ > Goose is still being tried out and doesn’t show in every studio yet. Where it doesn’t, nobody
150
+ > sees the tools, cards and page hooks below.
149
151
 
150
152
  ```ts
151
153
  server.registerAssistantTool({
@@ -169,9 +171,9 @@ server.registerAssistantTool({
169
171
  | `emit(message)` | The line the chat shows while the assistant works |
170
172
  | `changed()` | The tool changed what the space page shows: the page draws it again |
171
173
 
172
- - The model sees the tool as `<plugin>_<name>`, beside the assistant's own tools.
173
- - The assistant does not keep a tool's result from one message to the next. Keep what a later
174
- message needs in the plugin's tables.
174
+ - The model sees the tool as `<plugin>_<name>`, beside the assistant’s own tools.
175
+ - The assistant doesn’t keep a tool’s result from one message to the next. Keep what a later
176
+ message needs in the plugin’s tables.
175
177
 
176
178
  ### A card in the chat
177
179
 
@@ -193,8 +195,8 @@ studio.registerAssistantCard({
193
195
  });
194
196
  ```
195
197
 
196
- - `tool` is the name the server half registered, without the plugin's prefix.
197
- - The card stands below the assistant's answer. Without a card for the tool, nothing shows.
198
+ - `tool` is the name the server half registered, without the plugin’s prefix.
199
+ - The card stands below the assistant’s answer. Without a card for the tool, nothing shows.
198
200
  - `data` is what `execute` returned, sent as it is: keep it small.
199
201
  - `send(message)` sends a message to the assistant as if the person typed it.
200
202
 
@@ -203,11 +205,11 @@ studio.registerAssistantCard({
203
205
  `space.created`, `space.updated`, `space.deleted`, `space.file.ready` and `space.file.removed`
204
206
  say what changed. See [Events](./events.md).
205
207
 
206
- ## The assistant on the plugin's own page
208
+ ## The assistant on the plugin’s own page
207
209
 
208
- The assistant stands on the plugin's pages too, as on every page of the studio. A page tells it
210
+ The assistant stands on the plugin’s pages too, as on every page of the studio. A page tells it
209
211
  what to say there, and what it shows; the assistant is told what the page holds and uses the
210
- plugin's assistant tools first.
212
+ plugin’s assistant tools first.
211
213
 
212
214
  ```tsx
213
215
  const Assistant = studio.Assistant;
@@ -231,16 +233,19 @@ server.registerAssistantContext({
231
233
  ```
232
234
 
233
235
  - `studio.Assistant` draws nothing: place it once, anywhere in the page. `title` and `hello`
234
- greet before the first message; up to three `suggestions` stand under them. A click sends one
235
- at once (its `message`, or the label); `fill: true` puts it into the field to finish first.
236
- - `block` is asked once per message, only when the person writes from the plugin's page. A
237
- block is cut at 8,000 characters; one that throws is logged and left out.
238
- - In a read-only space the assistant has none of the space's tools nor the plugins'.
236
+ greet the person before the first message, and up to three `suggestions` stand under them. A
237
+ click sends a suggestion at once (its `message`, or the label). With `fill: true` it goes into
238
+ the field instead, for the person to finish.
239
+ - `block` is called once per message, and only when the person writes from the plugin’s page. A
240
+ block is cut at 8,000 characters. One that throws is logged and left out.
241
+ - Where the person may not change the space, the assistant gets neither the space’s tools nor
242
+ the plugins’. That is a space published from engenty wizards on a computer, or a person who
243
+ may not build here.
239
244
 
240
245
  ### What the page shows
241
246
 
242
247
  `studio.useAssistantPage` tells the assistant what the person sees: an open item, a selection,
243
- a line on what the page holds, its fields. It goes up with every message, beside the page's
248
+ a line on what the page holds, its fields. It is sent with every message, beside the page’s
244
249
  address and the trail of the top bar.
245
250
 
246
251
  ```tsx
@@ -253,7 +258,7 @@ studio.useAssistantPage({
253
258
  });
254
259
  ```
255
260
 
256
- - `open` is `{ kind, id?, label }`; `selection` the ids of what the person picked;
261
+ - `open` is `{ kind, id?, label }`, `selection` holds the ids of what the person picked, and
257
262
  `fields` is `{ id, label, value }[]`.
258
263
  - It takes the words of `studio.Assistant` too (`title`, `hello`, `placeholder`, `suggestions`,
259
264
  `onChanged`): one call can do both.
@@ -263,8 +268,9 @@ studio.useAssistantPage({
263
268
 
264
269
  `studio.useAssistantTool` gives the assistant a tool that runs in the browser while the page is
265
270
  shown: it filters a list, opens an item, picks a tab. The model sees it as `<plugin>_<name>`
266
- (a `-` in the plugin's id becomes `_`); `run` gets the arguments and returns what the assistant
267
- learns. It changes what the page shows, never saves: saving stays with the server half's tools.
271
+ (a `-` in the plugin’s id becomes `_`). `run` gets the arguments and returns what the assistant
272
+ learns. Such a tool changes what the page shows and never saves: saving stays with the server
273
+ half’s tools.
268
274
 
269
275
  ```tsx
270
276
  studio.useAssistantTool(
@@ -286,6 +292,6 @@ studio.useAssistantTool(
286
292
  );
287
293
  ```
288
294
 
289
- Besides a page's tools the assistant can open a studio page, point at an element, read what is
295
+ Besides a page’s tools, the assistant can open a studio page, point at an element, read what is
290
296
  on the screen, fill a field and scroll. It finds elements by their headings, labels and
291
- buttons; `data-assist="<id>"` on an element names it for the assistant.
297
+ buttons. `data-assist="<id>"` on an element names it for the assistant.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: Starters
3
- description: Wizards a plugin brings as templates — how to register one, where it shows, and what a wizard made from it gets.
4
- version: 0.3.2
3
+ description: Wizards a plugin brings as templates – how to register one, where it shows, and what a wizard made from it gets.
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
- A starter is a wizard a plugin brings as a template. It stands beside the marketplace's starters
8
- wherever a wizard is made, for the tenants that have the plugin.
7
+ A starter is a wizard a plugin brings as a template. For the tenants that have the plugin, it
8
+ stands beside the marketplace’s templates wherever a wizard is made.
9
9
 
10
10
  ```ts
11
11
  import { readFileSync } from "node:fs";
@@ -15,7 +15,7 @@ const read = (path: string) => readFileSync(wizards.resolvePath(path), "utf8");
15
15
  server.registerStarter({
16
16
  id: "follow-up",
17
17
  title: "Follow up a contact",
18
- description: "Looks the person up and drafts a follow-up e-mail.",
18
+ description: "Looks the person up and drafts a follow-up email.",
19
19
  language: "en",
20
20
  definition: JSON.parse(read("starters/follow-up.json")),
21
21
  files: { "widget/card.html": read("starters/card.html") },
@@ -29,13 +29,13 @@ server.registerStarter({
29
29
  | `description` | What it makes, in one or two sentences. Shown under the title |
30
30
  | `language` | `"de"` or `"en"`, the language its texts are in. Default: `"en"` |
31
31
  | `definition` | A complete wizard definition: the JSON the studio keeps and `get_authoring_guide` describes |
32
- | `files` | Files of the wizard's workspace by path: widgets, sample data. A string is written as UTF-8, a `Uint8Array` as it is |
32
+ | `files` | Files of the wizard’s workspace by path: widgets, sample data. A string is written as UTF-8, a `Uint8Array` as it is |
33
33
 
34
34
  ## Checked when it is registered
35
35
 
36
- The definition is read as a wizard when `registerStarter` is called. A starter that is no wizard,
37
- an id that is not allowed and an id registered twice throw, and the plugin does not load; the
38
- error names the starter:
36
+ The definition is read as a wizard when `registerStarter` is called. A starter that is not a
37
+ valid wizard, an id that is not allowed and an id registered twice all throw, and the plugin does
38
+ not load. The error names the starter:
39
39
 
40
40
  ```
41
41
  Starter "follow-up": steps.2.fields: …
@@ -43,23 +43,28 @@ Starter "follow-up": steps.2.fields: …
43
43
 
44
44
  ## Where it shows
45
45
 
46
- The starter is known as `<plugin id>.<starter id>`: `contacts.follow-up`. A marketplace entry's id
47
- has no dot, so the two never meet.
46
+ The starter is known as `<plugin id>.<starter id>`: `contacts.follow-up`. A marketplace entry’s id
47
+ has no dot, so the two never clash.
48
48
 
49
49
  | Where | |
50
50
  |---|---|
51
- | New wizard in the studio | First among the templates, with the plugin's name on its card. It cannot be starred: it is always there, also without a connection |
51
+ | New wizard in the studio | First among the gallery’s templates, and under “Similar wizards” when the wish someone types comes close to it. Its card shows the plugin’s name. It cannot be starred: it is always there, also without a connection |
52
52
  | `GET /api/studio/marketplace` | First on the first page, with `plugin: { id, name }` |
53
53
  | MCP `list_starters`, `get_starter`, `create_wizard` | Listed first with `plugin`; `starterId` takes its id |
54
54
  | `GET /api/v1/templates`, for programs with a key | First, with `plugin`; `POST /api/v1/wizards` takes its id as `starterId` |
55
55
 
56
56
  Only the tenants that have the plugin see it, by the same rule as its tools
57
- ([Which tenant has a plugin](./shipping.md#which-tenant-has-a-plugin)). Words and filters of a search find it by its title, its description
58
- and the plugin's name.
57
+ ([Which tenant has a plugin](./shipping.md#which-tenant-has-a-plugin)). A search finds it by its
58
+ title, its description and the plugin’s name.
59
+
60
+ A marketplace entry can stand for a plugin’s starter. Where the tenant has that plugin, the
61
+ entry is left out and the plugin’s own starter is listed. Where it doesn’t, the entry is marked
62
+ Pro with the plugins it needs, and a wizard cannot be made from it.
59
63
 
60
64
  ## A wizard made from it
61
65
 
62
- - gets a copy of the definition and the files; later changes of the starter do not reach it.
63
- - keeps the starter's id in `starter`. `starterRevision` stays empty: the starter changes with
64
- the plugin's version, not with a revision of its own.
66
+ - gets a copy of the definition and the files. Later changes to the starter do not reach it.
67
+ - keeps the starter’s id in `starter`. `starterRevision` stays empty: the starter changes with
68
+ the plugin’s version, not with a revision of its own.
69
+ - is written in the starter’s `language`, whatever language the person asked for.
65
70
  - is not counted at the marketplace.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: The studio half
3
- description: Pages, icons in the app bar, sections of the settings and of the space page — written in React, built once, drawn inside the studio.
4
- version: 0.4.4
3
+ description: Pages, icons in the app bar, sections of the settings and of the space page – written in React, built once, drawn inside the studio.
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
7
  The studio half is `ui/plugin.tsx`. Its default export is a function; the studio calls it after
@@ -84,16 +84,19 @@ export default defineStudioPlugin((studio) => {
84
84
 
85
85
  | Call | Adds |
86
86
  |---|---|
87
- | `studio.registerPage({ path, component, wide? })` | A page of the studio at `/studio<path>`, inside the studio's frame. `path` takes `:name` parts. `wide`: the page lays out its own columns and takes the screen's width |
87
+ | `studio.registerPage({ path, component, wide? })` | A page of the studio at `/studio<path>`, inside the studio’s frame. `path` takes `:name` parts. `wide`: the page lays out its own columns and takes the screen’s width |
88
88
  | `studio.registerNav({ to, label, icon })` | An icon in the app bar that opens a page |
89
89
  | `studio.registerSettingsSection({ id, label, icon, component, menu? })` | A section of the settings at `/studio/settings/<id>`. `menu`: the user menu links to it directly |
90
- | `studio.registerSpaceSection({ id, group?, label, hint?, component })` | A section of a part of the space page at `/studio/space/<group>#<id>`, below the part's own. See [The space](#the-space) |
91
- | `studio.registerShareSection({ runner, component })` | A runner's own settings for a wizard (a keyword), under the share dialog's gear once the runner is on and set up. See [Runners](./runners.md#the-runners-part-of-the-share-dialog) |
92
- | `studio.registerShareSetup({ runner, title, component, notice? })` | What "Einrichten" in the runner's row opens: a view inside the share dialog, with its buttons at the dialog's foot, and a banner of the runner's news. See [Runners](./runners.md#setting-up-inside-the-dialog) |
93
- | `studio.registerAssistantCard({ tool, component })` | A card in the assistant's chat for what a tool of the server half returned. See [The space](./space.md#a-card-in-the-chat) |
90
+ | `studio.registerSpaceSection({ id, group?, label, hint?, component })` | A section of a part of the space page at `/studio/space/<group>#<id>`, below the part’s own. See [The space](#the-space) |
91
+ | `studio.registerSpacePart({ id, label, icon, after?, component })` | A part of the space page of the plugin’s own, at `/studio/space/<id>`. See [A part of the plugin’s own](#a-part-of-the-plugins-own) |
92
+ | `studio.registerShareSection({ runner, component })` | A runner’s own settings for a wizard (a keyword), under the share dialog’s gear once the runner is on and set up. See [Runners](./runners.md#the-runners-part-of-the-share-dialog) |
93
+ | `studio.registerShareSetup({ runner, title, component, notice? })` | What “Set up” in the runner’s row opens: a view inside the share dialog, with its buttons at the dialog’s foot, and a banner of the runner’s news. See [Runners](./runners.md#setting-up-inside-the-dialog) |
94
+ | `studio.registerAssistantCard({ tool, component })` | A card in the assistant’s chat for what a tool of the server half returned. See [The space](./space.md#a-card-in-the-chat) |
94
95
 
95
- - The studio's own addresses are taken: `/new`, `/edit`, `/settings`, `/space`, `/setup`,
96
+ - The studio’s own addresses are taken: `/new`, `/edit`, `/settings`, `/space`, `/setup`,
96
97
  `/sign-in`.
98
+ - The settings’ own sections are taken as ids: `account`, `team`, `connectors`, `models`,
99
+ `integrate` and `plugins`.
97
100
  - `label` is a function, since the language can change while the studio is open.
98
101
  - `icon` is a component that takes `className`: any icon of `lucide-react`.
99
102
 
@@ -125,11 +128,11 @@ space page, behind the house icon (Space) in the app bar, shows them in four par
125
128
  | Part | `group` | Holds |
126
129
  |---|---|---|
127
130
  | Info & brand | `info` | Basics, Facts, Logos, Colours, Brand assets |
128
- | Knowledge | `knowledge` | Documents, pages, tables and files with their Kategorien: what every AI step looks things up in |
131
+ | Knowledge | `knowledge` | Documents, pages, tables and files with their categories: what every AI step looks things up in |
129
132
  | Data | `data` | What the wizards keep |
130
133
  | Results | `results` | The runs that reached their result |
131
134
 
132
- A plugin adds a section to one of them, below the part's own:
135
+ A plugin adds a section to one of them, below the part’s own:
133
136
 
134
137
  ```tsx
135
138
  function ContactsOfSpace() {
@@ -152,26 +155,27 @@ studio.registerSpaceSection({
152
155
  ```
153
156
 
154
157
  - `group` is the part; without one the section stands under Knowledge.
155
- - The studio draws the heading and the hint above the section, as it does for the space's own,
156
- and lists the section in the part's menu under the plugin's name. The component draws the
158
+ - The studio draws the heading and the hint above the section, as it does for the space’s own,
159
+ and lists the section in the part’s menu under the plugin’s name. The component draws the
157
160
  rest, usually a `Card`.
158
- - `id` is the section's anchor: `/studio/space/data#contacts` opens the page there, and so does
161
+ - `id` is the section’s anchor: `/studio/space/data#contacts` opens the page there, and so does
159
162
  `/studio/space#contacts`. In Knowledge a section is a page of its own:
160
163
  `/studio/space/knowledge?show=s:<id>`, where `#<id>` leads too. Ids are unique on the whole
161
- page; the space's own are taken: `base`, `facts`, `logos`, `colors`, `typography`, `assets`,
164
+ page; the space’s own are taken: `base`, `facts`, `logos`, `colors`, `typography`, `assets`,
162
165
  `documents`.
163
166
  - `studio.useSpace()` is a hook: `{ id, name, readOnly, brand }`, or `null` while there is none.
164
167
  A component that calls it draws again when the person picks another space, or changes its
165
- brand. `readOnly`: the space came from a local install, or nothing is built on this runtime;
166
- show it, change nothing. `brand` is what Info & brand holds of the look: `name`, `colors`
167
- (`{ name, value }`, the first is the accent) and `fonts` (`{ family, role, weights }`).
168
+ brand. `readOnly`: the space is edited in engenty wizards on a computer and only shown here,
169
+ or nothing is built on this runtime. Show it, but change nothing. `brand` is what Info & brand
170
+ holds of the look: `name`, `colors` (`{ name, value }`, the first is the accent) and `fonts`
171
+ (`{ family, role, weights }`).
168
172
  - Every wizard belongs to a space: a [run event](./events.md) names it as
169
- `wizard.projectId`. Keep it with what the plugin stores to show a space's part of it.
173
+ `wizard.projectId`. Keep it with what the plugin stores to show a space’s part of it.
170
174
 
171
- ### A part of the plugin's own
175
+ ### A part of the plugin’s own
172
176
 
173
177
  A plugin whose subject is as big as one of the parts adds a part of its own: an entry in the
174
- parts' menu, with a page at `/studio/space/<id>` that the plugin draws whole. The part's menu
178
+ parts’ menu, with a page at `/studio/space/<id>` that the plugin draws whole. The part’s menu
175
179
  lists what the page holds; `studio.useSpaceMenu` fills it while the page is drawn.
176
180
 
177
181
  ```tsx
@@ -195,16 +199,16 @@ studio.registerSpacePart({
195
199
  });
196
200
  ```
197
201
 
198
- - `id` is the address: `/studio/space/library`. The parts' own (`info`, `knowledge`, `data`,
202
+ - `id` is the address: `/studio/space/library`. The parts’ own (`info`, `knowledge`, `data`,
199
203
  `results`) are taken.
200
204
  - `after` is the part it follows in the menu: `info`, `knowledge` or `data` (default).
201
205
  - Each entry of `useSpaceMenu` jumps to the element with its `id` in what the part draws, and
202
- the menu marks the one in view. `Anchor` and `Section` draw them as the space's own parts do.
203
- - The component is drawn again for another space: keep the space's data under its id.
206
+ the menu marks the one in view. `Anchor` and `Section` draw them as the space’s own parts do.
207
+ - The component is drawn again for another space: keep the space’s data under its id.
204
208
 
205
209
  ## Words in two languages
206
210
 
207
- The studio speaks German and English. `studio.i18n.register` takes the plugin's words and gives
211
+ The studio speaks German and English. `studio.i18n.register` takes the plugin’s words and gives
208
212
  the function that reads them in the language in effect:
209
213
 
210
214
  ```ts
@@ -216,12 +220,12 @@ const t = studio.i18n.register({
216
220
  t("count", { n: 12 });
217
221
  ```
218
222
 
219
- - `de` is the source: a key missing in `en` shows the German.
223
+ - `de` is the source: a key missing in `en` shows the German, so give every key in both.
220
224
  - `{name}` in a text is filled from the second argument.
221
225
  - `studio.i18n.lang()` is `"de"` or `"en"`, for dates and numbers:
222
226
  `new Date(at).toLocaleString(studio.i18n.lang())`.
223
227
 
224
- ## The plugin's own routes
228
+ ## The plugin’s own routes
225
229
 
226
230
  `studio.api` calls the routes the server half registered, signed in as the person:
227
231
 
@@ -251,8 +255,8 @@ route.
251
255
 
252
256
  ## Where the page stands
253
257
 
254
- The top bar shows a trail after the space's name. `studio.useCrumbs` is a hook that sets it
255
- while the page is shown, in place of a heading of the page's own:
258
+ The top bar shows a trail after the space’s name. `studio.useCrumbs` is a hook that sets it
259
+ while the page is shown, in place of a heading of the page’s own:
256
260
 
257
261
  ```tsx
258
262
  studio.useCrumbs([{ label: t("title"), to: "/contacts" }, { label: person.name ?? person.email }]);
@@ -261,16 +265,54 @@ studio.useCrumbs([{ label: t("title"), to: "/contacts" }, { label: person.name ?
261
265
  - Each step is `{ label, to? }`; the last one is the page itself. A phone does not show the trail.
262
266
  - The assistant names the page by its trail.
263
267
 
268
+ ## A page with a menu
269
+
270
+ A page with parts of its own puts its menu where the studio’s own pages put theirs. `MenuPage`
271
+ from `@engenty-wizards/web/ui` draws the menu in the studio’s sidebar while that is open, and at
272
+ the page’s left otherwise, so there is only ever one menu at the left. `PageMenu` draws the menu
273
+ as the settings do: a list with one level below it, and tabs on a phone.
274
+
275
+ ```tsx
276
+ import { MenuPage, PageMenu } from "@engenty-wizards/web/ui";
277
+ import { useParams } from "react-router";
278
+
279
+ function ContactsPage() {
280
+ const { tab = "all" } = useParams();
281
+ return (
282
+ <MenuPage
283
+ menu={
284
+ <PageMenu
285
+ title={t("title")}
286
+ current={tab}
287
+ entries={[
288
+ { id: "all", label: t("all"), to: "/contacts/all" },
289
+ { id: "new", label: t("new"), to: "/contacts/new" },
290
+ ]}
291
+ />
292
+ }
293
+ >
294
+ <ContactList tab={tab} />
295
+ </MenuPage>
296
+ );
297
+ }
298
+ ```
299
+
300
+ - An entry is `{ id, label, icon?, to, entries? }`; `entries` is the level below.
301
+ - `current` is the id of the entry shown, at either level.
302
+
264
303
  ## The assistant
265
304
 
266
- The studio's assistant stands on a plugin's pages too. `studio.Assistant`,
305
+ The studio’s assistant stands on a plugin’s pages too. `studio.Assistant`,
267
306
  `studio.useAssistantPage` and `studio.useAssistantTool` tell it what to say there, what the page
268
307
  shows and what it may do in it. See [The space](./space.md#the-assistant-on-the-plugins-own-page).
269
308
 
309
+ For now the studio shows its assistant only where it is switched on, and it is off by default.
310
+ While it is off, these calls do no harm and nobody sees what they say.
311
+
270
312
  ## Build it
271
313
 
272
314
  The studio half is built to two files, `dist/client.js` and `dist/client.css`. The build needs
273
- a checkout of the repository, since it uses the studio's own tools:
315
+ a checkout of the repository, since it uses the studio’s own tools:
274
316
 
275
317
  ```bash
276
318
  node scripts/build-plugin.mjs <plugin folder>
@@ -280,7 +322,7 @@ node scripts/build-plugin.mjs <plugin folder>
280
322
  node scripts/build-plugin.mjs <plugin folder> --watch
281
323
  ```
282
324
 
283
- The plugin's folder may be anywhere, also outside the checkout. With `--watch` the files are
325
+ The plugin’s folder may be anywhere, also outside the checkout. With `--watch` the files are
284
326
  built again on every save, and an open studio takes them in place.
285
327
 
286
328
  Ship `dist/` with the plugin: an install without a checkout cannot build it.
@@ -296,11 +338,11 @@ there is one React and one router on the page:
296
338
  | `react-dom` | `createPortal`, `flushSync` |
297
339
  | `react-router` | `Link`, `NavLink`, `Navigate`, `useNavigate`, `useParams`, `useLocation`, `useSearchParams` |
298
340
  | `@tanstack/react-query` | `useQuery`, `useMutation`, `useQueryClient` |
299
- | `@engenty-wizards/web/ui` | The studio's components, see [Styles and components](./styling.md) |
341
+ | `@engenty-wizards/web/ui` | The studio’s components, see [Styles and components](./styling.md) |
300
342
  | `@engenty-wizards/plugin-sdk/studio` | `defineStudioPlugin` and the types |
301
343
 
302
- Everything else a plugin imports is bundled into `client.js`. A package the plugin's folder
303
- does not have, such as `lucide-react`, comes from the studio's.
344
+ Everything else a plugin imports is bundled into `client.js`. A package the plugin’s folder
345
+ does not have, such as `lucide-react`, comes from the studio’s.
304
346
 
305
347
  ## Undo on reload
306
348
 
@@ -314,7 +356,8 @@ studio.onUnload(() => clearInterval(timer));
314
356
 
315
357
  ## When it fails
316
358
 
317
- - A studio half that throws while it loads registers nothing. Settings → Plugins says "The
318
- studio half did not load" with the reason. The server half keeps working.
319
- - A page or section that throws while it draws shows "This part of a plugin crashed" in its
359
+ - A studio half that throws while it loads registers nothing. Settings → Plugins says “The
360
+ studio part didn’t load. Check the plugin and reload” with the reason. The server half keeps
361
+ working.
362
+ - A page or section that throws while it draws shows “This part of a plugin crashed.” in its
320
363
  place. The rest of the studio stays up.