@hera-al/server 1.6.12 → 1.6.13
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.
- package/bundled/a2ui/SKILL.md +339 -0
- package/bundled/buongiorno/SKILL.md +151 -0
- package/bundled/council/SKILL.md +168 -0
- package/bundled/council/scripts/council.mjs +202 -0
- package/bundled/dreaming/SKILL.md +177 -0
- package/bundled/google-workspace/SKILL.md +229 -0
- package/bundled/google-workspace/scripts/auth.sh +87 -0
- package/bundled/google-workspace/scripts/calendar.sh +508 -0
- package/bundled/google-workspace/scripts/drive.sh +459 -0
- package/bundled/google-workspace/scripts/gmail.sh +452 -0
- package/bundled/humanizer/SKILL.md +488 -0
- package/bundled/librarian/SKILL.md +155 -0
- package/bundled/plasma/SKILL.md +1417 -0
- package/bundled/sera/SKILL.md +143 -0
- package/bundled/the-skill-guardian/SKILL.md +103 -0
- package/bundled/the-skill-guardian/scripts/scan.sh +314 -0
- package/bundled/unix-time/SKILL.md +58 -0
- package/bundled/wandering/SKILL.md +174 -0
- package/bundled/xai-search/SKILL.md +91 -0
- package/bundled/xai-search/scripts/search.sh +197 -0
- package/dist/a2ui/parser.d.ts +76 -0
- package/dist/a2ui/parser.js +1 -0
- package/dist/a2ui/types.d.ts +147 -0
- package/dist/a2ui/types.js +1 -0
- package/dist/a2ui/validator.d.ts +32 -0
- package/dist/a2ui/validator.js +1 -0
- package/dist/agent/agent-service.d.ts +17 -11
- package/dist/agent/agent-service.js +1 -1
- package/dist/agent/session-agent.d.ts +1 -1
- package/dist/agent/session-agent.js +1 -1
- package/dist/agent/session-error-handler.js +1 -1
- package/dist/commands/debuga2ui.d.ts +13 -0
- package/dist/commands/debuga2ui.js +1 -0
- package/dist/commands/debugdynamic.d.ts +13 -0
- package/dist/commands/debugdynamic.js +1 -0
- package/dist/commands/mcp.d.ts +6 -3
- package/dist/commands/mcp.js +1 -1
- package/dist/gateway/node-registry.d.ts +29 -1
- package/dist/gateway/node-registry.js +1 -1
- package/dist/installer/hera.js +1 -1
- package/dist/memory/concept-store.d.ts +109 -0
- package/dist/memory/concept-store.js +1 -0
- package/dist/nostromo/nostromo.js +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.js +1 -1
- package/dist/tools/a2ui-tools.d.ts +23 -0
- package/dist/tools/a2ui-tools.js +1 -0
- package/dist/tools/concept-tools.d.ts +3 -0
- package/dist/tools/concept-tools.js +1 -0
- package/dist/tools/dynamic-ui-tools.d.ts +25 -0
- package/dist/tools/dynamic-ui-tools.js +1 -0
- package/dist/tools/node-tools.js +1 -1
- package/dist/tools/plasma-client-tools.d.ts +28 -0
- package/dist/tools/plasma-client-tools.js +1 -0
- package/installationPkg/AGENTS.md +168 -22
- package/installationPkg/SOUL.md +56 -0
- package/installationPkg/TOOLS.md +126 -0
- package/installationPkg/USER.md +54 -1
- package/installationPkg/config.example.yaml +145 -34
- package/installationPkg/default-jobs.json +77 -0
- package/package.json +3 -2
|
@@ -0,0 +1,1417 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plasma
|
|
3
|
+
description: Event-sourced dynamic UI system for AI-generated interactive interfaces with HTML/CSS/JS
|
|
4
|
+
user-invocable: true
|
|
5
|
+
command-dispatch: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# PLASMA — Event-Sourced Dynamic UI System v2
|
|
9
|
+
|
|
10
|
+
**Plasma** is Hera's Dynamic UI system that allows you to generate and control fully custom HTML/CSS/JS interfaces on connected nodes (ElectroNode) with complete change history.
|
|
11
|
+
|
|
12
|
+
Unlike template-based UI systems, Plasma gives you complete freedom to create any interface using standard web technologies, external libraries (Three.js, D3.js, Chart.js), and your own custom code.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🤖 Agent Usage Guidelines
|
|
17
|
+
|
|
18
|
+
**IMPORTANT**: You (the agent) call these tools based on the user's natural language requests. The user NEVER types tool calls directly.
|
|
19
|
+
|
|
20
|
+
### Prerequisite: Verify Plasma-Capable Nodes
|
|
21
|
+
|
|
22
|
+
Before using ANY Plasma or Dynamic UI tool, you MUST call `dynamic_ui_list_nodes` to check if there are connected nodes with `plasma` capability. If no nodes are found, inform the user that they need to connect an ElectroNode with the Plasma capability enabled. Do NOT attempt to call `plasma_create`, `plasma_mutate`, `plasma_load`, or `dynamic_ui_render`/`dynamic_ui_update` without a Plasma-capable node.
|
|
23
|
+
|
|
24
|
+
### When to Use Which Tool
|
|
25
|
+
|
|
26
|
+
| User Says | You Do |
|
|
27
|
+
|-----------|--------|
|
|
28
|
+
| "Create a form for customers" | Call `plasma_create` with appropriate HTML/CSS/JS |
|
|
29
|
+
| "Build a dashboard" | Call `plasma_create` with dashboard layout |
|
|
30
|
+
| "Make a calculator app" | Call `plasma_create` with calculator UI |
|
|
31
|
+
| "Add validation to the form" | Call `plasma_mutate` with validation JS |
|
|
32
|
+
| "Change the button color to blue" | Call `plasma_mutate` with color change JS |
|
|
33
|
+
| "Fix the layout" | Call `plasma_mutate` with layout fix JS |
|
|
34
|
+
| "Add a cancel button" | Call `plasma_mutate` with new button JS |
|
|
35
|
+
| "Show me the customer form" | Call `plasma_load` with organism name |
|
|
36
|
+
| "Open the dashboard" | Call `plasma_load` with organism name |
|
|
37
|
+
| "Launch the calculator" | Call `plasma_load` with organism name |
|
|
38
|
+
| "What apps do I have?" | Call `plasma_list` |
|
|
39
|
+
| "List my organisms" | Call `plasma_list` |
|
|
40
|
+
| "Show my UIs" | Call `plasma_list` |
|
|
41
|
+
| "Delete the old form" | Call `plasma_delete` with organism name |
|
|
42
|
+
| "Remove the test app" | Call `plasma_delete` with organism name |
|
|
43
|
+
| "What's in the form right now?" | Call `dynamic_ui_query` with JS to read values |
|
|
44
|
+
| "Read the current data from the app" | Call `dynamic_ui_query` with JS expression |
|
|
45
|
+
|
|
46
|
+
### Discovery Flow
|
|
47
|
+
|
|
48
|
+
When the user asks to load/open an app but you're not sure which one:
|
|
49
|
+
|
|
50
|
+
1. Call `plasma_list` to see available organisms
|
|
51
|
+
2. Check folder names (BASIC convention: descriptive, a-zA-Z0-9_)
|
|
52
|
+
3. If uncertain, read manifest.yaml frontmatter (name + description)
|
|
53
|
+
4. Select best match or ask user to clarify
|
|
54
|
+
5. Call `plasma_load` with selected organism
|
|
55
|
+
|
|
56
|
+
**Example**:
|
|
57
|
+
```
|
|
58
|
+
User: "Show me the form"
|
|
59
|
+
You: [Call plasma_list]
|
|
60
|
+
[See: customer_form, invoice_form, contact_form]
|
|
61
|
+
[If ambiguous: ask "Which form? I have customer_form, invoice_form, and contact_form"]
|
|
62
|
+
[If clear match: call plasma_load({name: "customer_form", node_id: ...})]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Naming Organisms
|
|
66
|
+
|
|
67
|
+
When creating organisms, generate descriptive names following BASIC convention:
|
|
68
|
+
- **Format**: a-zA-Z0-9_ only, max 4 words
|
|
69
|
+
- **Style**: snake_case preferred
|
|
70
|
+
- **Descriptive**: Name reflects content
|
|
71
|
+
|
|
72
|
+
**Good names**: `customer_form`, `sales_dashboard`, `task_manager`, `chat_ui`
|
|
73
|
+
**Bad names**: `form1`, `app`, `my-form`, `very_long_descriptive_name_here`
|
|
74
|
+
|
|
75
|
+
### Response Style
|
|
76
|
+
|
|
77
|
+
After calling tools, respond to the user in natural language:
|
|
78
|
+
|
|
79
|
+
❌ **Bad**: "I called plasma_create with parameters {name: 'customer_form', ...}"
|
|
80
|
+
✅ **Good**: "I've created a customer entry form with name and email fields. It's ready to use!"
|
|
81
|
+
|
|
82
|
+
❌ **Bad**: "Tool returned: ✅ Organism 'customer_form' created successfully..."
|
|
83
|
+
✅ **Good**: "The form is now live on your ElectroNode. You can start entering customer data!"
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Core Concept v2: Event Sourcing
|
|
88
|
+
|
|
89
|
+
**Create once, mutate incrementally, replay from history.**
|
|
90
|
+
|
|
91
|
+
- **main.code**: Initial app version (YAML structured: HTML/CSS/JS/activities)
|
|
92
|
+
- **Mutations**: Incremental JavaScript updates (1_add_validation.code, 2_change_color.code, ...)
|
|
93
|
+
- **Snapshots**: Automatic mechanical snapshots every 20 mutations for fast loading
|
|
94
|
+
- **No reloads**: Maintain animations, timers, Three.js scenes, canvas state during mutations
|
|
95
|
+
|
|
96
|
+
This enables smooth, interactive experiences where the interface evolves based on user actions with complete auditability and rollback capability.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Organism Architecture
|
|
101
|
+
|
|
102
|
+
### File Structure
|
|
103
|
+
```
|
|
104
|
+
.plasma/organisms/customer_form/ ← BASIC naming (a-zA-Z0-9_, max 4 words)
|
|
105
|
+
├── manifest.yaml ← Metadata with frontmatter
|
|
106
|
+
├── main.code ← Initial version (YAML structured)
|
|
107
|
+
├── 1_add_validation.code ← Mutation #1 (JavaScript only)
|
|
108
|
+
├── 2_change_theme.code ← Mutation #2 (JavaScript only)
|
|
109
|
+
├── ...
|
|
110
|
+
├── 19_fix_layout.code
|
|
111
|
+
├── snapshot_20.code ← Automatic snapshot (every 20)
|
|
112
|
+
├── 20_add_button.code
|
|
113
|
+
├── ...
|
|
114
|
+
├── state.json ← Optional persistent state
|
|
115
|
+
└── .archive/ ← Backups (for LLM optimization)
|
|
116
|
+
└── backup_2026-02-21_14-30/
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### manifest.yaml
|
|
120
|
+
```yaml
|
|
121
|
+
---
|
|
122
|
+
name: customer_form
|
|
123
|
+
description: Add and manage customer records
|
|
124
|
+
created: 2026-02-21T10:00:00Z
|
|
125
|
+
updated: 2026-02-21T14:30:00Z
|
|
126
|
+
mutations: 25
|
|
127
|
+
tags: [forms, database, customers]
|
|
128
|
+
---
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### main.code (YAML structured)
|
|
132
|
+
```yaml
|
|
133
|
+
---
|
|
134
|
+
activities:
|
|
135
|
+
- id: btn-submit
|
|
136
|
+
type: button
|
|
137
|
+
context: {action: submit_customer}
|
|
138
|
+
- id: name-input
|
|
139
|
+
type: input
|
|
140
|
+
context: {field: name}
|
|
141
|
+
---
|
|
142
|
+
html: |
|
|
143
|
+
<div class="form">
|
|
144
|
+
<input id="name-input" placeholder="Name">
|
|
145
|
+
<input id="email-input" placeholder="Email">
|
|
146
|
+
<button id="btn-submit">Submit</button>
|
|
147
|
+
<div id="result"></div>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
css: |
|
|
151
|
+
.form { padding: 20px; max-width: 400px; }
|
|
152
|
+
button { background: #e91e8c; color: white; padding: 10px 20px; }
|
|
153
|
+
input { width: 100%; padding: 8px; margin: 4px 0; }
|
|
154
|
+
|
|
155
|
+
js: |
|
|
156
|
+
window.app = {
|
|
157
|
+
resetForm: function() {
|
|
158
|
+
document.getElementById('name-input').value = '';
|
|
159
|
+
document.getElementById('email-input').value = '';
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Mutation Files (JavaScript only)
|
|
165
|
+
```javascript
|
|
166
|
+
// 1_add_validation.code
|
|
167
|
+
window.app.validate = function() {
|
|
168
|
+
const name = document.getElementById('name-input').value;
|
|
169
|
+
if (!name) {
|
|
170
|
+
alert('Name is required');
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
document.getElementById('btn-submit').onclick = function() {
|
|
177
|
+
if (window.app.validate()) {
|
|
178
|
+
// Submit logic
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Activities & Feedback Loop
|
|
186
|
+
|
|
187
|
+
Activities are the bridge between the UI on the node and you (the agent). They let you receive user interactions and respond with feedback on the user's chat (Telegram, WhatsApp, WebChat, etc.).
|
|
188
|
+
|
|
189
|
+
### How Activities Work
|
|
190
|
+
|
|
191
|
+
When you define an `activities` array, the node client **auto-injects event listeners** matching element IDs in your HTML. You don't write addEventListener code — just declare the activity and ensure your HTML element has a matching `id`.
|
|
192
|
+
|
|
193
|
+
### Activity Types
|
|
194
|
+
|
|
195
|
+
| Type | Auto-Wired Event | What Gets Sent |
|
|
196
|
+
|------|------------------|----------------|
|
|
197
|
+
| `button` | `click` (with preventDefault) | `[Dynamic UI Action] click on #btn-submit context={...} provided={...}` |
|
|
198
|
+
| `input` | `change` | `[Dynamic UI Action] change on #email-input data={value: "user@mail.com"} context={...} provided={...}` |
|
|
199
|
+
| `canvas` | None (manual) | Whatever you send via `window.sendAction()` |
|
|
200
|
+
| `custom` | None (manual) | Whatever you send via `window.sendAction()` |
|
|
201
|
+
|
|
202
|
+
### The `dataProvider` Field
|
|
203
|
+
|
|
204
|
+
Use `dataProvider` to attach a **JS expression** to a `button` or `input` activity. The expression is evaluated **at event time** (when the user clicks/changes), and its result is included as `provided` in the action payload.
|
|
205
|
+
|
|
206
|
+
This replaces the clone-button hack for sending runtime data with auto-wired activities.
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
activities:
|
|
210
|
+
- id: btn-submit
|
|
211
|
+
type: button
|
|
212
|
+
context: {action: submit_form}
|
|
213
|
+
dataProvider: "({name: document.getElementById('name').value, email: document.getElementById('email').value})"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
When clicked, the agent receives:
|
|
217
|
+
```
|
|
218
|
+
[Dynamic UI Action] click on #btn-submit context={action: "submit_form"} provided={name: "Mario", email: "mario@email.com"}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Key points**:
|
|
222
|
+
- The expression is wrapped in `eval('(' + expr + ')')` — return an object literal by wrapping in parentheses
|
|
223
|
+
- Errors in `dataProvider` are caught and logged; `provided` will be `undefined` on failure
|
|
224
|
+
- Works on both `button` and `input` activity types
|
|
225
|
+
- `context` is static metadata (set at creation), `provided` is dynamic data (evaluated at event time)
|
|
226
|
+
|
|
227
|
+
**Examples**:
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
# Single field value
|
|
231
|
+
dataProvider: "document.getElementById('search').value"
|
|
232
|
+
|
|
233
|
+
# Multiple fields as object
|
|
234
|
+
dataProvider: "({name: document.getElementById('name').value, qty: parseInt(document.getElementById('qty').value)})"
|
|
235
|
+
|
|
236
|
+
# App state
|
|
237
|
+
dataProvider: "window.app.getFormData()"
|
|
238
|
+
|
|
239
|
+
# Computed value
|
|
240
|
+
dataProvider: "document.querySelectorAll('.item.selected').length"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**When to use `dataProvider` vs `custom` type**:
|
|
244
|
+
|
|
245
|
+
| Scenario | Use |
|
|
246
|
+
|----------|-----|
|
|
247
|
+
| Auto-wired button that also needs form values | `type: button` + `dataProvider` |
|
|
248
|
+
| Complex custom event handling (multiple events, debounce, etc.) | `type: custom` + `window.sendAction()` |
|
|
249
|
+
| Simple "notify agent" button with no runtime data | `type: button` (no dataProvider) |
|
|
250
|
+
|
|
251
|
+
### The Feedback Loop
|
|
252
|
+
|
|
253
|
+
This is the key flow — **your response to an action is delivered to the user's chat as text feedback**:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
1. User clicks a button on the PLASMA form (on node screen)
|
|
257
|
+
2. Node sends action → gateway → routed to your session
|
|
258
|
+
3. You receive: [Dynamic UI Action] click on #btn-submit data={...} context={action: "submit_form"}
|
|
259
|
+
4. You process the action (validate, save data, call APIs, etc.)
|
|
260
|
+
5. Your text response is sent to the user's chat (Telegram, WhatsApp, etc.)
|
|
261
|
+
6. Optionally, you also update the UI via dynamic_ui_update
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Example**: User fills a form and clicks "Submit" on the PLASMA surface displayed on their Mac:
|
|
265
|
+
- You receive: `[Dynamic UI Action] click on #btn-submit data={} context={action: "submit_customer"}`
|
|
266
|
+
- You read the form data from context or via a follow-up dynamic_ui_update that collects values
|
|
267
|
+
- You respond: "Customer Mario Rossi saved successfully!" → this text appears on Telegram
|
|
268
|
+
- You also call `dynamic_ui_update` to show a green checkmark on the form UI
|
|
269
|
+
|
|
270
|
+
### Collecting Form Data
|
|
271
|
+
|
|
272
|
+
For forms, you need the input values. Two approaches:
|
|
273
|
+
|
|
274
|
+
**A. Use `input` activities** — each field change sends the value automatically:
|
|
275
|
+
```yaml
|
|
276
|
+
activities:
|
|
277
|
+
- id: name-input
|
|
278
|
+
type: input
|
|
279
|
+
context: {field: name}
|
|
280
|
+
- id: email-input
|
|
281
|
+
type: input
|
|
282
|
+
context: {field: email}
|
|
283
|
+
- id: btn-submit
|
|
284
|
+
type: button
|
|
285
|
+
context: {action: submit_form}
|
|
286
|
+
```
|
|
287
|
+
You receive a `change` action for each field as the user types/blurs, then a `click` for submit.
|
|
288
|
+
|
|
289
|
+
**B. Use `custom` type + `window.sendAction()`** — collect all values at once on submit:
|
|
290
|
+
```javascript
|
|
291
|
+
// In your JS code:
|
|
292
|
+
document.getElementById('btn-submit').addEventListener('click', function(e) {
|
|
293
|
+
e.preventDefault();
|
|
294
|
+
window.sendAction('btn-submit', 'submit', {
|
|
295
|
+
name: document.getElementById('name-input').value,
|
|
296
|
+
email: document.getElementById('email-input').value,
|
|
297
|
+
phone: document.getElementById('phone-input').value
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
```yaml
|
|
302
|
+
activities:
|
|
303
|
+
- id: btn-submit
|
|
304
|
+
type: custom
|
|
305
|
+
context: {action: submit_form}
|
|
306
|
+
```
|
|
307
|
+
You receive a single action with all form data in `data={name: "...", email: "...", phone: "..."}`.
|
|
308
|
+
|
|
309
|
+
**Approach B is recommended for forms** — it's simpler and sends all data at once.
|
|
310
|
+
|
|
311
|
+
### window.sendAction(activityId, type, data)
|
|
312
|
+
|
|
313
|
+
For `canvas` and `custom` activity types, a global `window.sendAction()` function is available in the PLASMA surface. Call it from your JS to send any action back to the agent:
|
|
314
|
+
|
|
315
|
+
```javascript
|
|
316
|
+
// Send a click with form data
|
|
317
|
+
window.sendAction("btn-submit", "submit", {
|
|
318
|
+
name: document.getElementById("name").value,
|
|
319
|
+
email: document.getElementById("email").value
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Send a custom drawing event
|
|
323
|
+
window.sendAction("canvas-draw", "draw", { x: 100, y: 200, tool: "pen" });
|
|
324
|
+
|
|
325
|
+
// Send a selection event
|
|
326
|
+
window.sendAction("item-list", "select", { itemId: 42, label: "Product A" });
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### The context Field
|
|
330
|
+
|
|
331
|
+
Use `context` to pass static metadata that helps you identify what the action means. It is echoed back verbatim in the action message:
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
activities:
|
|
335
|
+
- id: btn-approve
|
|
336
|
+
type: button
|
|
337
|
+
context: {action: approve, target: invoice, invoiceId: "INV-2026-001"}
|
|
338
|
+
- id: btn-reject
|
|
339
|
+
type: button
|
|
340
|
+
context: {action: reject, target: invoice, invoiceId: "INV-2026-001"}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
When clicked, you receive:
|
|
344
|
+
```
|
|
345
|
+
[Dynamic UI Action] click on #btn-approve context={action: "approve", target: "invoice", invoiceId: "INV-2026-001"}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
This lets you handle different buttons in the same form without ambiguity.
|
|
349
|
+
|
|
350
|
+
### Serializing Runtime Data in Actions
|
|
351
|
+
|
|
352
|
+
**Critical pattern**: Auto-wired `button` activities only send the static `context` — they do NOT include dynamic data from the UI (form values, accumulated records, canvas state, etc.). The data lives in the browser and the agent never sees it unless you explicitly include it.
|
|
353
|
+
|
|
354
|
+
**The problem** — a button declared as `type: button` without `dataProvider`:
|
|
355
|
+
```yaml
|
|
356
|
+
activities:
|
|
357
|
+
- id: btn-send
|
|
358
|
+
type: button
|
|
359
|
+
context: {action: send_records}
|
|
360
|
+
```
|
|
361
|
+
The agent receives only: `[Dynamic UI Action] click on #btn-send context={action: "send_records"}` — no actual records data.
|
|
362
|
+
|
|
363
|
+
**Solution A (preferred)** — add `dataProvider` to the activity:
|
|
364
|
+
```yaml
|
|
365
|
+
activities:
|
|
366
|
+
- id: btn-send
|
|
367
|
+
type: button
|
|
368
|
+
context: {action: send_records}
|
|
369
|
+
dataProvider: "JSON.parse(JSON.stringify(window.app.records))"
|
|
370
|
+
```
|
|
371
|
+
The agent receives: `[Dynamic UI Action] click on #btn-send context={action: "send_records"} provided=[{nome: "Mario", ...}, ...]`
|
|
372
|
+
|
|
373
|
+
**Solution B** — declare the button as `type: custom` and call `window.sendAction()` with serialized data:
|
|
374
|
+
|
|
375
|
+
```yaml
|
|
376
|
+
activities:
|
|
377
|
+
- id: btn-send
|
|
378
|
+
type: custom
|
|
379
|
+
context: {action: send_records}
|
|
380
|
+
```
|
|
381
|
+
```javascript
|
|
382
|
+
// In your JS: handle the click yourself and include runtime data
|
|
383
|
+
document.getElementById('btn-send').addEventListener('click', function(e) {
|
|
384
|
+
e.preventDefault();
|
|
385
|
+
if (!window.app.records.length) return;
|
|
386
|
+
|
|
387
|
+
// Serialize runtime data into the action payload
|
|
388
|
+
window.sendAction('btn-send', 'click', {
|
|
389
|
+
context: { action: 'send_records' },
|
|
390
|
+
records: JSON.parse(JSON.stringify(window.app.records))
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
The agent now receives:
|
|
396
|
+
```
|
|
397
|
+
[Dynamic UI Action] click on #btn-send data={context: {action: "send_records"}, records: [{nome: "Mario", cognome: "Rossi", email: "mario@email.com"}, ...]}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### Retrofitting an existing `type: button` activity
|
|
401
|
+
|
|
402
|
+
If a button was already declared as `type: button`, the node has auto-wired a click listener on it. To replace it with a custom handler that sends data, clone the element to strip all listeners:
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
// Remove auto-wired listener via clone trick
|
|
406
|
+
var oldBtn = document.getElementById('btn-send');
|
|
407
|
+
var newBtn = oldBtn.cloneNode(true);
|
|
408
|
+
oldBtn.parentNode.replaceChild(newBtn, oldBtn);
|
|
409
|
+
|
|
410
|
+
// Attach custom handler with data serialization
|
|
411
|
+
newBtn.addEventListener('click', function(e) {
|
|
412
|
+
e.preventDefault();
|
|
413
|
+
window.sendAction('btn-send', 'click', {
|
|
414
|
+
records: JSON.parse(JSON.stringify(window.app.records))
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
This is a valid workaround via `plasma_mutate`, but **prefer declaring `type: custom` from the start** for any button that needs to send runtime data.
|
|
420
|
+
|
|
421
|
+
#### Which type to use — decision guide
|
|
422
|
+
|
|
423
|
+
| Scenario | Activity Type | Why |
|
|
424
|
+
|----------|--------------|-----|
|
|
425
|
+
| Button that just notifies the agent ("user clicked X") | `button` | Auto-wired, no JS needed |
|
|
426
|
+
| Button that sends form values or simple computed data | `button` + `dataProvider` | Auto-wired with runtime data, no custom JS |
|
|
427
|
+
| Button with complex event handling (debounce, multi-event, etc.) | `custom` | Full control via `window.sendAction()` |
|
|
428
|
+
| Button that only does client-side work (no agent involvement) | Don't declare as activity | No activity needed — just use normal JS |
|
|
429
|
+
| Input field whose value changes matter to the agent | `input` | Auto-sends value on change |
|
|
430
|
+
| Complex interactive element (canvas, drag-drop, etc.) | `custom` | Full control via `window.sendAction()` |
|
|
431
|
+
|
|
432
|
+
#### Real-world example — multi-record form
|
|
433
|
+
|
|
434
|
+
This pattern was used in the Anagrafica organism: a form where the user adds multiple records client-side, then sends them all to the agent at once.
|
|
435
|
+
|
|
436
|
+
```yaml
|
|
437
|
+
activities:
|
|
438
|
+
- id: btn-add
|
|
439
|
+
type: button
|
|
440
|
+
context: {action: add_record} # notifies agent a record was added
|
|
441
|
+
- id: btn-export
|
|
442
|
+
type: button
|
|
443
|
+
context: {action: export_csv} # notifies agent of CSV export
|
|
444
|
+
- id: btn-send
|
|
445
|
+
type: custom # custom: sends serialized records
|
|
446
|
+
context: {action: send_records}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
```javascript
|
|
450
|
+
// btn-add and btn-export: client-side handlers (no sendAction needed)
|
|
451
|
+
document.getElementById('btn-add').addEventListener('click', function() {
|
|
452
|
+
window.app.addRecord(); // client-side only
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// btn-send: serialize and send all records to the agent
|
|
456
|
+
document.getElementById('btn-send').addEventListener('click', function(e) {
|
|
457
|
+
e.preventDefault();
|
|
458
|
+
var records = window.app.records;
|
|
459
|
+
if (!records.length) { alert('No records'); return; }
|
|
460
|
+
|
|
461
|
+
// Visual feedback while sending
|
|
462
|
+
this.innerHTML = '💎 Invio...';
|
|
463
|
+
this.style.opacity = '0.7';
|
|
464
|
+
var btn = this;
|
|
465
|
+
|
|
466
|
+
window.sendAction('btn-send', 'click', {
|
|
467
|
+
context: { action: 'send_records' },
|
|
468
|
+
records: JSON.parse(JSON.stringify(records))
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
setTimeout(function() {
|
|
472
|
+
btn.innerHTML = '💎 Inviato ✓';
|
|
473
|
+
setTimeout(function() { btn.innerHTML = '💎 Invia'; btn.style.opacity = '1'; }, 2000);
|
|
474
|
+
}, 400);
|
|
475
|
+
});
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The agent receives all records and can respond on the user's chat:
|
|
479
|
+
```
|
|
480
|
+
[Dynamic UI Action] click on #btn-send data={context: {action: "send_records"}, records: [{nome: "Mario", cognome: "Rossi", ...}, {nome: "Giulia", cognome: "Bianchi", ...}]}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Agent responds on Telegram: "Received 2 records: Mario Rossi, Giulia Bianchi. Saved to database."
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
## Available Tools
|
|
488
|
+
|
|
489
|
+
### 1. `plasma_create`
|
|
490
|
+
Create a new organism (app).
|
|
491
|
+
|
|
492
|
+
**When to use**: User requests a new UI application (form, dashboard, calculator, etc.)
|
|
493
|
+
|
|
494
|
+
**User says**: "Create a form", "Build a dashboard", "Make a calculator"
|
|
495
|
+
|
|
496
|
+
**You do**: Call this tool with appropriate HTML/CSS/JS
|
|
497
|
+
|
|
498
|
+
**Parameters**:
|
|
499
|
+
- `name`: Organism name (a-zA-Z0-9_, max 4 words, descriptive)
|
|
500
|
+
- `description`: What this organism does
|
|
501
|
+
- `html`: HTML content
|
|
502
|
+
- `css`: CSS styles (optional)
|
|
503
|
+
- `js`: JavaScript code (optional)
|
|
504
|
+
- `activities`: Array of interactive elements
|
|
505
|
+
- `tags`: Category tags (optional)
|
|
506
|
+
|
|
507
|
+
**Naming convention**: Use descriptive names following BASIC rules
|
|
508
|
+
- ✅ Good: `customer_form`, `sales_dashboard`, `task_manager`, `chat_ui`
|
|
509
|
+
- ❌ Bad: `form1`, `my-app`, `SuperCoolApp123`, `very_long_name_with_many_words`
|
|
510
|
+
|
|
511
|
+
**Example conversation**:
|
|
512
|
+
```
|
|
513
|
+
User: "Create a customer entry form with name and email fields"
|
|
514
|
+
|
|
515
|
+
You: [Call plasma_create tool internally]
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
**Tool call** (you make this call, not the user):
|
|
519
|
+
```javascript
|
|
520
|
+
plasma_create({
|
|
521
|
+
name: "customer_form",
|
|
522
|
+
description: "Entry form for customer records",
|
|
523
|
+
html: `
|
|
524
|
+
<div class="form">
|
|
525
|
+
<input id="name" placeholder="Name">
|
|
526
|
+
<input id="email" placeholder="Email">
|
|
527
|
+
<button id="btn-submit">Submit</button>
|
|
528
|
+
</div>
|
|
529
|
+
`,
|
|
530
|
+
css: `
|
|
531
|
+
.form { padding: 20px; }
|
|
532
|
+
button { background: #e91e8c; color: white; }
|
|
533
|
+
`,
|
|
534
|
+
js: `
|
|
535
|
+
window.app = {
|
|
536
|
+
submitForm: function() { ... }
|
|
537
|
+
};
|
|
538
|
+
`,
|
|
539
|
+
activities: [
|
|
540
|
+
{id: "btn-submit", type: "button", context: {action: "submit"}}
|
|
541
|
+
],
|
|
542
|
+
tags: ["forms", "database"]
|
|
543
|
+
})
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**Tool result** (internal):
|
|
547
|
+
```
|
|
548
|
+
✅ Organism 'customer_form' created successfully
|
|
549
|
+
Saved to: .plasma/organisms/customer_form/
|
|
550
|
+
Files: manifest.yaml, main.code (350 bytes)
|
|
551
|
+
Activities: 1 (#btn-submit)
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
**You respond to user**:
|
|
555
|
+
"I've created a customer entry form with name and email fields. The form has a submit button that will send you the data when clicked. Would you like me to load it on your ElectroNode now?"
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
### 2. `plasma_mutate`
|
|
560
|
+
Apply an incremental mutation to an existing organism.
|
|
561
|
+
|
|
562
|
+
**When to use**: User requests changes to an existing app
|
|
563
|
+
|
|
564
|
+
**User says**:
|
|
565
|
+
- "Add validation to the form"
|
|
566
|
+
- "Change the button color to blue"
|
|
567
|
+
- "Fix the layout"
|
|
568
|
+
- "Add a cancel button"
|
|
569
|
+
|
|
570
|
+
**You do**: Call this tool with JavaScript that implements the change
|
|
571
|
+
|
|
572
|
+
**Parameters**:
|
|
573
|
+
- `name`: Organism name
|
|
574
|
+
- `mutation_name`: Descriptive name for mutation (a-zA-Z0-9_)
|
|
575
|
+
- `js`: JavaScript code for the mutation
|
|
576
|
+
- `node_id`: Optional node ID to apply immediately
|
|
577
|
+
|
|
578
|
+
**Mutation naming**: Be descriptive
|
|
579
|
+
- ✅ Good: `add_validation`, `change_theme_blue`, `fix_button_layout`
|
|
580
|
+
- ❌ Bad: `update`, `fix`, `change`
|
|
581
|
+
|
|
582
|
+
**Example conversation**:
|
|
583
|
+
```
|
|
584
|
+
User: "Add validation to the customer form - name should be required"
|
|
585
|
+
|
|
586
|
+
You: [Call plasma_mutate tool internally]
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Tool call** (you make this call):
|
|
590
|
+
```javascript
|
|
591
|
+
plasma_mutate({
|
|
592
|
+
name: "customer_form",
|
|
593
|
+
mutation_name: "add_validation",
|
|
594
|
+
js: `
|
|
595
|
+
window.app.validate = function() {
|
|
596
|
+
const name = document.getElementById('name').value;
|
|
597
|
+
if (!name) {
|
|
598
|
+
alert('Name required');
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
return true;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
document.getElementById('btn-submit').onclick = function() {
|
|
605
|
+
if (window.app.validate()) {
|
|
606
|
+
window.app.submitForm();
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
`,
|
|
610
|
+
node_id: "electr-abc123" // Apply immediately
|
|
611
|
+
})
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Tool result** (internal):
|
|
615
|
+
```
|
|
616
|
+
✅ Mutation applied: 1_add_validation.code
|
|
617
|
+
Total mutations: 1
|
|
618
|
+
Applied to node electr-abc123
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
**You respond to user**:
|
|
622
|
+
"I've added name validation to the form. It will now show an alert if the user tries to submit without entering a name. The validation is active on your current session and will persist for future loads."
|
|
623
|
+
|
|
624
|
+
**Automatic snapshots**: Every 20 mutations, a snapshot is created automatically:
|
|
625
|
+
```
|
|
626
|
+
✅ Mutation applied to organism 'customer_form'
|
|
627
|
+
|
|
628
|
+
Mutation: 20_optimize_render.code
|
|
629
|
+
Total mutations: 20
|
|
630
|
+
|
|
631
|
+
📸 Snapshot created (snapshot_20.code) for fast loading
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
### 3. `plasma_load`
|
|
637
|
+
Load a saved organism and render it on a node.
|
|
638
|
+
|
|
639
|
+
**When to use**: User wants to view/open an existing app
|
|
640
|
+
|
|
641
|
+
**User says**:
|
|
642
|
+
- "Show me the customer form"
|
|
643
|
+
- "Open the sales dashboard"
|
|
644
|
+
- "Launch the calculator"
|
|
645
|
+
- "Load my task manager"
|
|
646
|
+
|
|
647
|
+
**You do**:
|
|
648
|
+
1. If organism name is ambiguous, call `plasma_list` first
|
|
649
|
+
2. Call `plasma_load` with the organism name and node_id
|
|
650
|
+
|
|
651
|
+
**Parameters**:
|
|
652
|
+
- `name`: Organism name
|
|
653
|
+
- `node_id`: Target node ID
|
|
654
|
+
|
|
655
|
+
**Loading strategy** (automatic, transparent):
|
|
656
|
+
1. Find best snapshot (if available) → fast load
|
|
657
|
+
2. Load main.code if no snapshot
|
|
658
|
+
3. Apply remaining mutations in sequence
|
|
659
|
+
4. Render on node
|
|
660
|
+
|
|
661
|
+
**Example conversation**:
|
|
662
|
+
```
|
|
663
|
+
User: "Show me the customer form"
|
|
664
|
+
|
|
665
|
+
You: [Call dynamic_ui_list_nodes to get node_id]
|
|
666
|
+
[Call plasma_load with organism name and node_id]
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
**Tool call** (you make this call):
|
|
670
|
+
```javascript
|
|
671
|
+
plasma_load({
|
|
672
|
+
name: "customer_form",
|
|
673
|
+
node_id: "electr-abc123"
|
|
674
|
+
})
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
**Tool result** (internal):
|
|
678
|
+
```
|
|
679
|
+
✅ Loaded and rendered on ElectroNode-darwin
|
|
680
|
+
Total mutations: 25
|
|
681
|
+
Loaded from: snapshot_20.code
|
|
682
|
+
Applied 5 additional mutations
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
**You respond to user**:
|
|
686
|
+
"The customer form is now displayed on your ElectroNode. It includes all 25 updates you've made, including the validation and styling changes. Ready to use!"
|
|
687
|
+
|
|
688
|
+
**Performance**: With snapshots, loading 100+ mutations takes ~100ms vs ~2s without.
|
|
689
|
+
|
|
690
|
+
---
|
|
691
|
+
|
|
692
|
+
### 4. `plasma_list`
|
|
693
|
+
List all saved organisms.
|
|
694
|
+
|
|
695
|
+
**When to use**: User asks what apps/organisms are available, or when you need to discover organism names
|
|
696
|
+
|
|
697
|
+
**User says**:
|
|
698
|
+
- "What apps do I have?"
|
|
699
|
+
- "List my organisms"
|
|
700
|
+
- "Show me my UIs"
|
|
701
|
+
|
|
702
|
+
**You do**: Call this tool (no parameters needed)
|
|
703
|
+
|
|
704
|
+
**Example conversation**:
|
|
705
|
+
```
|
|
706
|
+
User: "What apps do I have?"
|
|
707
|
+
|
|
708
|
+
You: [Call plasma_list]
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
**Tool result** (internal):
|
|
712
|
+
```
|
|
713
|
+
5 PLASMA organism(s) available:
|
|
714
|
+
|
|
715
|
+
- customer_form (customer_form) — 25 mutations [forms, database]
|
|
716
|
+
Entry form for customer records
|
|
717
|
+
|
|
718
|
+
- sales_dashboard (sales_dashboard) — 12 mutations [dashboard, charts]
|
|
719
|
+
Real-time sales metrics and visualization
|
|
720
|
+
|
|
721
|
+
- task_manager (task_manager) — 8 mutations [productivity]
|
|
722
|
+
Simple task list with drag-and-drop
|
|
723
|
+
|
|
724
|
+
- chat_ui (chat_ui) — 45 mutations [communication]
|
|
725
|
+
Real-time chat interface with typing indicators
|
|
726
|
+
|
|
727
|
+
- invoice_gen (invoice_gen) — 3 mutations [pdf, forms]
|
|
728
|
+
Generate PDF invoices from form data
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**You respond to user**:
|
|
732
|
+
"You have 5 Plasma apps:
|
|
733
|
+
1. **Customer Form** (25 updates) - Entry form for customer records
|
|
734
|
+
2. **Sales Dashboard** (12 updates) - Real-time sales metrics
|
|
735
|
+
3. **Task Manager** (8 updates) - Simple task list with drag-and-drop
|
|
736
|
+
4. **Chat UI** (45 updates) - Real-time chat interface
|
|
737
|
+
5. **Invoice Generator** (3 updates) - Generate PDF invoices
|
|
738
|
+
|
|
739
|
+
Which one would you like to open?"
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
### 5. `plasma_delete`
|
|
744
|
+
Delete an organism permanently.
|
|
745
|
+
|
|
746
|
+
**When to use**: User wants to remove an app they no longer need
|
|
747
|
+
|
|
748
|
+
**User says**:
|
|
749
|
+
- "Delete the old form"
|
|
750
|
+
- "Remove the test app"
|
|
751
|
+
- "I don't need the calculator anymore"
|
|
752
|
+
|
|
753
|
+
**You do**: Call this tool with the organism name
|
|
754
|
+
|
|
755
|
+
**Important**: Always confirm with the user before deleting unless they explicitly say to delete
|
|
756
|
+
|
|
757
|
+
**Example conversation**:
|
|
758
|
+
```
|
|
759
|
+
User: "Delete the test form"
|
|
760
|
+
|
|
761
|
+
You: "Are you sure you want to delete 'test_form'? This will remove it permanently along with all its mutations."
|
|
762
|
+
|
|
763
|
+
User: "Yes"
|
|
764
|
+
|
|
765
|
+
You: [Call plasma_delete]
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**Tool call** (you make this call):
|
|
769
|
+
```javascript
|
|
770
|
+
plasma_delete({name: "test_form"})
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Tool result** (internal):
|
|
774
|
+
```
|
|
775
|
+
✅ Organism 'test_form' deleted permanently
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
**You respond to user**:
|
|
779
|
+
"The test form has been deleted permanently. All mutations and history have been removed."
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
### 6. `dynamic_ui_query`
|
|
784
|
+
Execute JavaScript in the DynamicUI surface on a node and return the result.
|
|
785
|
+
|
|
786
|
+
**When to use**: You need to read runtime state from the UI — form values, accumulated data, DOM state, computed values — without the user triggering an action.
|
|
787
|
+
|
|
788
|
+
**User says**:
|
|
789
|
+
- "What's in the form right now?"
|
|
790
|
+
- "How many records have been added?"
|
|
791
|
+
- "Read the current settings from the app"
|
|
792
|
+
|
|
793
|
+
**You do**: Call this tool with a JS expression; the result comes back to you directly.
|
|
794
|
+
|
|
795
|
+
**Parameters**:
|
|
796
|
+
- `node_id`: Target node ID
|
|
797
|
+
- `js`: JavaScript expression to evaluate (result is returned)
|
|
798
|
+
|
|
799
|
+
**Example conversation**:
|
|
800
|
+
```
|
|
801
|
+
User: "What data is in the form?"
|
|
802
|
+
|
|
803
|
+
You: [Call dynamic_ui_query with JS to read form fields]
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
**Tool call** (you make this call):
|
|
807
|
+
```javascript
|
|
808
|
+
dynamic_ui_query({
|
|
809
|
+
node_id: "osxnode-abc123",
|
|
810
|
+
js: "({name: document.getElementById('name').value, email: document.getElementById('email').value})"
|
|
811
|
+
})
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
**Tool result** (internal):
|
|
815
|
+
```
|
|
816
|
+
Result: {"name": "Mario Rossi", "email": "mario@email.com"}
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
**You respond to user**:
|
|
820
|
+
"The form currently has: Name = Mario Rossi, Email = mario@email.com"
|
|
821
|
+
|
|
822
|
+
**More examples**:
|
|
823
|
+
```javascript
|
|
824
|
+
// Read app state
|
|
825
|
+
dynamic_ui_query({ node_id: "...", js: "JSON.stringify(window.app.records)" })
|
|
826
|
+
|
|
827
|
+
// Count elements
|
|
828
|
+
dynamic_ui_query({ node_id: "...", js: "document.querySelectorAll('.item').length" })
|
|
829
|
+
|
|
830
|
+
// Check visibility
|
|
831
|
+
dynamic_ui_query({ node_id: "...", js: "document.getElementById('panel').style.display !== 'none'" })
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
**`dataProvider` vs `dynamic_ui_query`**:
|
|
835
|
+
- `dataProvider`: Evaluated **at event time** (user clicks button) — data travels with the action
|
|
836
|
+
- `dynamic_ui_query`: Evaluated **on demand** (agent calls it) — agent proactively reads UI state
|
|
837
|
+
|
|
838
|
+
---
|
|
839
|
+
|
|
840
|
+
## Best Practices
|
|
841
|
+
|
|
842
|
+
### 0. Use Bootstrap for Forms and Form-Based Applications
|
|
843
|
+
|
|
844
|
+
**When the user requests a form, a form-based application, or any UI that is primarily form-driven** (contact forms, registration, data entry, surveys, settings panels, etc.), **always use Bootstrap** as the CSS framework.
|
|
845
|
+
|
|
846
|
+
**Why**: Bootstrap provides consistent, responsive, accessible form layouts out of the box — radio buttons, checkboxes, selects, validation states, input groups, floating labels, and grid alignment all work correctly without custom CSS.
|
|
847
|
+
|
|
848
|
+
**How**: Load Bootstrap from CDN in the `css` field or via `<link>` in the HTML:
|
|
849
|
+
|
|
850
|
+
```html
|
|
851
|
+
<!-- In the html field of plasma_create -->
|
|
852
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Or load both CSS + JS (for components like modals, tooltips, accordions):
|
|
856
|
+
|
|
857
|
+
```html
|
|
858
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
859
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
**Example form with Bootstrap**:
|
|
863
|
+
```javascript
|
|
864
|
+
plasma_create({
|
|
865
|
+
name: "customer_form",
|
|
866
|
+
description: "Customer registration form",
|
|
867
|
+
html: `
|
|
868
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
869
|
+
<div class="container py-4" style="max-width: 600px;">
|
|
870
|
+
<h3 class="mb-4">New Customer</h3>
|
|
871
|
+
<form id="customer-form">
|
|
872
|
+
<div class="mb-3">
|
|
873
|
+
<label for="name" class="form-label">Full Name</label>
|
|
874
|
+
<input type="text" class="form-control" id="name" required>
|
|
875
|
+
</div>
|
|
876
|
+
<div class="mb-3">
|
|
877
|
+
<label for="email" class="form-label">Email</label>
|
|
878
|
+
<input type="email" class="form-control" id="email" required>
|
|
879
|
+
</div>
|
|
880
|
+
<div class="mb-3">
|
|
881
|
+
<label for="phone" class="form-label">Phone</label>
|
|
882
|
+
<input type="tel" class="form-control" id="phone">
|
|
883
|
+
</div>
|
|
884
|
+
<button type="submit" id="btn-submit" class="btn btn-primary">Submit</button>
|
|
885
|
+
</form>
|
|
886
|
+
<div id="status" class="mt-3"></div>
|
|
887
|
+
</div>
|
|
888
|
+
`,
|
|
889
|
+
activities: [
|
|
890
|
+
{id: "btn-submit", type: "button", context: {action: "submit_customer"}}
|
|
891
|
+
],
|
|
892
|
+
tags: ["forms", "customers"]
|
|
893
|
+
})
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
**When NOT to use Bootstrap**: Dashboards, 3D visualizations, games, custom creative UIs, canvas-based apps — use custom CSS or other frameworks as appropriate.
|
|
897
|
+
|
|
898
|
+
### 1. Always Expose Global State in main.code
|
|
899
|
+
|
|
900
|
+
**Why**: Mutations need access to app state
|
|
901
|
+
|
|
902
|
+
```javascript
|
|
903
|
+
// GOOD: In main.code js section
|
|
904
|
+
window.app = {
|
|
905
|
+
scene: scene,
|
|
906
|
+
cube: cube,
|
|
907
|
+
camera: camera,
|
|
908
|
+
submitForm: function() { ... }
|
|
909
|
+
};
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
```javascript
|
|
913
|
+
// BAD: Everything local
|
|
914
|
+
const scene = new THREE.Scene(); // Can't access in mutations!
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
### 2. Descriptive Mutation Names
|
|
918
|
+
|
|
919
|
+
```javascript
|
|
920
|
+
// GOOD
|
|
921
|
+
plasma_mutate({
|
|
922
|
+
name: "customer_form",
|
|
923
|
+
mutation_name: "add_email_validation",
|
|
924
|
+
js: ...
|
|
925
|
+
})
|
|
926
|
+
|
|
927
|
+
// BAD
|
|
928
|
+
plasma_mutate({
|
|
929
|
+
name: "customer_form",
|
|
930
|
+
mutation_name: "fix", // Too vague
|
|
931
|
+
js: ...
|
|
932
|
+
})
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
### 3. Small, Focused Mutations
|
|
936
|
+
|
|
937
|
+
```javascript
|
|
938
|
+
// GOOD: One concern per mutation
|
|
939
|
+
1_add_validation.code → Validation logic
|
|
940
|
+
2_change_button_color.code → Visual change
|
|
941
|
+
3_add_cancel_button.code → New feature
|
|
942
|
+
|
|
943
|
+
// BAD: Giant mutation doing everything
|
|
944
|
+
1_big_update.code → Changes 10 different things
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
### 4. Use node_id for Immediate Feedback
|
|
948
|
+
|
|
949
|
+
```javascript
|
|
950
|
+
// If user is actively using the app, apply mutation immediately
|
|
951
|
+
plasma_mutate({
|
|
952
|
+
name: "customer_form",
|
|
953
|
+
mutation_name: "change_theme_dark",
|
|
954
|
+
js: `
|
|
955
|
+
document.body.style.background = '#1a1a1a';
|
|
956
|
+
document.body.style.color = '#ffffff';
|
|
957
|
+
`,
|
|
958
|
+
node_id: currentNodeId // ← Apply now
|
|
959
|
+
})
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### 5. Load Libraries from CDN in main.code
|
|
963
|
+
|
|
964
|
+
```javascript
|
|
965
|
+
// In main.code js section:
|
|
966
|
+
const script = document.createElement('script');
|
|
967
|
+
script.src = 'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js';
|
|
968
|
+
script.onload = function() {
|
|
969
|
+
// Three.js loaded, create scene
|
|
970
|
+
const scene = new THREE.Scene();
|
|
971
|
+
// ...
|
|
972
|
+
window.app = { scene, ... };
|
|
973
|
+
};
|
|
974
|
+
document.head.appendChild(script);
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
## Client-Side vs Server-Side Logic
|
|
980
|
+
|
|
981
|
+
**Golden Rule**: Handle as much as possible client-side. Only involve the server for actions that REQUIRE server capabilities.
|
|
982
|
+
|
|
983
|
+
### Handle CLIENT-SIDE (via mutations):
|
|
984
|
+
- ✅ Visual changes (color, size, position, rotation)
|
|
985
|
+
- ✅ Animations and transitions
|
|
986
|
+
- ✅ Form validation (basic)
|
|
987
|
+
- ✅ UI state changes (show/hide, enable/disable)
|
|
988
|
+
- ✅ Local filtering/sorting
|
|
989
|
+
- ✅ Canvas drawing
|
|
990
|
+
- ✅ Three.js scene manipulation
|
|
991
|
+
|
|
992
|
+
**Why**: Instant response, no network latency, preserves state.
|
|
993
|
+
|
|
994
|
+
### Handle SERVER-SIDE (via activity → agent responds):
|
|
995
|
+
- 📡 Data fetching from external APIs
|
|
996
|
+
- 📡 Database operations
|
|
997
|
+
- 📡 Complex computations
|
|
998
|
+
- 📡 Form submissions requiring persistence
|
|
999
|
+
- 📡 Authentication/authorization
|
|
1000
|
+
- 📡 File uploads
|
|
1001
|
+
- 📡 IoT device control
|
|
1002
|
+
|
|
1003
|
+
**Why**: Requires server capabilities you can't provide in browser.
|
|
1004
|
+
|
|
1005
|
+
**Important**: For server-side operations (database, API calls, file processing), use existing MCP tools that the agent already has access to. The agent can invoke any available tool in response to UI actions.
|
|
1006
|
+
|
|
1007
|
+
### Example: Color Picker
|
|
1008
|
+
|
|
1009
|
+
**USER REQUEST**: "I want to change the cube color"
|
|
1010
|
+
|
|
1011
|
+
**CLIENT-SIDE APPROACH** (PREFERRED):
|
|
1012
|
+
```javascript
|
|
1013
|
+
// Initial render includes color picker activity
|
|
1014
|
+
activities: [{id: "color-picker", type: "input", context: {}}]
|
|
1015
|
+
|
|
1016
|
+
// User changes color → you receive action
|
|
1017
|
+
// [Dynamic UI Action] change on #color-picker data={value: "#ff0000"}
|
|
1018
|
+
|
|
1019
|
+
// Response: Direct update (instant)
|
|
1020
|
+
dynamic_ui_update({
|
|
1021
|
+
node_id: "...",
|
|
1022
|
+
js: `window.app.cube.material.color.set('${value}');`
|
|
1023
|
+
})
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
**Why this works**: Color change is purely visual, no server needed.
|
|
1027
|
+
|
|
1028
|
+
### Example: Weather Data Form
|
|
1029
|
+
|
|
1030
|
+
**USER REQUEST**: "Show me a form to get weather for a city"
|
|
1031
|
+
|
|
1032
|
+
**HYBRID APPROACH**:
|
|
1033
|
+
```javascript
|
|
1034
|
+
// 1. Initial render with form (client-side)
|
|
1035
|
+
dynamic_ui_render({
|
|
1036
|
+
html: `
|
|
1037
|
+
<input id="city-input" type="text" placeholder="Enter city">
|
|
1038
|
+
<button id="btn-fetch">Get Weather</button>
|
|
1039
|
+
<div id="weather-result"></div>
|
|
1040
|
+
`,
|
|
1041
|
+
activities: [
|
|
1042
|
+
{id: "btn-fetch", type: "button", context: {action: "fetch_weather"}}
|
|
1043
|
+
]
|
|
1044
|
+
})
|
|
1045
|
+
|
|
1046
|
+
// 2. User clicks button → you receive:
|
|
1047
|
+
// [Dynamic UI Action] click on #btn-fetch
|
|
1048
|
+
|
|
1049
|
+
// 3. You fetch weather from server API (server-side)
|
|
1050
|
+
const weather = await fetchWeatherAPI(city);
|
|
1051
|
+
|
|
1052
|
+
// 4. Update UI with result (client-side)
|
|
1053
|
+
dynamic_ui_update({
|
|
1054
|
+
js: `
|
|
1055
|
+
document.getElementById('weather-result').innerHTML = '
|
|
1056
|
+
<h3>${weather.city}</h3>
|
|
1057
|
+
<p>Temperature: ${weather.temp}°C</p>
|
|
1058
|
+
<p>Conditions: ${weather.conditions}</p>
|
|
1059
|
+
';
|
|
1060
|
+
`
|
|
1061
|
+
})
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
**Why hybrid**: Form display is client-side, but weather API requires server.
|
|
1065
|
+
|
|
1066
|
+
---
|
|
1067
|
+
|
|
1068
|
+
## When to Use PLASMA vs Dynamic UI Directly
|
|
1069
|
+
|
|
1070
|
+
**Use `plasma_create` + `plasma_load` when**:
|
|
1071
|
+
- UI will be reused multiple times
|
|
1072
|
+
- App should load instantly from disk
|
|
1073
|
+
- Want complete change history (event sourcing)
|
|
1074
|
+
- Building library of reusable apps
|
|
1075
|
+
|
|
1076
|
+
**Use `dynamic_ui_render` directly when**:
|
|
1077
|
+
- One-off visualization
|
|
1078
|
+
- Highly dynamic content that changes every time
|
|
1079
|
+
- Prototyping/testing
|
|
1080
|
+
|
|
1081
|
+
**Typical workflow**:
|
|
1082
|
+
```javascript
|
|
1083
|
+
// 1. Create organism (saved to disk)
|
|
1084
|
+
plasma_create({ name: "my_app", ... })
|
|
1085
|
+
|
|
1086
|
+
// 2. Evolve it incrementally
|
|
1087
|
+
plasma_mutate({ name: "my_app", mutation_name: "add_feature", js: "..." })
|
|
1088
|
+
|
|
1089
|
+
// 3. Load it any time
|
|
1090
|
+
plasma_load({ name: "my_app", node_id: nodeId })
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
---
|
|
1094
|
+
|
|
1095
|
+
## Common Patterns
|
|
1096
|
+
|
|
1097
|
+
### Pattern 1: Form with Progressive Enhancement
|
|
1098
|
+
|
|
1099
|
+
```javascript
|
|
1100
|
+
// 1. Create base form
|
|
1101
|
+
plasma_create({
|
|
1102
|
+
name: "contact_form",
|
|
1103
|
+
description: "Contact form with progressive enhancements",
|
|
1104
|
+
html: `
|
|
1105
|
+
<div class="form">
|
|
1106
|
+
<input id="name" placeholder="Name">
|
|
1107
|
+
<input id="email" placeholder="Email">
|
|
1108
|
+
<textarea id="message" placeholder="Message"></textarea>
|
|
1109
|
+
<button id="btn-submit">Submit</button>
|
|
1110
|
+
<div id="status"></div>
|
|
1111
|
+
</div>
|
|
1112
|
+
`,
|
|
1113
|
+
activities: [
|
|
1114
|
+
{id: "btn-submit", type: "button", context: {action: "submit"}}
|
|
1115
|
+
]
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
// 2. Add validation (mutation #1)
|
|
1119
|
+
plasma_mutate({
|
|
1120
|
+
name: "contact_form",
|
|
1121
|
+
mutation_name: "add_validation",
|
|
1122
|
+
js: `
|
|
1123
|
+
window.app.validate = function() {
|
|
1124
|
+
const email = document.getElementById('email').value;
|
|
1125
|
+
if (!email.includes('@')) {
|
|
1126
|
+
document.getElementById('status').textContent = '❌ Invalid email';
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
return true;
|
|
1130
|
+
};
|
|
1131
|
+
`
|
|
1132
|
+
})
|
|
1133
|
+
|
|
1134
|
+
// 3. Add character counter (mutation #2)
|
|
1135
|
+
plasma_mutate({
|
|
1136
|
+
name: "contact_form",
|
|
1137
|
+
mutation_name: "add_char_counter",
|
|
1138
|
+
js: `
|
|
1139
|
+
const textarea = document.getElementById('message');
|
|
1140
|
+
const counter = document.createElement('div');
|
|
1141
|
+
counter.id = 'counter';
|
|
1142
|
+
textarea.after(counter);
|
|
1143
|
+
|
|
1144
|
+
textarea.oninput = function() {
|
|
1145
|
+
counter.textContent = this.value.length + ' characters';
|
|
1146
|
+
};
|
|
1147
|
+
`
|
|
1148
|
+
})
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
### Pattern 2: Interactive 3D Visualization
|
|
1152
|
+
|
|
1153
|
+
```javascript
|
|
1154
|
+
// Create Three.js app
|
|
1155
|
+
plasma_create({
|
|
1156
|
+
name: "cube_3d",
|
|
1157
|
+
description: "Interactive rotating cube",
|
|
1158
|
+
html: `<div id="container" style="width:100%; height:500px;"></div>`,
|
|
1159
|
+
js: `
|
|
1160
|
+
const script = document.createElement('script');
|
|
1161
|
+
script.src = 'https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js';
|
|
1162
|
+
script.onload = () => {
|
|
1163
|
+
const scene = new THREE.Scene();
|
|
1164
|
+
const camera = new THREE.PerspectiveCamera(75, 800/500, 0.1, 1000);
|
|
1165
|
+
const renderer = new THREE.WebGLRenderer({antialias: true});
|
|
1166
|
+
renderer.setSize(800, 500);
|
|
1167
|
+
container.appendChild(renderer.domElement);
|
|
1168
|
+
|
|
1169
|
+
const cube = new THREE.Mesh(
|
|
1170
|
+
new THREE.BoxGeometry(2,2,2),
|
|
1171
|
+
new THREE.MeshPhongMaterial({color: 0xe91e8c})
|
|
1172
|
+
);
|
|
1173
|
+
scene.add(cube);
|
|
1174
|
+
|
|
1175
|
+
const light = new THREE.DirectionalLight(0xffffff, 1);
|
|
1176
|
+
light.position.set(5,5,5);
|
|
1177
|
+
scene.add(light);
|
|
1178
|
+
camera.position.z = 5;
|
|
1179
|
+
|
|
1180
|
+
function animate() {
|
|
1181
|
+
requestAnimationFrame(animate);
|
|
1182
|
+
cube.rotation.x += 0.01;
|
|
1183
|
+
cube.rotation.y += 0.01;
|
|
1184
|
+
renderer.render(scene, camera);
|
|
1185
|
+
}
|
|
1186
|
+
animate();
|
|
1187
|
+
|
|
1188
|
+
// EXPOSE STATE for mutations
|
|
1189
|
+
window.app = { scene, camera, renderer, cube };
|
|
1190
|
+
};
|
|
1191
|
+
document.head.appendChild(script);
|
|
1192
|
+
`,
|
|
1193
|
+
activities: []
|
|
1194
|
+
})
|
|
1195
|
+
|
|
1196
|
+
// Mutation: Change color
|
|
1197
|
+
plasma_mutate({
|
|
1198
|
+
name: "cube_3d",
|
|
1199
|
+
mutation_name: "change_color_blue",
|
|
1200
|
+
js: `window.app.cube.material.color.set('#0000ff');`,
|
|
1201
|
+
node_id: nodeId
|
|
1202
|
+
})
|
|
1203
|
+
|
|
1204
|
+
// Mutation: Double size
|
|
1205
|
+
plasma_mutate({
|
|
1206
|
+
name: "cube_3d",
|
|
1207
|
+
mutation_name: "double_size",
|
|
1208
|
+
js: `window.app.cube.scale.set(2, 2, 2);`,
|
|
1209
|
+
node_id: nodeId
|
|
1210
|
+
})
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
### Pattern 3: Dashboard with Live Updates
|
|
1214
|
+
|
|
1215
|
+
```javascript
|
|
1216
|
+
// Create dashboard
|
|
1217
|
+
plasma_create({
|
|
1218
|
+
name: "system_monitor",
|
|
1219
|
+
description: "System monitoring dashboard",
|
|
1220
|
+
html: `
|
|
1221
|
+
<div class="dashboard">
|
|
1222
|
+
<h2>System Status</h2>
|
|
1223
|
+
<div id="cpu" class="metric">CPU: --</div>
|
|
1224
|
+
<div id="memory" class="metric">Memory: --</div>
|
|
1225
|
+
<div id="disk" class="metric">Disk: --</div>
|
|
1226
|
+
</div>
|
|
1227
|
+
`,
|
|
1228
|
+
css: `
|
|
1229
|
+
.dashboard { padding: 20px; font-family: monospace; }
|
|
1230
|
+
.metric { font-size: 18px; margin: 10px 0; }
|
|
1231
|
+
`,
|
|
1232
|
+
activities: []
|
|
1233
|
+
})
|
|
1234
|
+
|
|
1235
|
+
// Server periodically sends updates via mutations
|
|
1236
|
+
// (Agent calls plasma_mutate when new data arrives)
|
|
1237
|
+
plasma_mutate({
|
|
1238
|
+
name: "system_monitor",
|
|
1239
|
+
mutation_name: "update_metrics",
|
|
1240
|
+
js: `
|
|
1241
|
+
document.getElementById('cpu').textContent = 'CPU: 45%';
|
|
1242
|
+
document.getElementById('memory').textContent = 'Memory: 62%';
|
|
1243
|
+
document.getElementById('disk').textContent = 'Disk: 78%';
|
|
1244
|
+
`,
|
|
1245
|
+
node_id: nodeId
|
|
1246
|
+
})
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
---
|
|
1250
|
+
|
|
1251
|
+
## Snapshots (Automatic)
|
|
1252
|
+
|
|
1253
|
+
Snapshots are created **automatically every 20 mutations** to optimize loading performance.
|
|
1254
|
+
|
|
1255
|
+
### What is a Snapshot?
|
|
1256
|
+
|
|
1257
|
+
A snapshot is a **mechanical concatenation** of main.code + all mutations up to N:
|
|
1258
|
+
|
|
1259
|
+
```javascript
|
|
1260
|
+
// snapshot_20.code (auto-generated)
|
|
1261
|
+
// Snapshot at mutation 20
|
|
1262
|
+
// Auto-generated: 2026-02-21T14:30:00.000Z
|
|
1263
|
+
|
|
1264
|
+
// === main.code ===
|
|
1265
|
+
[... full main.code content ...]
|
|
1266
|
+
|
|
1267
|
+
// === 1_add_validation.code ===
|
|
1268
|
+
[... mutation 1 content ...]
|
|
1269
|
+
|
|
1270
|
+
// === 2_change_color.code ===
|
|
1271
|
+
[... mutation 2 content ...]
|
|
1272
|
+
|
|
1273
|
+
...
|
|
1274
|
+
|
|
1275
|
+
// === 20_optimize_render.code ===
|
|
1276
|
+
[... mutation 20 content ...]
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
### Loading Strategy
|
|
1280
|
+
|
|
1281
|
+
When loading an organism with 45 mutations:
|
|
1282
|
+
|
|
1283
|
+
**Without snapshot** (slow):
|
|
1284
|
+
```
|
|
1285
|
+
Load: main.code + 1.code + 2.code + ... + 45.code
|
|
1286
|
+
= 46 file reads + 45 sequential updates
|
|
1287
|
+
```
|
|
1288
|
+
|
|
1289
|
+
**With snapshot** (fast):
|
|
1290
|
+
```
|
|
1291
|
+
Load: snapshot_40.code (includes main + 1-40)
|
|
1292
|
+
Apply: 41.code + 42.code + 43.code + 44.code + 45.code
|
|
1293
|
+
= 1 snapshot read + 5 mutations
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
**Result**: ~10x faster loading for mature organisms.
|
|
1297
|
+
|
|
1298
|
+
### Snapshot Management
|
|
1299
|
+
|
|
1300
|
+
- **Creation**: Automatic at mutations 20, 40, 60, 80, ...
|
|
1301
|
+
- **Storage**: Separate .code files (snapshot_N.code)
|
|
1302
|
+
- **Selection**: Automatically picks best snapshot when loading
|
|
1303
|
+
- **No maintenance needed**: System handles everything
|
|
1304
|
+
|
|
1305
|
+
---
|
|
1306
|
+
|
|
1307
|
+
## Future: LLM-Based Optimization (plasma_optimize)
|
|
1308
|
+
|
|
1309
|
+
**Status**: Planned, not yet implemented
|
|
1310
|
+
|
|
1311
|
+
**Concept**: Use LLM to intelligently compact mutations
|
|
1312
|
+
|
|
1313
|
+
```javascript
|
|
1314
|
+
// Future tool
|
|
1315
|
+
plasma_optimize({
|
|
1316
|
+
name: "customer_form",
|
|
1317
|
+
strategy: "smart" // vs "mechanical"
|
|
1318
|
+
})
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
**What it does**:
|
|
1322
|
+
1. Read main.code + all mutations
|
|
1323
|
+
2. LLM analyzes and refactors into optimized main.code
|
|
1324
|
+
3. Removes dead code (mutation 5 overwrites mutation 2)
|
|
1325
|
+
4. Simplifies redundant logic
|
|
1326
|
+
5. Validates result in sandbox
|
|
1327
|
+
6. Creates backup in .archive/
|
|
1328
|
+
7. Replaces main.code if validation passes
|
|
1329
|
+
|
|
1330
|
+
**When to use** (future):
|
|
1331
|
+
- Organism has >50 mutations
|
|
1332
|
+
- Loading is slow even with snapshots
|
|
1333
|
+
- Code has accumulated technical debt
|
|
1334
|
+
- User requests optimization
|
|
1335
|
+
|
|
1336
|
+
**Safety**: Always validates result and creates backup first.
|
|
1337
|
+
|
|
1338
|
+
---
|
|
1339
|
+
|
|
1340
|
+
## Workflow Examples
|
|
1341
|
+
|
|
1342
|
+
### Example 1: Create and Evolve a Form
|
|
1343
|
+
|
|
1344
|
+
```
|
|
1345
|
+
User: "Create a customer entry form with name and email fields"
|
|
1346
|
+
|
|
1347
|
+
You: [Call plasma_create internally with HTML/CSS/JS]
|
|
1348
|
+
"I've created a customer form with name and email input fields and a submit button.
|
|
1349
|
+
Ready to use!"
|
|
1350
|
+
|
|
1351
|
+
---
|
|
1352
|
+
|
|
1353
|
+
User: "Add validation - email must contain @"
|
|
1354
|
+
|
|
1355
|
+
You: [Call plasma_mutate with email validation JS]
|
|
1356
|
+
"Added email validation. The form will now check that the email contains an @ symbol
|
|
1357
|
+
before allowing submission."
|
|
1358
|
+
|
|
1359
|
+
---
|
|
1360
|
+
|
|
1361
|
+
User: "Make the submit button blue"
|
|
1362
|
+
|
|
1363
|
+
You: [Call plasma_mutate with button color change]
|
|
1364
|
+
"Changed the submit button color to blue."
|
|
1365
|
+
|
|
1366
|
+
---
|
|
1367
|
+
|
|
1368
|
+
User: "Show me the form"
|
|
1369
|
+
|
|
1370
|
+
You: [Call dynamic_ui_list_nodes to get node]
|
|
1371
|
+
[Call plasma_load with customer_form]
|
|
1372
|
+
"The customer form is now displayed on your ElectroNode with all updates
|
|
1373
|
+
(validation + blue button). Ready to collect customer data!"
|
|
1374
|
+
```
|
|
1375
|
+
|
|
1376
|
+
### Example 2: Dashboard with Live Data
|
|
1377
|
+
|
|
1378
|
+
```
|
|
1379
|
+
User: "Create a sales dashboard showing revenue and orders"
|
|
1380
|
+
|
|
1381
|
+
You: [Call plasma_create with dashboard HTML/CSS/JS including Chart.js]
|
|
1382
|
+
"I've created a sales dashboard with revenue and orders metrics.
|
|
1383
|
+
It's ready to display on your node. Want me to load it?"
|
|
1384
|
+
|
|
1385
|
+
User: "Yes, and update it with today's data"
|
|
1386
|
+
|
|
1387
|
+
You: [Call plasma_load to display]
|
|
1388
|
+
[Fetch sales data from API/database using other tools]
|
|
1389
|
+
[Call plasma_mutate with data update]
|
|
1390
|
+
"Dashboard is live! Showing today's revenue: $15,234 and 42 orders.
|
|
1391
|
+
The chart reflects the hourly breakdown."
|
|
1392
|
+
|
|
1393
|
+
---
|
|
1394
|
+
|
|
1395
|
+
[Later, when new sales data arrives via webhook/polling]
|
|
1396
|
+
|
|
1397
|
+
You: [Call plasma_mutate to update numbers]
|
|
1398
|
+
[Update displayed automatically]
|
|
1399
|
+
"Dashboard updated - revenue now at $18,450 (+$3,216)."
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
---
|
|
1403
|
+
|
|
1404
|
+
## Summary
|
|
1405
|
+
|
|
1406
|
+
**PLASMA v2 = Event-Sourced UI with Complete History**
|
|
1407
|
+
|
|
1408
|
+
- **Create** organisms with `plasma_create`
|
|
1409
|
+
- **Mutate** incrementally with `plasma_mutate`
|
|
1410
|
+
- **Load** instantly with `plasma_load` (snapshot support)
|
|
1411
|
+
- **Automatic snapshots** every 20 mutations
|
|
1412
|
+
- **Complete history** of all changes
|
|
1413
|
+
- **Rollback capable** (load specific mutation range)
|
|
1414
|
+
- **Zero LLM cost** for snapshots (mechanical)
|
|
1415
|
+
- **Future-ready** for LLM optimization
|
|
1416
|
+
|
|
1417
|
+
**Three core tools, infinite possibilities, complete auditability.**
|